diff options
Diffstat (limited to 'extern')
123 files changed, 30189 insertions, 0 deletions
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 60253e08d4c..6215673eb44 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -67,3 +67,7 @@ endif() if(WITH_LIBMV) add_subdirectory(libmv) endif() + +if(WITH_CARVE) + add_subdirectory(carve) +endif() diff --git a/extern/SConscript b/extern/SConscript index 031471a8a01..738342b3dcc 100644 --- a/extern/SConscript +++ b/extern/SConscript @@ -31,3 +31,6 @@ if env['WITH_BF_LZMA']: if env['WITH_BF_LIBMV']: SConscript(['libmv/SConscript']) + +if env['WITH_BF_CARVE']: + SConscript(['carve/SConscript']) diff --git a/extern/carve/CMakeLists.txt b/extern/carve/CMakeLists.txt new file mode 100644 index 00000000000..abd35d33c1c --- /dev/null +++ b/extern/carve/CMakeLists.txt @@ -0,0 +1,166 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# The Original Code is Copyright (C) 2006, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): Jacques Beaurai, Erwin Coumans +# +# ***** END GPL LICENSE BLOCK ***** + +# NOTE: This file is automatically generated by bundle.sh script +# If you're doing changes in this file, please update template +# in that script too + +set(INC + include +) + +set(INC_SYS +) + +set(SRC + lib/carve.cpp + lib/mesh.cpp + lib/intersect_group.cpp + lib/intersect_classify_edge.cpp + lib/intersect_classify_group.cpp + lib/polyhedron.cpp + lib/geom3d.cpp + lib/polyline.cpp + lib/csg_collector.cpp + lib/triangulator.cpp + lib/intersect_face_division.cpp + lib/intersect_half_classify_group.cpp + lib/edge.cpp + lib/math.cpp + lib/geom2d.cpp + lib/tag.cpp + lib/intersection.cpp + lib/convex_hull.cpp + lib/csg.cpp + lib/intersect.cpp + lib/face.cpp + lib/pointset.cpp + lib/timing.cpp + lib/octree.cpp + lib/aabb.cpp + lib/intersect_debug.cpp + + lib/intersect_classify_common.hpp + lib/csg_data.hpp + lib/csg_collector.hpp + lib/intersect_common.hpp + lib/intersect_classify_common_impl.hpp + lib/csg_detail.hpp + lib/intersect_debug.hpp + + include/carve/polyhedron_decl.hpp + include/carve/geom2d.hpp + include/carve/exact.hpp + include/carve/triangulator_impl.hpp + include/carve/collection.hpp + include/carve/pointset.hpp + include/carve/djset.hpp + include/carve/kd_node.hpp + include/carve/polyline.hpp + include/carve/polyline_iter.hpp + include/carve/geom3d.hpp + include/carve/edge_decl.hpp + include/carve/face_decl.hpp + include/carve/aabb_impl.hpp + include/carve/colour.hpp + include/carve/pointset_iter.hpp + include/carve/polyline_decl.hpp + include/carve/rescale.hpp + include/carve/mesh_impl.hpp + include/carve/classification.hpp + include/carve/util.hpp + include/carve/triangulator.hpp + include/carve/polyhedron_base.hpp + include/carve/rtree.hpp + include/carve/math.hpp + include/carve/math_constants.hpp + include/carve/octree_decl.hpp + include/carve/input.hpp + include/carve/mesh_ops.hpp + include/carve/debug_hooks.hpp + include/carve/mesh_simplify.hpp + include/carve/interpolator.hpp + include/carve/poly_decl.hpp + include/carve/csg.hpp + include/carve/mesh.hpp + include/carve/carve.hpp + include/carve/gnu_cxx.h + include/carve/polyhedron_impl.hpp + include/carve/poly_impl.hpp + include/carve/aabb.hpp + include/carve/convex_hull.hpp + include/carve/vertex_decl.hpp + include/carve/win32.h + include/carve/edge_impl.hpp + include/carve/tag.hpp + include/carve/tree.hpp + include/carve/heap.hpp + include/carve/matrix.hpp + include/carve/poly.hpp + include/carve/vector.hpp + include/carve/intersection.hpp + include/carve/faceloop.hpp + include/carve/geom_impl.hpp + include/carve/octree_impl.hpp + include/carve/spacetree.hpp + include/carve/collection/unordered/std_impl.hpp + include/carve/collection/unordered/tr1_impl.hpp + include/carve/collection/unordered/libstdcpp_impl.hpp + include/carve/collection/unordered/boost_impl.hpp + include/carve/collection/unordered/vcpp_impl.hpp + include/carve/collection/unordered/fallback_impl.hpp + include/carve/collection/unordered.hpp + include/carve/face_impl.hpp + include/carve/pointset_impl.hpp + include/carve/cbrt.h + include/carve/vcpp_config.h + include/carve/geom.hpp + include/carve/vertex_impl.hpp + include/carve/polyline_impl.hpp + include/carve/pointset_decl.hpp + include/carve/timing.hpp + include/carve/csg_triangulator.hpp + include/carve/iobj.hpp + include/carve/collection_types.hpp +) + +if(WITH_BOOST) + if(NOT MSVC) + # Boost is setting as preferred collections library in the Carve code when using MSVC compiler + add_definitions( + -DHAVE_BOOST_UNORDERED_COLLECTIONS + ) + endif() + + add_definitions( + -DCARVE_SYSTEM_BOOST + ) + + list(APPEND INC + ${BOOST_INCLUDE_DIR} + ) +endif() + +blender_add_lib(extern_carve "${SRC}" "${INC}" "${INC_SYS}") diff --git a/extern/carve/LICENSE.GPL2 b/extern/carve/LICENSE.GPL2 new file mode 100644 index 00000000000..792acb92f6a --- /dev/null +++ b/extern/carve/LICENSE.GPL2 @@ -0,0 +1,361 @@ + GNU GENERAL PUBLIC LICENSE + + The Qt GUI Toolkit is Copyright (C) 1994-2008 Trolltech ASA. + + You may use, distribute and copy the Qt GUI Toolkit under the terms of + GNU General Public License version 2, which is displayed below. + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + +------------------------------------------------------------------------- + +In addition, as a special exception, Trolltech gives permission to link the +code of its release of Qt with the OpenSSL project's "OpenSSL" library (or +modified versions of it that use the same license as the "OpenSSL" +library), and distribute the linked executables. You must comply with the GNU +General Public License version 2 or the GNU General Public License version 3 +in all respects for all of the code used other than the "OpenSSL" code. If +you modify this file, you may extend this exception to your version of the +file, but you are not obligated to do so. If you do not wish to do so, +delete this exception statement from your version of this file. diff --git a/extern/carve/SConscript b/extern/carve/SConscript new file mode 100644 index 00000000000..4a87b825186 --- /dev/null +++ b/extern/carve/SConscript @@ -0,0 +1,22 @@ +#!/usr/bin/python + +# NOTE: This file is automatically generated by bundle.sh script +# If you're doing changes in this file, please update template +# in that script too + +Import ('env') + +sources = env.Glob('lib/*.cpp') + +defs = [] +incs = ['include'] + +if env['WITH_BF_BOOST']: + if env['OURPLATFORM'] not in ('win32-vc', 'win64-vc'): + # Boost is setting as preferred collections library in the Carve code when using MSVC compiler + defs.append('HAVE_BOOST_UNORDERED_COLLECTIONS') + + defs.append('CARVE_SYSTEM_BOOST') + incs.append(env['BF_BOOST_INC']) + +env.BlenderLib ('extern_carve', Split(sources), incs, defs, libtype=['extern'], priority=[40] ) diff --git a/extern/carve/bundle.sh b/extern/carve/bundle.sh new file mode 100755 index 00000000000..cbc1fe6d2d5 --- /dev/null +++ b/extern/carve/bundle.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +if [ -d ./.svn ]; then + echo "This script is supposed to work only when using git-svn" + exit 1 +fi + +tmp=`mktemp -d` + +hg clone https://code.google.com/p/carve/ $tmp/carve + +for p in `cat ./patches/series`; do + echo "Applying patch $p..." + cat ./patches/$p | patch -d $tmp/carve -p1 +done + +rm -rf include +rm -rf lib + +cat "files.txt" | while f=`line`; do + mkdir -p `dirname $f` + cp $tmp/carve/$f $f +done + +rm -rf $tmp + +sources=`find ./lib -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\//\t/'` +headers=`find ./lib -type f -iname '*.h' -or -iname '*.hpp' | sed -r 's/^\.\//\t/'` +includes=`find ./include -type f -iname '*.h' -or -iname '*.hpp' | sed -r 's/^\.\//\t/'` + +mkdir -p include/carve/external/boost +cp patches/files/random.hpp include/carve/external/boost/random.hpp +cp patches/files/config.h include/carve/config.h + +cat > CMakeLists.txt << EOF +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# The Original Code is Copyright (C) 2006, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): Jacques Beaurai, Erwin Coumans +# +# ***** END GPL LICENSE BLOCK ***** + +# NOTE: This file is automatically generated by bundle.sh script +# If you're doing changes in this file, please update template +# in that script too + +set(INC + include +) + +set(INC_SYS +) + +set(SRC +${sources} + +${headers} + +${includes} +) + +if(WITH_BOOST) + if(NOT MSVC) + # Boost is setting as preferred collections library in the Carve code when using MSVC compiler + add_definitions( + -DHAVE_BOOST_UNORDERED_COLLECTIONS + ) + endif() + + add_definitions( + -DCARVE_SYSTEM_BOOST + ) + + list(APPEND INC + \${BOOST_INCLUDE_DIR} + ) +endif() + +blender_add_lib(extern_carve "\${SRC}" "\${INC}" "\${INC_SYS}") +EOF + +cat > SConscript << EOF +#!/usr/bin/python + +# NOTE: This file is automatically generated by bundle.sh script +# If you're doing changes in this file, please update template +# in that script too + +Import ('env') + +sources = env.Glob('lib/*.cpp') + +defs = [] +incs = ['include'] + +if env['WITH_BF_BOOST']: + if env['OURPLATFORM'] not in ('win32-vc', 'win64-vc'): + # Boost is setting as preferred collections library in the Carve code when using MSVC compiler + defs.append('HAVE_BOOST_UNORDERED_COLLECTIONS') + + defs.append('CARVE_SYSTEM_BOOST') + incs.append(env['BF_BOOST_INC']) + +env.BlenderLib ('extern_carve', Split(sources), incs, defs, libtype=['extern'], priority=[40] ) +EOF diff --git a/extern/carve/files.txt b/extern/carve/files.txt new file mode 100644 index 00000000000..c3cb9275950 --- /dev/null +++ b/extern/carve/files.txt @@ -0,0 +1,107 @@ +include/carve/polyhedron_decl.hpp +include/carve/geom2d.hpp +include/carve/exact.hpp +include/carve/triangulator_impl.hpp +include/carve/collection.hpp +include/carve/pointset.hpp +include/carve/djset.hpp +include/carve/kd_node.hpp +include/carve/polyline.hpp +include/carve/polyline_iter.hpp +include/carve/geom3d.hpp +include/carve/edge_decl.hpp +include/carve/face_decl.hpp +include/carve/aabb_impl.hpp +include/carve/colour.hpp +include/carve/pointset_iter.hpp +include/carve/polyline_decl.hpp +include/carve/rescale.hpp +include/carve/mesh_impl.hpp +include/carve/classification.hpp +include/carve/util.hpp +include/carve/triangulator.hpp +include/carve/polyhedron_base.hpp +include/carve/rtree.hpp +include/carve/math.hpp +include/carve/math_constants.hpp +include/carve/octree_decl.hpp +include/carve/input.hpp +include/carve/mesh_ops.hpp +include/carve/debug_hooks.hpp +include/carve/mesh_simplify.hpp +include/carve/interpolator.hpp +include/carve/poly_decl.hpp +include/carve/csg.hpp +include/carve/mesh.hpp +include/carve/carve.hpp +include/carve/gnu_cxx.h +include/carve/polyhedron_impl.hpp +include/carve/poly_impl.hpp +include/carve/aabb.hpp +include/carve/convex_hull.hpp +include/carve/vertex_decl.hpp +include/carve/win32.h +include/carve/edge_impl.hpp +include/carve/tag.hpp +include/carve/tree.hpp +include/carve/heap.hpp +include/carve/matrix.hpp +include/carve/poly.hpp +include/carve/vector.hpp +include/carve/intersection.hpp +include/carve/faceloop.hpp +include/carve/geom_impl.hpp +include/carve/octree_impl.hpp +include/carve/spacetree.hpp +include/carve/collection/unordered/std_impl.hpp +include/carve/collection/unordered/tr1_impl.hpp +include/carve/collection/unordered/libstdcpp_impl.hpp +include/carve/collection/unordered/boost_impl.hpp +include/carve/collection/unordered/vcpp_impl.hpp +include/carve/collection/unordered/fallback_impl.hpp +include/carve/collection/unordered.hpp +include/carve/face_impl.hpp +include/carve/pointset_impl.hpp +include/carve/cbrt.h +include/carve/vcpp_config.h +include/carve/geom.hpp +include/carve/vertex_impl.hpp +include/carve/polyline_impl.hpp +include/carve/pointset_decl.hpp +include/carve/timing.hpp +include/carve/csg_triangulator.hpp +include/carve/iobj.hpp +include/carve/collection_types.hpp +lib/carve.cpp +lib/mesh.cpp +lib/intersect_group.cpp +lib/intersect_classify_common.hpp +lib/intersect_classify_edge.cpp +lib/intersect_classify_group.cpp +lib/csg_data.hpp +lib/polyhedron.cpp +lib/csg_collector.hpp +lib/geom3d.cpp +lib/polyline.cpp +lib/csg_collector.cpp +lib/triangulator.cpp +lib/intersect_face_division.cpp +lib/intersect_half_classify_group.cpp +lib/edge.cpp +lib/math.cpp +lib/geom2d.cpp +lib/tag.cpp +lib/intersection.cpp +lib/convex_hull.cpp +lib/intersect_common.hpp +lib/intersect_classify_common_impl.hpp +lib/csg.cpp +lib/intersect.cpp +lib/csg_detail.hpp +lib/face.cpp +lib/pointset.cpp +lib/timing.cpp +lib/octree.cpp +lib/aabb.cpp +lib/intersect_debug.hpp +lib/intersect_debug.cpp diff --git a/extern/carve/include/carve/aabb.hpp b/extern/carve/include/carve/aabb.hpp new file mode 100644 index 00000000000..20ee028aa45 --- /dev/null +++ b/extern/carve/include/carve/aabb.hpp @@ -0,0 +1,150 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/geom.hpp> + +#include <vector> + +namespace carve { + namespace geom { + + + + // n-dimensional AABB + template<unsigned ndim> + struct aabb { + typedef vector<ndim> vector_t; + typedef aabb<ndim> aabb_t; + + vector_t pos; // the centre of the AABB + vector_t extent; // the extent of the AABB - the vector from the centre to the maximal vertex. + + void empty(); + + bool isEmpty() const; + + void fit(const vector_t &v1); + void fit(const vector_t &v1, const vector_t &v2); + void fit(const vector_t &v1, const vector_t &v2, const vector_t &v3); + + template<typename iter_t, typename value_type> + void _fit(iter_t begin, iter_t end, value_type); + + template<typename iter_t> + void _fit(iter_t begin, iter_t end, vector_t); + + template<typename iter_t> + void _fit(iter_t begin, iter_t end, aabb_t); + + template<typename iter_t> + void fit(iter_t begin, iter_t end); + + template<typename iter_t, typename adapt_t> + void fit(iter_t begin, iter_t end, adapt_t adapt); + + void unionAABB(const aabb<ndim> &a); + + void expand(double pad); + + bool completelyContains(const aabb<ndim> &other) const; + + bool containsPoint(const vector_t &v) const; + + bool intersectsLineSegment(const vector_t &v1, const vector_t &v2) const; + + double axisSeparation(const aabb<ndim> &other, unsigned axis) const; + + double maxAxisSeparation(const aabb<ndim> &other) const; + + bool intersects(const aabb<ndim> &other) const; + bool intersects(const sphere<ndim> &s) const; + bool intersects(const plane<ndim> &plane) const; + bool intersects(const ray<ndim> &ray) const; + bool intersects(tri<ndim> tri) const; + bool intersects(const linesegment<ndim> &ls) const; + + std::pair<double, double> rangeInDirection(const carve::geom::vector<ndim> &v) const; + + vector_t min() const; + vector_t mid() const; + vector_t max() const; + + double min(unsigned dim) const; + double mid(unsigned dim) const; + double max(unsigned dim) const; + + double volume() const; + + int compareAxis(const axis_pos &ap) const; + + void constrainMax(const axis_pos &ap); + void constrainMin(const axis_pos &ap); + + aabb getAABB() const; + + aabb(const vector_t &_pos = vector_t::ZERO(), + const vector_t &_extent = vector_t::ZERO()); + + template<typename iter_t, typename adapt_t> + aabb(iter_t begin, iter_t end, adapt_t adapt); + + template<typename iter_t> + aabb(iter_t begin, iter_t end); + + aabb(const aabb<ndim> &a, const aabb<ndim> &b); + }; + + template<unsigned ndim> + bool operator==(const aabb<ndim> &a, const aabb<ndim> &b); + + template<unsigned ndim> + bool operator!=(const aabb<ndim> &a, const aabb<ndim> &b); + + template<unsigned ndim> + std::ostream &operator<<(std::ostream &o, const aabb<ndim> &a); + + + + template<unsigned ndim, typename obj_t> + struct get_aabb { + aabb<ndim> operator()(const obj_t &obj) const { + return obj.getAABB(); + } + }; + + template<unsigned ndim, typename obj_t> + struct get_aabb<ndim, obj_t *> { + aabb<ndim> operator()(const obj_t *obj) const { + return obj->getAABB(); + } + }; + + + + } +} + +namespace carve { + namespace geom3d { + typedef carve::geom::aabb<3> AABB; + } +} + +#include <carve/aabb_impl.hpp> diff --git a/extern/carve/include/carve/aabb_impl.hpp b/extern/carve/include/carve/aabb_impl.hpp new file mode 100644 index 00000000000..ccdddef160b --- /dev/null +++ b/extern/carve/include/carve/aabb_impl.hpp @@ -0,0 +1,423 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/vector.hpp> +#include <carve/geom3d.hpp> + +#include <carve/geom.hpp> + +#include <vector> + +namespace carve { + namespace geom { + + template<unsigned ndim> + void aabb<ndim>::empty() { + pos.setZero(); + extent.setZero(); + } + + template<unsigned ndim> + bool aabb<ndim>::isEmpty() const { + return extent.exactlyZero(); + } + + template<unsigned ndim> + template<typename iter_t, typename value_type> + void aabb<ndim>::_fit(iter_t begin, iter_t end, value_type) { + if (begin == end) { + empty(); + return; + } + + vector_t min, max; + aabb<ndim> a = get_aabb<ndim, value_type>()(*begin); ++begin; + min = a.min(); + max = a.max(); + while (begin != end) { + aabb<ndim> a = get_aabb<ndim, value_type>()(*begin); ++begin; + assign_op(min, min, a.min(), carve::util::min_functor()); + assign_op(max, max, a.max(), carve::util::max_functor()); + } + + pos = (min + max) / 2.0; + assign_op(extent, max - pos, pos - min, carve::util::max_functor()); + } + + template<unsigned ndim> + template<typename iter_t> + void aabb<ndim>::_fit(iter_t begin, iter_t end, vector_t) { + if (begin == end) { + empty(); + return; + } + + vector_t min, max; + bounds(begin, end, min, max); + pos = (min + max) / 2.0; + assign_op(extent, max - pos, pos - min, carve::util::max_functor()); + } + + template<unsigned ndim> + template<typename iter_t> + void aabb<ndim>::_fit(iter_t begin, iter_t end, aabb_t) { + if (begin == end) { + empty(); + return; + } + + vector_t min, max; + aabb<ndim> a = *begin++; + min = a.min(); + max = a.max(); + while (begin != end) { + aabb<ndim> a = *begin; ++begin; + assign_op(min, min, a.min(), carve::util::min_functor()); + assign_op(max, max, a.max(), carve::util::max_functor()); + } + + pos = (min + max) / 2.0; + assign_op(extent, max - pos, pos - min, carve::util::max_functor()); + } + + template<unsigned ndim> + void aabb<ndim>::fit(const vector_t &v1) { + pos = v1; + extent.setZero(); + } + + template<unsigned ndim> + void aabb<ndim>::fit(const vector_t &v1, const vector_t &v2) { + vector_t min, max; + assign_op(min, v1, v2, carve::util::min_functor()); + assign_op(max, v1, v2, carve::util::max_functor()); + + pos = (min + max) / 2.0; + assign_op(extent, max - pos, pos - min, carve::util::max_functor()); + } + + template<unsigned ndim> + void aabb<ndim>::fit(const vector_t &v1, const vector_t &v2, const vector_t &v3) { + vector_t min, max; + min = max = v1; + + assign_op(min, min, v2, carve::util::min_functor()); + assign_op(max, max, v2, carve::util::max_functor()); + assign_op(min, min, v3, carve::util::min_functor()); + assign_op(max, max, v3, carve::util::max_functor()); + + pos = (min + max) / 2.0; + assign_op(extent, max - pos, pos - min, carve::util::max_functor()); + } + + template<unsigned ndim> + template<typename iter_t, typename adapt_t> + void aabb<ndim>::fit(iter_t begin, iter_t end, adapt_t adapt) { + vector_t min, max; + + bounds(begin, end, adapt, min, max); + pos = (min + max) / 2.0; + assign_op(extent, max - pos, pos - min, carve::util::max_functor()); + } + + template<unsigned ndim> + template<typename iter_t> + void aabb<ndim>::fit(iter_t begin, iter_t end) { + _fit(begin, end, typename std::iterator_traits<iter_t>::value_type()); + } + + template<unsigned ndim> + void aabb<ndim>::expand(double pad) { + extent += pad; + } + + template<unsigned ndim> + void aabb<ndim>::unionAABB(const aabb<ndim> &a) { + vector_t vmin, vmax; + + assign_op(vmin, min(), a.min(), carve::util::min_functor()); + assign_op(vmax, max(), a.max(), carve::util::max_functor()); + pos = (vmin + vmax) / 2.0; + assign_op(extent, vmax - pos, pos - vmin, carve::util::max_functor()); + } + + template<unsigned ndim> + bool aabb<ndim>::completelyContains(const aabb<ndim> &other) const { + for (unsigned i = 0; i < ndim; ++i) { + if (fabs(other.pos.v[i] - pos.v[i]) + other.extent.v[i] > extent.v[i]) return false; + } + return true; + } + + template<unsigned ndim> + bool aabb<ndim>::containsPoint(const vector_t &v) const { + for (unsigned i = 0; i < ndim; ++i) { + if (fabs(v.v[i] - pos.v[i]) > extent.v[i]) return false; + } + return true; + } + + template<unsigned ndim> + double aabb<ndim>::axisSeparation(const aabb<ndim> &other, unsigned axis) const { + return fabs(other.pos.v[axis] - pos.v[axis]) - extent.v[axis] - other.extent.v[axis]; + } + + template<unsigned ndim> + double aabb<ndim>::maxAxisSeparation(const aabb<ndim> &other) const { + double m = axisSeparation(other, 0); + for (unsigned i = 1; i < ndim; ++i) { + m = std::max(m, axisSeparation(other, i)); + } + return m; + } + + template<unsigned ndim> + bool aabb<ndim>::intersects(const aabb<ndim> &other) const { + return maxAxisSeparation(other) <= 0.0; + } + + template<unsigned ndim> + bool aabb<ndim>::intersects(const sphere<ndim> &s) const { + double r = 0.0; + for (unsigned i = 0; i < ndim; ++i) { + double t = fabs(s.C[i] - pos[i]) - extent[i]; if (t > 0.0) r += t*t; + } + return r <= s.r*s.r; + } + + template<unsigned ndim> + bool aabb<ndim>::intersects(const plane<ndim> &plane) const { + double d1 = fabs(distance(plane, pos)); + double d2 = dot(abs(plane.N), extent); + return d1 <= d2; + } + + template<unsigned ndim> + bool aabb<ndim>::intersects(const linesegment<ndim> &ls) const { + return intersectsLineSegment(ls.v1, ls.v2); + } + + template<unsigned ndim> + std::pair<double, double> aabb<ndim>::rangeInDirection(const carve::geom::vector<ndim> &v) const { + double d1 = dot(v, pos); + double d2 = dot(abs(v), extent); + + return std::make_pair(d1 - d2, d1 + d2); + } + + template<unsigned ndim> + typename aabb<ndim>::vector_t aabb<ndim>::min() const { return pos - extent; } + + template<unsigned ndim> + typename aabb<ndim>::vector_t aabb<ndim>::mid() const { return pos; } + + template<unsigned ndim> + typename aabb<ndim>::vector_t aabb<ndim>::max() const { return pos + extent; } + + template<unsigned ndim> + double aabb<ndim>::min(unsigned dim) const { return pos.v[dim] - extent.v[dim]; } + + template<unsigned ndim> + double aabb<ndim>::mid(unsigned dim) const { return pos.v[dim]; } + + template<unsigned ndim> + double aabb<ndim>::max(unsigned dim) const { return pos.v[dim] + extent.v[dim]; } + + template<unsigned ndim> + double aabb<ndim>::volume() const { + double v = 1.0; + for (size_t dim = 0; dim < ndim; ++dim) { v *= 2.0 * extent.v[dim]; } + return v; + } + + template<unsigned ndim> + int aabb<ndim>::compareAxis(const axis_pos &ap) const { + double p = ap.pos - pos[ap.axis]; + if (p > extent[ap.axis]) return -1; + if (p < -extent[ap.axis]) return +1; + return 0; + } + + template<unsigned ndim> + void aabb<ndim>::constrainMax(const axis_pos &ap) { + if (pos[ap.axis] + extent[ap.axis] > ap.pos) { + double min = std::min(ap.pos, pos[ap.axis] - extent[ap.axis]); + pos[ap.axis] = (min + ap.pos) / 2.0; + extent[ap.axis] = ap.pos - pos[ap.axis]; + } + } + + template<unsigned ndim> + void aabb<ndim>::constrainMin(const axis_pos &ap) { + if (pos[ap.axis] - extent[ap.axis] < ap.pos) { + double max = std::max(ap.pos, pos[ap.axis] + extent[ap.axis]); + pos[ap.axis] = (ap.pos + max) / 2.0; + extent[ap.axis] = pos[ap.axis] - ap.pos; + } + } + + template<unsigned ndim> + aabb<ndim> aabb<ndim>::getAABB() const { + return *this; + } + + template<unsigned ndim> + aabb<ndim>::aabb(const vector_t &_pos, + const vector_t &_extent) : pos(_pos), extent(_extent) { + } + + template<unsigned ndim> + template<typename iter_t, typename adapt_t> + aabb<ndim>::aabb(iter_t begin, iter_t end, adapt_t adapt) { + fit(begin, end, adapt); + } + + template<unsigned ndim> + template<typename iter_t> + aabb<ndim>::aabb(iter_t begin, iter_t end) { + fit(begin, end); + } + + template<unsigned ndim> + aabb<ndim>::aabb(const aabb<ndim> &a, const aabb<ndim> &b) { + fit(a, b); + } + + template<unsigned ndim> + bool operator==(const aabb<ndim> &a, const aabb<ndim> &b) { + return a.pos == b.pos && a.extent == b.extent; + } + + template<unsigned ndim> + bool operator!=(const aabb<ndim> &a, const aabb<ndim> &b) { + return a.pos != b.pos || a.extent != b.extent; + } + + template<unsigned ndim> + std::ostream &operator<<(std::ostream &o, const aabb<ndim> &a) { + o << (a.pos - a.extent) << "--" << (a.pos + a.extent); + return o; + } + + template<> + inline bool aabb<3>::intersects(const ray<3> &ray) const { + vector<3> t = pos - ray.v; + double r; + + //l.cross(x-axis)? + r = extent.y * fabs(ray.D.z) + extent.z * fabs(ray.D.y); + if (fabs(t.y * ray.D.z - t.z * ray.D.y) > r) return false; + + //ray.D.cross(y-axis)? + r = extent.x * fabs(ray.D.z) + extent.z * fabs(ray.D.x); + if (fabs(t.z * ray.D.x - t.x * ray.D.z) > r) return false; + + //ray.D.cross(z-axis)? + r = extent.x*fabs(ray.D.y) + extent.y*fabs(ray.D.x); + if (fabs(t.x * ray.D.y - t.y * ray.D.x) > r) return false; + + return true; + } + + template<> + inline bool aabb<3>::intersectsLineSegment(const vector<3> &v1, const vector<3> &v2) const { + vector<3> half_length = 0.5 * (v2 - v1); + vector<3> t = pos - half_length - v1; + double r; + + //do any of the principal axes form a separating axis? + if(fabs(t.x) > extent.x + fabs(half_length.x)) return false; + if(fabs(t.y) > extent.y + fabs(half_length.y)) return false; + if(fabs(t.z) > extent.z + fabs(half_length.z)) return false; + + // NOTE: Since the separating axis is perpendicular to the line in + // these last four cases, the line does not contribute to the + // projection. + + //line.cross(x-axis)? + r = extent.y * fabs(half_length.z) + extent.z * fabs(half_length.y); + if (fabs(t.y * half_length.z - t.z * half_length.y) > r) return false; + + //half_length.cross(y-axis)? + r = extent.x * fabs(half_length.z) + extent.z * fabs(half_length.x); + if (fabs(t.z * half_length.x - t.x * half_length.z) > r) return false; + + //half_length.cross(z-axis)? + r = extent.x*fabs(half_length.y) + extent.y*fabs(half_length.x); + if (fabs(t.x * half_length.y - t.y * half_length.x) > r) return false; + + return true; + } + + template<int Ax, int Ay, int Az, int c> + static inline bool intersectsTriangle_axisTest_3(const aabb<3> &aabb, const tri<3> &tri) { + const int d = (c+1) % 3, e = (c+2) % 3; + const vector<3> a = cross(VECTOR(Ax, Ay, Az), tri.v[d] - tri.v[c]); + double p1 = dot(a, tri.v[c]), p2 = dot(a, tri.v[e]); + if (p1 > p2) std::swap(p1, p2); + const double r = dot(abs(a), aabb.extent); + return !(p1 > r || p2 < -r); + } + + template<int c> + static inline bool intersectsTriangle_axisTest_2(const aabb<3> &aabb, const tri<3> &tri) { + double vmin = std::min(std::min(tri.v[0][c], tri.v[1][c]), tri.v[2][c]), + vmax = std::max(std::max(tri.v[0][c], tri.v[1][c]), tri.v[2][c]); + return !(vmin > aabb.extent[c] || vmax < -aabb.extent[c]); + } + + static inline bool intersectsTriangle_axisTest_1(const aabb<3> &aabb, const tri<3> &tri) { + vector<3> n = cross(tri.v[1] - tri.v[0], tri.v[2] - tri.v[0]); + double d1 = fabs(dot(n, tri.v[0])); + double d2 = dot(abs(n), aabb.extent); + return d1 <= d2; + } + + template<> + inline bool aabb<3>::intersects(tri<3> tri) const { + tri.v[0] -= pos; + tri.v[1] -= pos; + tri.v[2] -= pos; + + if (!intersectsTriangle_axisTest_2<0>(*this, tri)) return false; + if (!intersectsTriangle_axisTest_2<1>(*this, tri)) return false; + if (!intersectsTriangle_axisTest_2<2>(*this, tri)) return false; + + if (!intersectsTriangle_axisTest_3<1,0,0,0>(*this, tri)) return false; + if (!intersectsTriangle_axisTest_3<1,0,0,1>(*this, tri)) return false; + if (!intersectsTriangle_axisTest_3<1,0,0,2>(*this, tri)) return false; + + if (!intersectsTriangle_axisTest_3<0,1,0,0>(*this, tri)) return false; + if (!intersectsTriangle_axisTest_3<0,1,0,1>(*this, tri)) return false; + if (!intersectsTriangle_axisTest_3<0,1,0,2>(*this, tri)) return false; + + if (!intersectsTriangle_axisTest_3<0,0,1,0>(*this, tri)) return false; + if (!intersectsTriangle_axisTest_3<0,0,1,1>(*this, tri)) return false; + if (!intersectsTriangle_axisTest_3<0,0,1,2>(*this, tri)) return false; + + if (!intersectsTriangle_axisTest_1(*this, tri)) return false; + + return true; + } + + + + } +} diff --git a/extern/carve/include/carve/carve.hpp b/extern/carve/include/carve/carve.hpp new file mode 100644 index 00000000000..90ca6b4977b --- /dev/null +++ b/extern/carve/include/carve/carve.hpp @@ -0,0 +1,238 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#pragma once + +#if defined(CMAKE_BUILD) +# include <carve/config.h> +#elif defined(XCODE_BUILD) +# include <carve/xcode_config.h> +#elif defined(_MSC_VER) +# include <carve/vcpp_config.h> +#else +# include <carve/config.h> +#endif + +#if defined(WIN32) +# include <carve/win32.h> +#elif defined(__GNUC__) +# include <carve/gnu_cxx.h> +#endif + +#if defined(CARVE_SYSTEM_BOOST) +# define BOOST_INCLUDE(x) <boost/x> +#else +# define BOOST_INCLUDE(x) <carve/external/boost/x> +#endif + +#include <math.h> + +#include <string> +#include <set> +#include <map> +#include <vector> +#include <list> +#include <sstream> +#include <iomanip> + +#include <carve/collection.hpp> + +#include <carve/util.hpp> + +#include <stdarg.h> + +#define STR(x) #x +#define XSTR(x) STR(x) + +/** + * \brief Top level Carve namespace. + */ +namespace carve { + static struct noinit_t {} NOINIT; + + inline std::string fmtstring(const char *fmt, ...); + + /** + * \brief Base class for all Carve exceptions. + */ + struct exception { + private: + mutable std::string err; + mutable std::ostringstream accum; + + public: + exception(const std::string &e) : err(e), accum() { } + exception() : err(), accum() { } + exception(const exception &e) : err(e.str()), accum() { } + exception &operator=(const exception &e) { + if (this != &e) { + err = e.str(); + accum.str(""); + } + return *this; + } + + const std::string &str() const { + if (accum.str().size() > 0) { + err += accum.str(); + accum.str(""); + } + return err; + } + + template<typename T> + exception &operator<<(const T &t) { + accum << t; + return *this; + } + }; + + template<typename iter_t, typename order_t = std::less<typename std::iterator_traits<iter_t>::value_type > > + struct index_sort { + iter_t base; + order_t order; + index_sort(const iter_t &_base) : base(_base), order() { } + index_sort(const iter_t &_base, const order_t &_order) : base(_base), order(_order) { } + template<typename U> + bool operator()(const U &a, const U &b) { + return order(*(base + a), *(base + b)); + } + }; + + template<typename iter_t, typename order_t> + index_sort<iter_t, order_t> make_index_sort(const iter_t &base, const order_t &order) { + return index_sort<iter_t, order_t>(base, order); + } + + template<typename iter_t> + index_sort<iter_t> make_index_sort(const iter_t &base) { + return index_sort<iter_t>(base); + } + + + enum RayIntersectionClass { + RR_DEGENERATE = -2, + RR_PARALLEL = -1, + RR_NO_INTERSECTION = 0, + RR_INTERSECTION = 1 + }; + + enum LineIntersectionClass { + COLINEAR = -1, + NO_INTERSECTION = 0, + INTERSECTION_LL = 1, + INTERSECTION_PL = 2, + INTERSECTION_LP = 3, + INTERSECTION_PP = 4 + }; + + enum PointClass { + POINT_UNK = -2, + POINT_OUT = -1, + POINT_ON = 0, + POINT_IN = 1, + POINT_VERTEX = 2, + POINT_EDGE = 3 + }; + + enum IntersectionClass { + INTERSECT_BAD = -1, + INTERSECT_NONE = 0, + INTERSECT_FACE = 1, + INTERSECT_VERTEX = 2, + INTERSECT_EDGE = 3, + INTERSECT_PLANE = 4, + }; + + + + extern double EPSILON; + extern double EPSILON2; + + static inline void setEpsilon(double ep) { EPSILON = ep; EPSILON2 = ep * ep; } + + + + template<typename T> + struct identity_t { + typedef T argument_type; + typedef T result_type; + const T &operator()(const T &t) const { return t; } + }; + + + + template<typename iter_t> + inline bool is_sorted(iter_t first, iter_t last) { + if (first == last) return true; + + iter_t iter = first; + iter_t next = first; ++next; + for (; next != last; iter = next, ++next) { + if (*next < *iter) { + return false; + } + } + return true; + } + + + + template<typename iter_t, + typename pred_t> + inline bool is_sorted(iter_t first, iter_t last, pred_t pred) { + if (first == last) return true; + + iter_t iter = first; + iter_t next = first; ++next; + for (; next != last; iter = next, ++next) { + if (pred(*next, *iter)) { + return false; + } + } + return true; + } + + + + inline double rangeSeparation(const std::pair<double, double> &a, + const std::pair<double, double> &b) { + if (a.second < b.first) { + return b.first - a.second; + } else if (b.second < a.first) { + return a.first - b.second; + } else { + return 0.0; + } + } +} + + +#if defined(_MSC_VER) +# define MACRO_BEGIN do { +# define MACRO_END __pragma(warning(push)) __pragma(warning(disable:4127)) } while(0) __pragma(warning(pop)) +#else +# define MACRO_BEGIN do { +# define MACRO_END } while(0) +#endif + +#if !defined(CARVE_NODEBUG) +# define CARVE_ASSERT(x) MACRO_BEGIN if (!(x)) throw carve::exception() << __FILE__ << ":" << __LINE__ << " " << #x; MACRO_END +#else +# define CARVE_ASSERT(X) +#endif + +#define CARVE_FAIL(x) MACRO_BEGIN throw carve::exception() << __FILE__ << ":" << __LINE__ << " " << #x; MACRO_END diff --git a/extern/carve/include/carve/cbrt.h b/extern/carve/include/carve/cbrt.h new file mode 100644 index 00000000000..aeceab01426 --- /dev/null +++ b/extern/carve/include/carve/cbrt.h @@ -0,0 +1,93 @@ +// N.B. only appropriate for IEEE doubles. +// Cube root implementation obtained from code with the following notice: + +/* @(#)s_cbrt.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* Sometimes it's necessary to define __LITTLE_ENDIAN explicitly + but these catch some common cases. */ + +#if defined(i386) || defined(i486) || \ + defined(intel) || defined(x86) || defined(i86pc) || \ + defined(__alpha) || defined(__osf__) +#define __LITTLE_ENDIAN +#endif + +#ifdef __LITTLE_ENDIAN +#define __HI(x) *(1+(int*)&x) +#define __LO(x) *(int*)&x +#define __HIp(x) *(1+(int*)x) +#define __LOp(x) *(int*)x +#else +#define __HI(x) *(int*)&x +#define __LO(x) *(1+(int*)&x) +#define __HIp(x) *(int*)x +#define __LOp(x) *(1+(int*)x) +#endif + +/* cbrt(x) + * Return cube root of x + */ + +inline double cbrt(double x) { + + static const unsigned + B1 = 715094163, /* B1 = (682-0.03306235651)*2**20 */ + B2 = 696219795; /* B2 = (664-0.03306235651)*2**20 */ + static const double + C = 5.42857142857142815906e-01, /* 19/35 = 0x3FE15F15, 0xF15F15F1 */ + D = -7.05306122448979611050e-01, /* -864/1225 = 0xBFE691DE, 0x2532C834 */ + E = 1.41428571428571436819e+00, /* 99/70 = 0x3FF6A0EA, 0x0EA0EA0F */ + F = 1.60714285714285720630e+00, /* 45/28 = 0x3FF9B6DB, 0x6DB6DB6E */ + G = 3.57142857142857150787e-01; /* 5/14 = 0x3FD6DB6D, 0xB6DB6DB7 */ + + int hx; + double r,s,t=0.0,w; + unsigned sign; + + hx = __HI(x); /* high word of x */ + sign=hx&0x80000000; /* sign= sign(x) */ + hx ^=sign; + if(hx>=0x7ff00000) return(x+x); /* cbrt(NaN,INF) is itself */ + if((hx|__LO(x))==0) + return(x); /* cbrt(0) is itself */ + + __HI(x) = hx; /* x <- |x| */ + /* rough cbrt to 5 bits */ + if(hx<0x00100000) /* subnormal number */ + {__HI(t)=0x43500000; /* set t= 2**54 */ + t*=x; __HI(t)=__HI(t)/3+B2; + } + else + __HI(t)=hx/3+B1; + + + /* new cbrt to 23 bits, may be implemented in single precision */ + r=t*t/x; + s=C+r*t; + t*=G+F/(s+E+D/s); + + /* chopped to 20 bits and make it larger than cbrt(x) */ + __LO(t)=0; __HI(t)+=0x00000001; + + /* one step newton iteration to 53 bits with error less than 0.667 ulps */ + s=t*t; /* t*t is exact */ + r=x/s; + w=t+t; + r=(r-t)/(w+r); /* r-s is exact */ + t=t+t*r; + + /* retore the sign bit */ + __HI(t) |= sign; + return(t); +} diff --git a/extern/carve/include/carve/classification.hpp b/extern/carve/include/carve/classification.hpp new file mode 100644 index 00000000000..7df86db7823 --- /dev/null +++ b/extern/carve/include/carve/classification.hpp @@ -0,0 +1,115 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> +#include <carve/collection_types.hpp> + +namespace carve { + namespace csg { + + enum FaceClass { + FACE_UNCLASSIFIED = -3, + FACE_ON_ORIENT_OUT = -2, + FACE_OUT = -1, + FACE_ON = 0, + FACE_IN = +1, + FACE_ON_ORIENT_IN = +2 + }; + + enum FaceClassBit { + FACE_ON_ORIENT_OUT_BIT = 0x01, + FACE_OUT_BIT = 0x02, + FACE_IN_BIT = 0x04, + FACE_ON_ORIENT_IN_BIT = 0x08, + + FACE_ANY_BIT = 0x0f, + FACE_ON_BIT = 0x09, + FACE_NOT_ON_BIT = 0x06 + }; + + static inline FaceClass class_bit_to_class(unsigned i) { + if (i & FACE_ON_ORIENT_OUT_BIT) return FACE_ON_ORIENT_OUT; + if (i & FACE_OUT_BIT) return FACE_OUT; + if (i & FACE_IN_BIT) return FACE_IN; + if (i & FACE_ON_ORIENT_IN_BIT) return FACE_ON_ORIENT_IN; + return FACE_UNCLASSIFIED; + } + + static inline unsigned class_to_class_bit(FaceClass f) { + switch (f) { + case FACE_ON_ORIENT_OUT: return FACE_ON_ORIENT_OUT_BIT; + case FACE_OUT: return FACE_OUT_BIT; + case FACE_ON: return FACE_ON_BIT; + case FACE_IN: return FACE_IN_BIT; + case FACE_ON_ORIENT_IN: return FACE_ON_ORIENT_IN_BIT; + case FACE_UNCLASSIFIED: return FACE_ANY_BIT; + default: return 0; + } + } + + enum EdgeClass { + EDGE_UNK = -2, + EDGE_OUT = -1, + EDGE_ON = 0, + EDGE_IN = 1 + }; + + + + const char *ENUM(FaceClass f); + const char *ENUM(PointClass p); + + + + struct ClassificationInfo { + const carve::mesh::Mesh<3> *intersected_mesh; + FaceClass classification; + + ClassificationInfo() : intersected_mesh(NULL), classification(FACE_UNCLASSIFIED) { } + ClassificationInfo(const carve::mesh::Mesh<3> *_intersected_mesh, + FaceClass _classification) : + intersected_mesh(_intersected_mesh), + classification(_classification) { + } + bool intersectedMeshIsClosed() const { + return intersected_mesh->isClosed(); + } + }; + + + + struct EC2 { + EdgeClass cls[2]; + EC2() { cls[0] = cls[1] = EDGE_UNK; } + EC2(EdgeClass a, EdgeClass b) { cls[0] = a; cls[1] = b; } + }; + + struct PC2 { + PointClass cls[2]; + PC2() { cls[0] = cls[1] = POINT_UNK; } + PC2(PointClass a, PointClass b) { cls[0] = a; cls[1] = b; } + }; + + typedef std::unordered_map<std::pair<const carve::mesh::MeshSet<3>::vertex_t *, const carve::mesh::MeshSet<3>::vertex_t *>, + EC2> EdgeClassification; + + typedef std::unordered_map<const carve::mesh::Vertex<3> *, PC2> VertexClassification; + + } +} diff --git a/extern/carve/include/carve/collection.hpp b/extern/carve/include/carve/collection.hpp new file mode 100644 index 00000000000..ace63d174f6 --- /dev/null +++ b/extern/carve/include/carve/collection.hpp @@ -0,0 +1,51 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/collection/unordered.hpp> + +namespace carve { + + template<typename set_t> + class set_insert_iterator : public std::iterator<std::output_iterator_tag, void, void, void, void> { + + protected: + set_t *set; + public: + + set_insert_iterator(set_t &s) : set(&s) { + } + + set_insert_iterator & + operator=(typename set_t::const_reference value) { + set->insert(value); + return *this; + } + + set_insert_iterator &operator*() { return *this; } + set_insert_iterator &operator++() { return *this; } + set_insert_iterator &operator++(int) { return *this; } + }; + + template<typename set_t> + inline set_insert_iterator<set_t> + set_inserter(set_t &s) { + return set_insert_iterator<set_t>(s); + } + +} diff --git a/extern/carve/include/carve/collection/unordered.hpp b/extern/carve/include/carve/collection/unordered.hpp new file mode 100644 index 00000000000..494a1ca4013 --- /dev/null +++ b/extern/carve/include/carve/collection/unordered.hpp @@ -0,0 +1,43 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#pragma once + +#if defined(HAVE_STD_UNORDERED_COLLECTIONS) + +# include <carve/collection/unordered/std_impl.hpp> + +#elif defined(HAVE_TR1_UNORDERED_COLLECTIONS) + +# include <carve/collection/unordered/tr1_impl.hpp> + +#elif defined(HAVE_BOOST_UNORDERED_COLLECTIONS) + +# include <carve/collection/unordered/boost_impl.hpp> + +#elif defined(HAVE_LIBSTDCPP_UNORDERED_COLLECTIONS) + +# include <carve/collection/unordered/libstdcpp_impl.hpp> + +#elif defined(_MSC_VER) && _MSC_VER >= 1300 + +# include <carve/collection/unordered/vcpp_impl.hpp> + +#else + +# include <carve/collection/unordered/fallback_impl.hpp> + +#endif diff --git a/extern/carve/include/carve/collection/unordered/boost_impl.hpp b/extern/carve/include/carve/collection/unordered/boost_impl.hpp new file mode 100644 index 00000000000..96b1407e9eb --- /dev/null +++ b/extern/carve/include/carve/collection/unordered/boost_impl.hpp @@ -0,0 +1,45 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include BOOST_INCLUDE(unordered_set.hpp) +#include BOOST_INCLUDE(unordered_map.hpp) + +#include <functional> + +namespace std { + template <typename Key, typename T, typename Hash = boost::hash<Key>, + typename Pred = std::equal_to<Key> > + class unordered_map : public boost::unordered_map<Key, T, Hash, Pred> { + + public: + typedef T data_type; + }; + + template <typename Key, typename T, typename Hash = boost::hash<Key>, + typename Pred = std::equal_to<Key> > + class unordered_multimap : public boost::unordered_multimap<Key, T, Hash, Pred> { + }; + + template <typename Value, typename Hash = boost::hash<Value>, + typename Pred = std::equal_to<Value> > + class unordered_set : public boost::unordered_set<Value, Hash, Pred> { + }; +} + +#undef UNORDERED_COLLECTIONS_SUPPORT_RESIZE diff --git a/extern/carve/include/carve/collection/unordered/fallback_impl.hpp b/extern/carve/include/carve/collection/unordered/fallback_impl.hpp new file mode 100644 index 00000000000..70422b2a215 --- /dev/null +++ b/extern/carve/include/carve/collection/unordered/fallback_impl.hpp @@ -0,0 +1,40 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <set> +#include <map> + +namespace std { + + template<typename K, typename T, typename H = int> + class unordered_map : public std::map<K, T> { + typedef std::map<K, T> super; + public: + typedef T data_type; + }; + + template<typename K, typename H = int> + class unordered_set : public std::set<K> { + typedef std::set<K> super; + public: + }; + +} + +#undef UNORDERED_COLLECTIONS_SUPPORT_RESIZE diff --git a/extern/carve/include/carve/collection/unordered/libstdcpp_impl.hpp b/extern/carve/include/carve/collection/unordered/libstdcpp_impl.hpp new file mode 100644 index 00000000000..e1ad430709d --- /dev/null +++ b/extern/carve/include/carve/collection/unordered/libstdcpp_impl.hpp @@ -0,0 +1,61 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#pragma once + +#include <ext/hash_map> +#include <ext/hash_set> + +namespace __gnu_cxx { + template <typename T> + struct hash<T *> : public std::unary_function<T *, size_t> { + size_t operator()(T *v) const { + size_t x = (size_t)(v); + return x + (x>>3); + } + }; + + template <typename A, typename B> + struct hash<std::pair<A, B> > : public std::unary_function<std::pair<A, B>, size_t> { + size_t operator()(const std::pair<A, B> &v) const { + std::size_t seed = 0; + + seed ^= hash<A>()(v.first); + seed ^= hash<B>()(v.second) + (seed<<6) + (seed>>2); + + return seed; + } + }; +} + +namespace std { + + template<typename K, typename V, typename H = __gnu_cxx::hash<K> > + class unordered_map : public __gnu_cxx::hash_map<K, V, H> { + typedef __gnu_cxx::hash_map<K, V, H> super; + public: + typedef typename super::mapped_type data_type; + }; + + template<typename K, typename H = __gnu_cxx::hash<K> > + class unordered_set : public __gnu_cxx::hash_set<K, H> { + typedef __gnu_cxx::hash_set<K, H> super; + public: + }; + +} + +#define UNORDERED_COLLECTIONS_SUPPORT_RESIZE 1 diff --git a/extern/carve/include/carve/collection/unordered/std_impl.hpp b/extern/carve/include/carve/collection/unordered/std_impl.hpp new file mode 100644 index 00000000000..68a3f122e86 --- /dev/null +++ b/extern/carve/include/carve/collection/unordered/std_impl.hpp @@ -0,0 +1,23 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <unordered_map> +#include <unordered_set> + +#undef UNORDERED_COLLECTIONS_SUPPORT_RESIZE diff --git a/extern/carve/include/carve/collection/unordered/tr1_impl.hpp b/extern/carve/include/carve/collection/unordered/tr1_impl.hpp new file mode 100644 index 00000000000..2c7f39f6a74 --- /dev/null +++ b/extern/carve/include/carve/collection/unordered/tr1_impl.hpp @@ -0,0 +1,58 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <tr1/unordered_map> +#include <tr1/unordered_set> +#include <tr1/functional> + +namespace std { + namespace tr1 { + template <typename A, typename B> + struct hash<std::pair<A, B> > : public std::unary_function<std::pair<A, B>, size_t> { + size_t operator()(const std::pair<A, B> &v) const { + std::size_t seed = 0; + + seed ^= hash<A>()(v.first); + seed ^= hash<B>()(v.second) + (seed<<6) + (seed>>2); + + return seed; + } + }; + } + + + + template <typename Key, typename T, + typename Hash = tr1::hash<Key>, + typename Pred = std::equal_to<Key> > + class unordered_map : public std::tr1::unordered_map<Key, T, Hash, Pred> { + public: + typedef T data_type; + }; + + template <typename Value, + typename Hash = tr1::hash<Value>, + typename Pred = std::equal_to<Value> > + class unordered_set : public std::tr1::unordered_set<Value, Hash, Pred> { + public: + }; + +} + +#undef UNORDERED_COLLECTIONS_SUPPORT_RESIZE diff --git a/extern/carve/include/carve/collection/unordered/vcpp_impl.hpp b/extern/carve/include/carve/collection/unordered/vcpp_impl.hpp new file mode 100644 index 00000000000..825edad1433 --- /dev/null +++ b/extern/carve/include/carve/collection/unordered/vcpp_impl.hpp @@ -0,0 +1,65 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <hash_map> +#include <hash_set> + +namespace std { + + namespace { + + template<class Value, class Hash> class hash_traits { + Hash hash_value; + std::less<Value> comp; + public: + enum { + bucket_size = 4, + min_buckets = 8 + }; + // hash _Keyval to size_t value + size_t operator()(const Value& v) const { + return ((size_t)hash_value(v)); + } + // test if _Keyval1 ordered before _Keyval2 + bool operator()(const Value& v1, const Value& v2) const { + return (comp(v1, v2)); + } + }; + + } + + template <typename Key, typename T, typename Hash = stdext::hash_compare<Key, less<Key> >, typename Pred = std::equal_to<Key> > + class unordered_map + : public stdext::hash_map<Key, T, hash_traits<Key, Hash> > { + typedef stdext::hash_map<Key, T, hash_traits<Key, Hash> > super; + public: + unordered_map() : super() {} + }; + + template <typename Value, typename Hash = stdext::hash_compare<Key, less<Key> >, typename Pred = std::equal_to<Value> > + class unordered_set + : public stdext::hash_set<Value, hash_traits<Value, Hash> > { + typedef stdext::hash_set<Value, hash_traits<Value, Hash> > super; + public: + unordered_set() : super() {} + }; + +} + +#undef UNORDERED_COLLECTIONS_SUPPORT_RESIZE diff --git a/extern/carve/include/carve/collection_types.hpp b/extern/carve/include/carve/collection_types.hpp new file mode 100644 index 00000000000..997d5811801 --- /dev/null +++ b/extern/carve/include/carve/collection_types.hpp @@ -0,0 +1,63 @@ + +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/mesh.hpp> + +namespace carve { + namespace csg { + + typedef std::pair< + carve::mesh::MeshSet<3>::vertex_t *, + carve::mesh::MeshSet<3>::vertex_t *> V2; + + typedef std::pair< + carve::mesh::MeshSet<3>::face_t *, + carve::mesh::MeshSet<3>::face_t *> F2; + + static inline V2 ordered_edge( + carve::mesh::MeshSet<3>::vertex_t *a, + carve::mesh::MeshSet<3>::vertex_t *b) { + return V2(std::min(a, b), std::max(a, b)); + } + + static inline V2 flip(const V2 &v) { + return V2(v.second, v.first); + } + + // include/carve/csg.hpp include/carve/faceloop.hpp + // lib/intersect.cpp lib/intersect_classify_common_impl.hpp + // lib/intersect_classify_edge.cpp + // lib/intersect_classify_group.cpp + // lib/intersect_classify_simple.cpp + // lib/intersect_face_division.cpp lib/intersect_group.cpp + // lib/intersect_half_classify_group.cpp + typedef std::unordered_set<V2> V2Set; + + // include/carve/csg.hpp include/carve/polyhedron_decl.hpp + // lib/csg_collector.cpp lib/intersect.cpp + // lib/intersect_common.hpp lib/intersect_face_division.cpp + // lib/polyhedron.cpp + typedef std::unordered_map< + carve::mesh::MeshSet<3>::vertex_t *, + carve::mesh::MeshSet<3>::vertex_t *> VVMap; + } +} diff --git a/extern/carve/include/carve/colour.hpp b/extern/carve/include/carve/colour.hpp new file mode 100644 index 00000000000..420f8f7e13b --- /dev/null +++ b/extern/carve/include/carve/colour.hpp @@ -0,0 +1,47 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> +#include <carve/geom.hpp> + +namespace carve { + namespace colour { + static inline void HSV2RGB(float H, float S, float V, float &r, float &g, float &b) { + H = 6.0f * H; + if (S < 5.0e-6) { + r = g = b = V; return; + } else { + int i = (int)H; + float f = H - i; + float p1 = V * (1.0f - S); + float p2 = V * (1.0f - S * f); + float p3 = V * (1.0f - S * (1.0f - f)); + switch (i) { + case 0: r = V; g = p3; b = p1; return; + case 1: r = p2; g = V; b = p1; return; + case 2: r = p1; g = V; b = p3; return; + case 3: r = p1; g = p2; b = V; return; + case 4: r = p3; g = p1; b = V; return; + case 5: r = V; g = p1; b = p2; return; + } + } + r = g = b = 0.0; + } + } +} diff --git a/extern/carve/include/carve/config.h b/extern/carve/include/carve/config.h new file mode 100644 index 00000000000..fdae2d2843f --- /dev/null +++ b/extern/carve/include/carve/config.h @@ -0,0 +1,12 @@ +#define CARVE_VERSION "2.0.0a" + +#undef CARVE_DEBUG +#undef CARVE_DEBUG_WRITE_PLY_DATA + +#if defined(__GNUC__) +# if !defined(HAVE_BOOST_UNORDERED_COLLECTIONS) +# define HAVE_TR1_UNORDERED_COLLECTIONS +# endif + +# define HAVE_STDINT_H +#endif diff --git a/extern/carve/include/carve/convex_hull.hpp b/extern/carve/include/carve/convex_hull.hpp new file mode 100644 index 00000000000..bcb7c97e20f --- /dev/null +++ b/extern/carve/include/carve/convex_hull.hpp @@ -0,0 +1,52 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <list> +#include <vector> +#include <algorithm> + +#include <carve/carve.hpp> + +#include <carve/geom2d.hpp> + +namespace carve { + namespace geom { + std::vector<int> convexHull(const std::vector<carve::geom2d::P2> &points); + + template<typename project_t, typename polygon_container_t> + std::vector<int> convexHull(const project_t &project, const polygon_container_t &points) { + std::vector<carve::geom2d::P2> proj; + proj.reserve(points.size()); + for (typename polygon_container_t::const_iterator i = points.begin(); i != points.end(); ++i) { + proj.push_back(project(*i)); + } + return convexHull(proj); + } + + template<typename project_t, typename iter_t> + std::vector<int> convexHull(const project_t &project, iter_t beg, iter_t end, size_t size_hint = 0) { + std::vector<carve::geom2d::P2> proj; + if (size_hint) proj.reserve(size_hint); + for (; beg != end; ++beg) { + proj.push_back(project(*beg)); + } + return convexHull(proj); + } + } +} diff --git a/extern/carve/include/carve/csg.hpp b/extern/carve/include/carve/csg.hpp new file mode 100644 index 00000000000..db32273a33c --- /dev/null +++ b/extern/carve/include/carve/csg.hpp @@ -0,0 +1,498 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <list> +#include <vector> +#include <algorithm> + +#include <carve/carve.hpp> + +#include <carve/geom3d.hpp> + +#include <carve/mesh.hpp> + +#include <carve/collection_types.hpp> +#include <carve/classification.hpp> +#include <carve/iobj.hpp> +#include <carve/faceloop.hpp> +#include <carve/intersection.hpp> +#include <carve/rtree.hpp> + +namespace carve { + namespace csg { + + class VertexPool { + typedef carve::mesh::MeshSet<3>::vertex_t vertex_t; + + const static unsigned blocksize = 1024; + typedef std::list<std::vector<vertex_t> > pool_t; + pool_t pool; + public: + void reset(); + vertex_t *get(const vertex_t::vector_t &v = vertex_t::vector_t::ZERO()); + bool inPool(vertex_t *v) const; + + VertexPool(); + ~VertexPool(); + }; + + + + namespace detail { + struct Data; + class LoopEdges; + } + + /** + * \class CSG + * \brief The class responsible for the computation of CSG operations. + * + */ + class CSG { + private: + + public: + typedef carve::mesh::MeshSet<3> meshset_t; + + struct Hook { + /** + * \class Hook + * \brief Provides API access to intermediate steps in CSG calculation. + * + */ + virtual void intersectionVertex(const meshset_t::vertex_t * /* vertex */, + const IObjPairSet & /* intersections */) { + } + virtual void processOutputFace(std::vector<meshset_t::face_t *> & /* faces */, + const meshset_t::face_t * /* orig_face */, + bool /* flipped */) { + } + virtual void resultFace(const meshset_t::face_t * /* new_face */, + const meshset_t::face_t * /* orig_face */, + bool /* flipped */) { + } + + virtual ~Hook() { + } + }; + + /** + * \class Hooks + * \brief Management of API hooks. + * + */ + class Hooks { + public: + enum { + RESULT_FACE_HOOK = 0, + PROCESS_OUTPUT_FACE_HOOK = 1, + INTERSECTION_VERTEX_HOOK = 2, + HOOK_MAX = 3, + + RESULT_FACE_BIT = 0x0001, + PROCESS_OUTPUT_FACE_BIT = 0x0002, + INTERSECTION_VERTEX_BIT = 0x0004 + }; + + std::vector<std::list<Hook *> > hooks; + + bool hasHook(unsigned hook_num); + + void intersectionVertex(const meshset_t::vertex_t *vertex, + const IObjPairSet &intersections); + + void processOutputFace(std::vector<meshset_t::face_t *> &faces, + const meshset_t::face_t *orig_face, + bool flipped); + + void resultFace(const meshset_t::face_t *new_face, + const meshset_t::face_t *orig_face, + bool flipped); + + void registerHook(Hook *hook, unsigned hook_bits); + void unregisterHook(Hook *hook); + + void reset(); + + Hooks(); + ~Hooks(); + }; + + /** + * \class Collector + * \brief Base class for objects responsible for selecting result from which form the result polyhedron. + * + */ + class Collector { + Collector(const Collector &); + Collector &operator=(const Collector &); + + protected: + + public: + virtual void collect(FaceLoopGroup *group, CSG::Hooks &) =0; + virtual meshset_t *done(CSG::Hooks &) =0; + + Collector() {} + virtual ~Collector() {} + }; + + private: + typedef carve::geom::RTreeNode<3, carve::mesh::Face<3> *> face_rtree_t; + typedef std::unordered_map<carve::mesh::Face<3> *, std::vector<carve::mesh::Face<3> *> > face_pairs_t; + + /// The computed intersection data. + Intersections intersections; + + /// A map from intersection point to a set of intersections + /// represented by pairs of intersection objects. + VertexIntersections vertex_intersections; + + /// A pool from which temporary vertices are allocated. Also + /// provides testing for pool membership. + VertexPool vertex_pool; + + void init(); + + void makeVertexIntersections(); + + void groupIntersections(); + + void _generateVertexVertexIntersections(carve::mesh::MeshSet<3>::vertex_t *va, + carve::mesh::MeshSet<3>::edge_t *eb); + void generateVertexVertexIntersections(carve::mesh::MeshSet<3>::face_t *a, + const std::vector<carve::mesh::MeshSet<3>::face_t *> &b); + + void _generateVertexEdgeIntersections(carve::mesh::MeshSet<3>::vertex_t *va, + carve::mesh::MeshSet<3>::edge_t *eb); + void generateVertexEdgeIntersections(carve::mesh::MeshSet<3>::face_t *a, + const std::vector<carve::mesh::MeshSet<3>::face_t *> &b); + + void _generateEdgeEdgeIntersections(carve::mesh::MeshSet<3>::edge_t *ea, + carve::mesh::MeshSet<3>::edge_t *eb); + void generateEdgeEdgeIntersections(carve::mesh::MeshSet<3>::face_t *a, + const std::vector<carve::mesh::MeshSet<3>::face_t *> &b); + + void _generateVertexFaceIntersections(carve::mesh::MeshSet<3>::face_t *fa, + carve::mesh::MeshSet<3>::edge_t *eb); + void generateVertexFaceIntersections(carve::mesh::MeshSet<3>::face_t *a, + const std::vector<carve::mesh::MeshSet<3>::face_t *> &b); + + void _generateEdgeFaceIntersections(carve::mesh::MeshSet<3>::face_t *fa, + carve::mesh::MeshSet<3>::edge_t *eb); + void generateEdgeFaceIntersections(carve::mesh::MeshSet<3>::face_t *a, + const std::vector<carve::mesh::MeshSet<3>::face_t *> &b); + + void generateIntersectionCandidates(carve::mesh::MeshSet<3> *a, + const face_rtree_t *a_node, + carve::mesh::MeshSet<3> *b, + const face_rtree_t *b_node, + face_pairs_t &face_pairs, + bool descend_a = true); + /** + * \brief Compute all points of intersection between poly \a a and poly \a b + * + * @param a Polyhedron a. + * @param b Polyhedron b. + */ + void generateIntersections(meshset_t *a, + const face_rtree_t *a_node, + meshset_t *b, + const face_rtree_t *b_node, + detail::Data &data); + + /** + * \brief Generate tables of intersecting pairs of faces. + * + * @param[out] data Internal data-structure holding intersection info. + */ + void intersectingFacePairs(detail::Data &data); + + /** + * \brief Divide edges in \a edges that are intersected by polyhedron \a poly + * + * @param edges The edges to divide. + * @param[in] poly The polyhedron to divide against. + * @param[in,out] data Intersection information. + */ + void divideEdges( + const std::vector<meshset_t::edge_t> &edges, + meshset_t *poly, + detail::Data &data); + + void divideIntersectedEdges(detail::Data &data); + + /** + * \brief From the intersection points of pairs of intersecting faces, compute intersection edges. + * + * @param[out] eclass Classification information about created edges. + * @param[in,out] data Intersection information. + */ + void makeFaceEdges( + EdgeClassification &eclass, + detail::Data &data); + + friend void classifyEasyFaces( + FaceLoopList &face_loops, + VertexClassification &vclass, + meshset_t *other_poly, + int other_poly_num, + CSG &csg, + CSG::Collector &collector); + + size_t generateFaceLoops( + meshset_t *poly, + const detail::Data &data, + FaceLoopList &face_loops_out); + + + + // intersect_group.cpp + + /** + * \brief Build a loop edge mapping from a list of face loops. + * + * @param[in] loops A list of face loops. + * @param[in] edge_count A hint as to the number of edges in \a loops. + * @param[out] edge_map The calculated map of edges to loops. + */ + void makeEdgeMap( + const FaceLoopList &loops, + size_t edge_count, + detail::LoopEdges &edge_map); + + /** + * \brief Divide a list of face loops into groups that are connected by at least one edge not present in \a no_cross. + * + * @param[in] src The source mesh from which these loops derive. + * @param[in,out] face_loops The list of loops (will be emptied as a side effect) + * @param[in] loop_edges A loop edge map used for traversing connected loops. + * @param[in] no_cross A set of edges not to cross. + * @param[out] out_loops A list of grouped face loops. + */ + void groupFaceLoops( + carve::mesh::MeshSet<3> *src, + FaceLoopList &face_loops, + const detail::LoopEdges &loop_edges, + const V2Set &no_cross, + FLGroupList &out_loops); + + /** + * \brief Find the set of edges shared between two edge maps. + * + * @param[in] edge_map_a The first edge map. + * @param[in] edge_map_b The second edge map. + * @param[out] shared_edges The resulting set of common edges. + */ + void findSharedEdges( + const detail::LoopEdges &edge_map_a, + const detail::LoopEdges &edge_map_b, + V2Set &shared_edges); + + + // intersect_classify_edge.cpp + + /** + * + * + * @param shared_edges + * @param vclass + * @param poly_a + * @param a_loops_grouped + * @param a_edge_map + * @param poly_b + * @param b_loops_grouped + * @param b_edge_map + * @param collector + */ + void classifyFaceGroupsEdge( + const V2Set &shared_edges, + VertexClassification &vclass, + meshset_t *poly_a, + const face_rtree_t *poly_a_rtree, + FLGroupList &a_loops_grouped, + const detail::LoopEdges &a_edge_map, + meshset_t *poly_b, + const face_rtree_t *poly_b_rtree, + FLGroupList &b_loops_grouped, + const detail::LoopEdges &b_edge_map, + CSG::Collector &collector); + + // intersect_classify_group.cpp + + /** + * + * + * @param shared_edges + * @param vclass + * @param poly_a + * @param a_loops_grouped + * @param a_edge_map + * @param poly_b + * @param b_loops_grouped + * @param b_edge_map + * @param collector + */ + void classifyFaceGroups( + const V2Set &shared_edges, + VertexClassification &vclass, + meshset_t *poly_a, + const face_rtree_t *poly_a_rtree, + FLGroupList &a_loops_grouped, + const detail::LoopEdges &a_edge_map, + meshset_t *poly_b, + const face_rtree_t *poly_b_rtree, + FLGroupList &b_loops_grouped, + const detail::LoopEdges &b_edge_map, + CSG::Collector &collector); + + // intersect_half_classify_group.cpp + + /** + * + * + * @param shared_edges + * @param vclass + * @param poly_a + * @param a_loops_grouped + * @param a_edge_map + * @param poly_b + * @param b_loops_grouped + * @param b_edge_map + * @param FaceClass + * @param b_out + */ + void halfClassifyFaceGroups( + const V2Set &shared_edges, + VertexClassification &vclass, + meshset_t *poly_a, + const face_rtree_t *poly_a_rtree, + FLGroupList &a_loops_grouped, + const detail::LoopEdges &a_edge_map, + meshset_t *poly_b, + const face_rtree_t *poly_b_rtree, + FLGroupList &b_loops_grouped, + const detail::LoopEdges &b_edge_map, + std::list<std::pair<FaceClass, meshset_t *> > &b_out); + + // intersect.cpp + + /** + * \brief The main calculation method for CSG. + * + * @param[in] a Polyhedron a + * @param[in] b Polyhedron b + * @param[out] vclass + * @param[out] eclass + * @param[out] a_face_loops + * @param[out] b_face_loops + * @param[out] a_edge_count + * @param[out] b_edge_count + */ + void calc( + meshset_t *a, + const face_rtree_t *a_rtree, + meshset_t *b, + const face_rtree_t *b_rtree, + VertexClassification &vclass, + EdgeClassification &eclass, + FaceLoopList &a_face_loops, + FaceLoopList &b_face_loops, + size_t &a_edge_count, + size_t &b_edge_count); + + public: + /** + * \enum OP + * \brief Enumeration of the supported CSG operations. + */ + enum OP { + UNION, /**< in a or b. */ + INTERSECTION, /**< in a and b. */ + A_MINUS_B, /**< in a, but not b. */ + B_MINUS_A, /**< in b, but not a. */ + SYMMETRIC_DIFFERENCE, /**< in a or b, but not both. */ + ALL /**< all split faces from a and b */ + }; + + /** + * \enum CLASSIFY_TYPE + * \brief The type of classification algorithm to use. + */ + enum CLASSIFY_TYPE { + CLASSIFY_NORMAL, /**< Normal (group) classifier. */ + CLASSIFY_EDGE /**< Edge classifier. */ + }; + + CSG::Hooks hooks; /**< The manager for calculation hooks. */ + + CSG(); + ~CSG(); + + /** + * \brief Compute a CSG operation between two polyhedra, \a a and \a b. + * + * @param a Polyhedron a + * @param b Polyhedron b + * @param collector The collector (determines the CSG operation performed) + * @param shared_edges A pointer to a set that will be populated with shared edges (if not NULL). + * @param classify_type The type of classifier to use. + * + * @return + */ + meshset_t *compute( + meshset_t *a, + meshset_t *b, + CSG::Collector &collector, + V2Set *shared_edges = NULL, + CLASSIFY_TYPE classify_type = CLASSIFY_NORMAL); + + /** + * \brief Compute a CSG operation between two closed polyhedra, \a a and \a b. + * + * @param a Polyhedron a + * @param b Polyhedron b + * @param op The CSG operation (A collector is created automatically). + * @param shared_edges A pointer to a set that will be populated with shared edges (if not NULL). + * @param classify_type The type of classifier to use. + * + * @return + */ + meshset_t *compute( + meshset_t *a, + meshset_t *b, + OP op, + V2Set *shared_edges = NULL, + CLASSIFY_TYPE classify_type = CLASSIFY_NORMAL); + + void slice( + meshset_t *a, + meshset_t *b, + std::list<meshset_t *> &a_sliced, + std::list<meshset_t *> &b_sliced, + V2Set *shared_edges = NULL); + + bool sliceAndClassify( + meshset_t *closed, + meshset_t *open, + std::list<std::pair<FaceClass, meshset_t *> > &result, + V2Set *shared_edges = NULL); + }; + } +} diff --git a/extern/carve/include/carve/csg_triangulator.hpp b/extern/carve/include/carve/csg_triangulator.hpp new file mode 100644 index 00000000000..740585571bf --- /dev/null +++ b/extern/carve/include/carve/csg_triangulator.hpp @@ -0,0 +1,434 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#include <carve/csg.hpp> +#include <carve/tag.hpp> +#include <carve/poly.hpp> +#include <carve/triangulator.hpp> +#include <deque> + +namespace carve { + namespace csg { + + namespace detail { + template<bool with_improvement> + class CarveTriangulator : public csg::CSG::Hook { + + public: + CarveTriangulator() { + } + + virtual ~CarveTriangulator() { + } + + virtual void processOutputFace(std::vector<carve::mesh::MeshSet<3>::face_t *> &faces, + const carve::mesh::MeshSet<3>::face_t *orig, + bool flipped) { + std::vector<carve::mesh::MeshSet<3>::face_t *> out_faces; + + size_t n_tris = 0; + for (size_t f = 0; f < faces.size(); ++f) { + CARVE_ASSERT(faces[f]->nVertices() >= 3); + n_tris += faces[f]->nVertices() - 2; + } + + out_faces.reserve(n_tris); + + for (size_t f = 0; f < faces.size(); ++f) { + carve::mesh::MeshSet<3>::face_t *face = faces[f]; + + if (face->nVertices() == 3) { + out_faces.push_back(face); + continue; + } + + std::vector<triangulate::tri_idx> result; + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> vloop; + face->getVertices(vloop); + + triangulate::triangulate( + carve::mesh::MeshSet<3>::face_t::projection_mapping(face->project), + vloop, + result); + + if (with_improvement) { + triangulate::improve( + carve::mesh::MeshSet<3>::face_t::projection_mapping(face->project), + vloop, + carve::mesh::vertex_distance(), + result); + } + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> fv; + fv.resize(3); + for (size_t i = 0; i < result.size(); ++i) { + fv[0] = vloop[result[i].a]; + fv[1] = vloop[result[i].b]; + fv[2] = vloop[result[i].c]; + out_faces.push_back(face->create(fv.begin(), fv.end(), false)); + } + delete face; + } + std::swap(faces, out_faces); + } + }; + } + + typedef detail::CarveTriangulator<false> CarveTriangulator; + typedef detail::CarveTriangulator<true> CarveTriangulatorWithImprovement; + + class CarveTriangulationImprover : public csg::CSG::Hook { + public: + CarveTriangulationImprover() { + } + + virtual ~CarveTriangulationImprover() { + } + + virtual void processOutputFace(std::vector<carve::mesh::MeshSet<3>::face_t *> &faces, + const carve::mesh::MeshSet<3>::face_t *orig, + bool flipped) { + if (faces.size() == 1) return; + + // doing improvement as a separate hook is much messier than + // just incorporating it into the triangulation hook. + + typedef std::map<carve::mesh::MeshSet<3>::vertex_t *, size_t> vert_map_t; + std::vector<carve::mesh::MeshSet<3>::face_t *> out_faces; + vert_map_t vert_map; + + out_faces.reserve(faces.size()); + + + carve::mesh::MeshSet<3>::face_t::projection_mapping projector(faces[0]->project); + + std::vector<triangulate::tri_idx> result; + + for (size_t f = 0; f < faces.size(); ++f) { + carve::mesh::MeshSet<3>::face_t *face = faces[f]; + if (face->nVertices() != 3) { + out_faces.push_back(face); + } else { + triangulate::tri_idx tri; + for (carve::mesh::MeshSet<3>::face_t::edge_iter_t i = face->begin(); i != face->end(); ++i) { + size_t v = 0; + vert_map_t::iterator j = vert_map.find(i->vert); + if (j == vert_map.end()) { + v = vert_map.size(); + vert_map[i->vert] = v; + } else { + v = (*j).second; + } + tri.v[i.idx()] = v; + } + result.push_back(tri); + delete face; + } + } + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> verts; + verts.resize(vert_map.size()); + for (vert_map_t::iterator i = vert_map.begin(); i != vert_map.end(); ++i) { + verts[(*i).second] = (*i).first; + } + + triangulate::improve(projector, verts, carve::mesh::vertex_distance(), result); + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> fv; + fv.resize(3); + for (size_t i = 0; i < result.size(); ++i) { + fv[0] = verts[result[i].a]; + fv[1] = verts[result[i].b]; + fv[2] = verts[result[i].c]; + out_faces.push_back(orig->create(fv.begin(), fv.end(), false)); + } + + std::swap(faces, out_faces); + } + }; + + class CarveTriangulationQuadMerger : public csg::CSG::Hook { + // this code is incomplete. + typedef std::map<V2, F2> edge_map_t; + + public: + CarveTriangulationQuadMerger() { + } + + virtual ~CarveTriangulationQuadMerger() { + } + + double scoreQuad(edge_map_t::iterator i, edge_map_t &edge_map) { + if (!(*i).second.first || !(*i).second.second) return -1; + return 0; + } + + carve::mesh::MeshSet<3>::face_t *mergeQuad(edge_map_t::iterator i, edge_map_t &edge_map) { + return NULL; + } + + void recordEdge(carve::mesh::MeshSet<3>::vertex_t *v1, + carve::mesh::MeshSet<3>::vertex_t *v2, + carve::mesh::MeshSet<3>::face_t *f, + edge_map_t &edge_map) { + if (v1 < v2) { + edge_map[V2(v1, v2)].first = f; + } else { + edge_map[V2(v2, v1)].second = f; + } + } + + virtual void processOutputFace(std::vector<carve::mesh::MeshSet<3>::face_t *> &faces, + const carve::mesh::MeshSet<3>::face_t *orig, + bool flipped) { + if (faces.size() == 1) return; + + std::vector<carve::mesh::MeshSet<3>::face_t *> out_faces; + edge_map_t edge_map; + + out_faces.reserve(faces.size()); + + poly::p2_adapt_project<3> projector(faces[0]->project); + + for (size_t f = 0; f < faces.size(); ++f) { + carve::mesh::MeshSet<3>::face_t *face = faces[f]; + if (face->nVertices() != 3) { + out_faces.push_back(face); + } else { + carve::mesh::MeshSet<3>::face_t::vertex_t *v1, *v2, *v3; + v1 = face->edge->vert; + v2 = face->edge->next->vert; + v3 = face->edge->next->next->vert; + recordEdge(v1, v2, face, edge_map); + recordEdge(v2, v3, face, edge_map); + recordEdge(v3, v1, face, edge_map); + } + } + + for (edge_map_t::iterator i = edge_map.begin(); i != edge_map.end();) { + if ((*i).second.first && (*i).second.second) { + ++i; + } else { + edge_map.erase(i++); + } + } + + while (edge_map.size()) { + edge_map_t::iterator i = edge_map.begin(); + edge_map_t::iterator best = i; + double best_score = scoreQuad(i, edge_map); + for (++i; i != edge_map.end(); ++i) { + double score = scoreQuad(i, edge_map); + if (score > best_score) best = i; + } + if (best_score < 0) break; + out_faces.push_back(mergeQuad(best, edge_map)); + } + + if (edge_map.size()) { + tagable::tag_begin(); + for (edge_map_t::iterator i = edge_map.begin(); i != edge_map.end(); ++i) { + carve::mesh::MeshSet<3>::face_t *a = const_cast<carve::mesh::MeshSet<3>::face_t *>((*i).second.first); + carve::mesh::MeshSet<3>::face_t *b = const_cast<carve::mesh::MeshSet<3>::face_t *>((*i).second.first); + if (a && a->tag_once()) out_faces.push_back(a); + if (b && b->tag_once()) out_faces.push_back(b); + } + } + + std::swap(faces, out_faces); + } + }; + + class CarveHoleResolver : public csg::CSG::Hook { + + public: + CarveHoleResolver() { + } + + virtual ~CarveHoleResolver() { + } + + bool findRepeatedEdges(const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices, + std::list<std::pair<size_t, size_t> > &edge_pos) { + std::map<V2, size_t> edges; + for (size_t i = 0; i < vertices.size() - 1; ++i) { + edges[std::make_pair(vertices[i], vertices[i+1])] = i; + } + edges[std::make_pair(vertices[vertices.size()-1], vertices[0])] = vertices.size() - 1; + + for (std::map<V2, size_t>::iterator i = edges.begin(); i != edges.end(); ++i) { + V2 rev = V2((*i).first.second, (*i).first.first); + std::map<V2, size_t>::iterator j = edges.find(rev); + if (j != edges.end()) { + edge_pos.push_back(std::make_pair((*i).second, (*j).second)); + } + } + return edge_pos.size() > 0; + } + + void flood(size_t t1, + size_t t2, + size_t old_grp, + size_t new_grp_1, + size_t new_grp_2, + std::vector<size_t> &grp, + const std::vector<triangulate::tri_idx> &tris, + const std::map<std::pair<size_t, size_t>, size_t> &tri_edge) { + grp[t1] = new_grp_1; + grp[t2] = new_grp_2; + + std::deque<size_t> to_visit; + to_visit.push_back(t1); + to_visit.push_back(t2); + std::vector<std::pair<size_t, size_t> > rev; + rev.resize(3); + while (to_visit.size()) { + size_t curr = to_visit.front(); + to_visit.pop_front(); + triangulate::tri_idx ct = tris[curr]; + rev[0] = std::make_pair(ct.b, ct.a); + rev[1] = std::make_pair(ct.c, ct.b); + rev[2] = std::make_pair(ct.a, ct.c); + + for (size_t i = 0; i < 3; ++i) { + std::map<std::pair<size_t, size_t>, size_t>::const_iterator adj = tri_edge.find(rev[i]); + if (adj == tri_edge.end()) continue; + size_t next = (*adj).second; + if (grp[next] != old_grp) continue; + grp[next] = grp[curr]; + to_visit.push_back(next); + } + } + } + + void findPerimeter(const std::vector<triangulate::tri_idx> &tris, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &verts, + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &out) { + std::map<std::pair<size_t, size_t>, size_t> edges; + for (size_t i = 0; i < tris.size(); ++i) { + edges[std::make_pair(tris[i].a, tris[i].b)] = i; + edges[std::make_pair(tris[i].b, tris[i].c)] = i; + edges[std::make_pair(tris[i].c, tris[i].a)] = i; + } + std::map<size_t, size_t> unpaired; + for (std::map<std::pair<size_t, size_t>, size_t>::iterator i = edges.begin(); i != edges.end(); ++i) { + if (edges.find(std::make_pair((*i).first.second, (*i).first.first)) == edges.end()) { + CARVE_ASSERT(unpaired.find((*i).first.first) == unpaired.end()); + unpaired[(*i).first.first] = (*i).first.second; + } + } + out.clear(); + out.reserve(unpaired.size()); + size_t start = (*unpaired.begin()).first; + size_t vert = start; + do { + out.push_back(verts[vert]); + CARVE_ASSERT(unpaired.find(vert) != unpaired.end()); + vert = unpaired[vert]; + } while (vert != start); + } + + virtual void processOutputFace(std::vector<carve::mesh::MeshSet<3>::face_t *> &faces, + const carve::mesh::MeshSet<3>::face_t *orig, + bool flipped) { + std::vector<carve::mesh::MeshSet<3>::face_t *> out_faces; + + for (size_t f = 0; f < faces.size(); ++f) { + carve::mesh::MeshSet<3>::face_t *face = faces[f]; + + if (face->nVertices() == 3) { + out_faces.push_back(face); + continue; + } + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> vloop; + face->getVertices(vloop); + + std::list<std::pair<size_t, size_t> > rep_edges; + if (!findRepeatedEdges(vloop, rep_edges)) { + out_faces.push_back(face); + continue; + } + + std::vector<triangulate::tri_idx> result; + triangulate::triangulate( + carve::mesh::MeshSet<3>::face_t::projection_mapping(face->project), + vloop, + result); + + std::map<std::pair<size_t, size_t>, size_t> tri_edge; + for (size_t i = 0; i < result.size(); ++i) { + tri_edge[std::make_pair(result[i].a, result[i].b)] = i; + tri_edge[std::make_pair(result[i].b, result[i].c)] = i; + tri_edge[std::make_pair(result[i].c, result[i].a)] = i; + } + + std::vector<size_t> grp; + grp.resize(result.size(), 0); + + size_t grp_max = 0; + + while (rep_edges.size()) { + std::pair<size_t, size_t> e1, e2; + + e1.first = rep_edges.front().first; + e1.second = (e1.first + 1) % vloop.size(); + + e2.first = rep_edges.front().second; + e2.second = (e2.first + 1) % vloop.size(); + + rep_edges.pop_front(); + + CARVE_ASSERT(tri_edge.find(e1) != tri_edge.end()); + size_t t1 = tri_edge[e1]; + CARVE_ASSERT(tri_edge.find(e2) != tri_edge.end()); + size_t t2 = tri_edge[e2]; + + if (grp[t1] != grp[t2]) { + continue; + } + + size_t t1g = ++grp_max; + size_t t2g = ++grp_max; + + flood(t1, t2, grp[t1], t1g, t2g, grp, result, tri_edge); + } + + std::set<size_t> groups; + std::copy(grp.begin(), grp.end(), std::inserter(groups, groups.begin())); + + // now construct perimeters for each group. + std::vector<triangulate::tri_idx> grp_tris; + grp_tris.reserve(result.size()); + for (std::set<size_t>::iterator i = groups.begin(); i != groups.end(); ++i) { + size_t grp_id = *i; + grp_tris.clear(); + for (size_t j = 0; j < grp.size(); ++j) { + if (grp[j] == grp_id) { + grp_tris.push_back(result[j]); + } + } + std::vector<carve::mesh::MeshSet<3>::vertex_t *> grp_perim; + findPerimeter(grp_tris, vloop, grp_perim); + out_faces.push_back(face->create(grp_perim.begin(), grp_perim.end(), false)); + } + } + std::swap(faces, out_faces); + } + }; + } +} diff --git a/extern/carve/include/carve/debug_hooks.hpp b/extern/carve/include/carve/debug_hooks.hpp new file mode 100644 index 00000000000..9ef7fc83573 --- /dev/null +++ b/extern/carve/include/carve/debug_hooks.hpp @@ -0,0 +1,97 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/vector.hpp> +#include <carve/geom3d.hpp> +#include <carve/csg.hpp> + +#include <iomanip> + +template<typename MAP> +void map_histogram(std::ostream &out, const MAP &map) { + std::vector<int> hist; + for (typename MAP::const_iterator i = map.begin(); i != map.end(); ++i) { + size_t n = (*i).second.size(); + if (hist.size() <= n) { + hist.resize(n + 1); + } + hist[n]++; + } + int total = map.size(); + std::string bar(50, '*'); + for (size_t i = 0; i < hist.size(); i++) { + if (hist[i] > 0) { + out << std::setw(5) << i << " : " << std::setw(5) << hist[i] << " " << bar.substr(50 - hist[i] * 50 / total) << std::endl; + } + } +} + +namespace carve { + namespace csg { + class IntersectDebugHooks { + public: + virtual void drawIntersections(const VertexIntersections & /* vint */) { + } + + virtual void drawPoint(const carve::mesh::MeshSet<3>::vertex_t * /* v */, + float /* r */, + float /* g */, + float /* b */, + float /* a */, + float /* rad */) { + } + virtual void drawEdge(const carve::mesh::MeshSet<3>::vertex_t * /* v1 */, + const carve::mesh::MeshSet<3>::vertex_t * /* v2 */, + float /* rA */, float /* gA */, float /* bA */, float /* aA */, + float /* rB */, float /* gB */, float /* bB */, float /* aB */, + float /* thickness */ = 1.0) { + } + + virtual void drawFaceLoopWireframe(const std::vector<carve::mesh::MeshSet<3>::vertex_t *> & /* face_loop */, + const carve::mesh::MeshSet<3>::vertex_t & /* normal */, + float /* r */, float /* g */, float /* b */, float /* a */, + bool /* inset */ = true) { + } + + virtual void drawFaceLoop(const std::vector<carve::mesh::MeshSet<3>::vertex_t *> & /* face_loop */, + const carve::mesh::MeshSet<3>::vertex_t & /* normal */, + float /* r */, float /* g */, float /* b */, float /* a */, + bool /* offset */ = true, + bool /* lit */ = true) { + } + + virtual void drawFaceLoop2(const std::vector<carve::mesh::MeshSet<3>::vertex_t *> & /* face_loop */, + const carve::mesh::MeshSet<3>::vertex_t & /* normal */, + float /* rF */, float /* gF */, float /* bF */, float /* aF */, + float /* rB */, float /* gB */, float /* bB */, float /* aB */, + bool /* offset */ = true, + bool /* lit */ = true) { + } + + virtual ~IntersectDebugHooks() { + } + }; + + IntersectDebugHooks *intersect_installDebugHooks(IntersectDebugHooks *hooks); + bool intersect_debugEnabled(); + + } +} diff --git a/extern/carve/include/carve/djset.hpp b/extern/carve/include/carve/djset.hpp new file mode 100644 index 00000000000..542858d59f4 --- /dev/null +++ b/extern/carve/include/carve/djset.hpp @@ -0,0 +1,134 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> +#include <vector> + + + +namespace carve { +namespace djset { + + + + class djset { + + protected: + struct elem { + size_t parent, rank; + elem(size_t p, size_t r) : parent(p), rank(r) {} + elem() {} + }; + + std::vector<elem> set; + size_t n_sets; + + public: + djset() : set(), n_sets(0) { + } + + djset(size_t N) { + n_sets = N; + set.reserve(N); + for (size_t i = 0; i < N; ++i) { + set.push_back(elem(i,0)); + } + } + + void init(size_t N) { + if (N == set.size()) { + for (size_t i = 0; i < N; ++i) { + set[i] = elem(i,0); + } + n_sets = N; + } else { + djset temp(N); + std::swap(set, temp.set); + std::swap(n_sets, temp.n_sets); + } + } + + size_t count() const { + return n_sets; + } + + size_t find_set_head(size_t a) { + if (a == set[a].parent) return a; + + size_t a_head = a; + while (set[a_head].parent != a_head) a_head = set[a_head].parent; + set[a].parent = a_head; + return a_head; + } + + bool same_set(size_t a, size_t b) { + return find_set_head(a) == find_set_head(b); + } + + void merge_sets(size_t a, size_t b) { + a = find_set_head(a); + b = find_set_head(b); + if (a != b) { + n_sets--; + if (set[a].rank < set[b].rank) { + set[a].parent = b; + } else if (set[b].rank < set[a].rank) { + set[b].parent = a; + } else { + set[a].rank++; + set[b].parent = a; + } + } + } + + void get_index_to_set(std::vector<size_t> &index_set, std::vector<size_t> &set_size) { + index_set.clear(); + index_set.resize(set.size(), n_sets); + set_size.clear(); + set_size.resize(n_sets, 0); + + size_t c = 0; + for (size_t i = 0; i < set.size(); ++i) { + size_t s = find_set_head(i); + if (index_set[s] == n_sets) index_set[s] = c++; + index_set[i] = index_set[s]; + set_size[index_set[s]]++; + } + } + + template<typename in_iter_t, typename out_collection_t> + void collate(in_iter_t in, out_collection_t &out) { + std::vector<size_t> set_id(set.size(), n_sets); + out.clear(); + out.resize(n_sets); + size_t c = 0; + for (size_t i = 0; i < set.size(); ++i) { + size_t s = find_set_head(i); + if (set_id[s] == n_sets) set_id[s] = c++; + s = set_id[s]; + std::insert_iterator<typename out_collection_t::value_type> j(out[s], out[s].end()); + *j = *in++; + } + } + }; + + + +} +} diff --git a/extern/carve/include/carve/edge_decl.hpp b/extern/carve/include/carve/edge_decl.hpp new file mode 100644 index 00000000000..cafef5de7b1 --- /dev/null +++ b/extern/carve/include/carve/edge_decl.hpp @@ -0,0 +1,68 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/vector.hpp> +#include <carve/tag.hpp> + +#include <vector> +#include <list> + +namespace carve { + namespace poly { + + + + struct Object; + + + + template<unsigned ndim> + class Edge : public tagable { + public: + typedef Vertex<ndim> vertex_t; + typedef typename Vertex<ndim>::vector_t vector_t; + typedef Object obj_t; + + const vertex_t *v1, *v2; + const obj_t *owner; + + Edge(const vertex_t *_v1, const vertex_t *_v2, const obj_t *_owner) : + tagable(), v1(_v1), v2(_v2), owner(_owner) { + } + + ~Edge() { + } + }; + + + + struct hash_edge_ptr { + template<unsigned ndim> + size_t operator()(const Edge<ndim> * const &e) const { + return (size_t)e; + } + }; + + + + } +} + diff --git a/extern/carve/include/carve/edge_impl.hpp b/extern/carve/include/carve/edge_impl.hpp new file mode 100644 index 00000000000..504972c7de0 --- /dev/null +++ b/extern/carve/include/carve/edge_impl.hpp @@ -0,0 +1,23 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +namespace carve { + namespace poly { + } +} diff --git a/extern/carve/include/carve/exact.hpp b/extern/carve/include/carve/exact.hpp new file mode 100644 index 00000000000..afb491211fd --- /dev/null +++ b/extern/carve/include/carve/exact.hpp @@ -0,0 +1,702 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <vector> +#include <numeric> +#include <algorithm> + + +namespace carve { + namespace exact { + + class exact_t : public std::vector<double> { + typedef std::vector<double> super; + + public: + exact_t() : super() { + } + + exact_t(double v, size_t sz = 1) : super(sz, v) { + } + + template<typename iter_t> + exact_t(iter_t a, iter_t b) : super(a, b) { + } + + exact_t(double a, double b) : super() { + reserve(2); + push_back(a); + push_back(b); + } + + exact_t(double a, double b, double c) : super() { + reserve(3); + push_back(a); + push_back(b); + push_back(c); + } + + exact_t(double a, double b, double c, double d) : super() { + reserve(4); + push_back(a); + push_back(b); + push_back(c); + push_back(d); + } + + exact_t(double a, double b, double c, double d, double e) : super() { + reserve(5); + push_back(a); + push_back(b); + push_back(c); + push_back(d); + push_back(e); + } + + exact_t(double a, double b, double c, double d, double e, double f) : super() { + reserve(6); + push_back(a); + push_back(b); + push_back(c); + push_back(d); + push_back(e); + push_back(f); + } + + exact_t(double a, double b, double c, double d, double e, double f, double g) : super() { + reserve(7); + push_back(a); + push_back(b); + push_back(c); + push_back(d); + push_back(e); + push_back(f); + push_back(g); + } + + exact_t(double a, double b, double c, double d, double e, double f, double g, double h) : super() { + reserve(8); + push_back(a); + push_back(b); + push_back(c); + push_back(d); + push_back(e); + push_back(f); + push_back(g); + push_back(h); + } + + void compress(); + + exact_t compressed() const { + exact_t result(*this); + result.compress(); + return result; + } + + operator double() const { + return std::accumulate(begin(), end(), 0.0); + } + + void removeZeroes() { + erase(std::remove(begin(), end(), 0.0), end()); + } + }; + + inline std::ostream &operator<<(std::ostream &out, const exact_t &p) { + out << '{'; + out << p[0]; + for (size_t i = 1; i < p.size(); ++i) out << ';' << p[i]; + out << '}'; + return out; + } + + + + namespace detail { + const struct constants_t { + double splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */ + double epsilon; /* = 2^(-p). Used to estimate roundoff errors. */ + /* A set of coefficients used to calculate maximum roundoff errors. */ + double resulterrbound; + double ccwerrboundA, ccwerrboundB, ccwerrboundC; + double o3derrboundA, o3derrboundB, o3derrboundC; + double iccerrboundA, iccerrboundB, iccerrboundC; + double isperrboundA, isperrboundB, isperrboundC; + + constants_t() { + double half; + double check, lastcheck; + int every_other; + + every_other = 1; + half = 0.5; + epsilon = 1.0; + splitter = 1.0; + check = 1.0; + /* Repeatedly divide `epsilon' by two until it is too small to add to */ + /* one without causing roundoff. (Also check if the sum is equal to */ + /* the previous sum, for machines that round up instead of using exact */ + /* rounding. Not that this library will work on such machines anyway. */ + do { + lastcheck = check; + epsilon *= half; + if (every_other) { + splitter *= 2.0; + } + every_other = !every_other; + check = 1.0 + epsilon; + } while ((check != 1.0) && (check != lastcheck)); + splitter += 1.0; + + /* Error bounds for orientation and incircle tests. */ + resulterrbound = (3.0 + 8.0 * epsilon) * epsilon; + ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon; + ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon; + ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon; + o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon; + o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon; + o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon; + iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; + iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon; + iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon; + isperrboundA = (16.0 + 224.0 * epsilon) * epsilon; + isperrboundB = (5.0 + 72.0 * epsilon) * epsilon; + isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon; + } + } constants; + + template<unsigned U, unsigned V> + struct op { + enum { + Vlo = V / 2, + Vhi = V - Vlo + }; + + static inline void add(const double *a, const double *b, double *r) { + double t[U + Vlo]; + op<U, Vlo>::add(a, b, t); + for (size_t i = 0; i < Vlo; ++i) r[i] = t[i]; + op<U, Vhi>::add(t + Vlo, b + Vlo, r + Vlo); + } + + static inline void sub(const double *a, const double *b, double *r) { + double t[U + Vlo]; + op<U, Vlo>::sub(a, b, t); + for (size_t i = 0; i < Vlo; ++i) r[i] = t[i]; + op<U, Vhi>::sub(t + Vlo, b + Vlo, r + Vlo); + } + }; + + template<unsigned U> + struct op<U, 1> { + enum { + Ulo = U / 2, + Uhi = U - Ulo + }; + static void add(const double *a, const double *b, double *r) { + double t[Ulo + 1]; + op<Ulo, 1>::add(a, b, t); + for (size_t i = 0; i < Ulo; ++i) r[i] = t[i]; + op<Uhi, 1>::add(a + Ulo, t + Ulo, r + Ulo); + } + + static void sub(const double *a, const double *b, double *r) { + double t[Ulo + 1]; + op<Ulo, 1>::sub(a, b, t); + for (size_t i = 0; i < Ulo; ++i) r[i] = t[i]; + op<Uhi, 1>::add(a + Ulo, t + Ulo, r + Ulo); + } + }; + + template<> + struct op<1, 1> { + static void add_fast(const double *a, const double *b, double *r) { + assert(fabs(a[0]) >= fabs(b[0])); + volatile double sum = a[0] + b[0]; + volatile double bvirt = sum - a[0]; + r[0] = b[0] - bvirt; + r[1] = sum; + } + + static void sub_fast(const double *a, const double *b, double *r) { + assert(fabs(a[0]) >= fabs(b[0])); + volatile double diff = a[0] - b[0]; + volatile double bvirt = a[0] - diff; + r[0] = bvirt - b[0]; + r[1] = diff; + } + + static void add(const double *a, const double *b, double *r) { + volatile double sum = a[0] + b[0]; + volatile double bvirt = sum - a[0]; + double avirt = sum - bvirt; + double bround = b[0] - bvirt; + double around = a[0] - avirt; + r[0] = around + bround; + r[1] = sum; + } + + static void sub(const double *a, const double *b, double *r) { + volatile double diff = a[0] - b[0]; + volatile double bvirt = a[0] - diff; + double avirt = diff + bvirt; + double bround = bvirt - b[0]; + double around = a[0] - avirt; + r[0] = around + bround; + r[1] = diff; + } + }; + + + template<unsigned U, unsigned V> + static exact_t add(const double *a, const double *b) { + exact_t result; + result.resize(U + V); + op<U,V>::add(a, b, &result[0]); + return result; + } + + + template<unsigned U, unsigned V> + static exact_t sub(const double *a, const double *b) { + exact_t result; + result.resize(U + V); + op<U,V>::sub(a, b, &result[0]); + return result; + } + + + template<unsigned U, unsigned V> + static exact_t add(const exact_t &a, const exact_t &b) { + assert(a.size() == U); + assert(b.size() == V); + exact_t result; + result.resize(U + V); + std::fill(result.begin(), result.end(), std::numeric_limits<double>::quiet_NaN()); + op<U,V>::add(&a[0], &b[0], &result[0]); + return result; + } + + + template<unsigned U, unsigned V> + static exact_t add(const exact_t &a, const double *b) { + assert(a.size() == U); + exact_t result; + result.resize(U + V); + std::fill(result.begin(), result.end(), std::numeric_limits<double>::quiet_NaN()); + op<U,V>::add(&a[0], b, &result[0]); + return result; + } + + + template<unsigned U, unsigned V> + static exact_t sub(const exact_t &a, const exact_t &b) { + assert(a.size() == U); + assert(b.size() == V); + exact_t result; + result.resize(U + V); + std::fill(result.begin(), result.end(), std::numeric_limits<double>::quiet_NaN()); + op<U,V>::sub(&a[0], &b[0], &result[0]); + return result; + } + + + template<unsigned U, unsigned V> + static exact_t sub(const exact_t &a, const double *b) { + assert(a.size() == U); + exact_t result; + result.resize(U + V); + std::fill(result.begin(), result.end(), std::numeric_limits<double>::quiet_NaN()); + op<U,V>::sub(&a[0], &b[0], &result[0]); + return result; + } + + + static inline void split(const double a, double *r) { + volatile double c = constants.splitter * a; + volatile double abig = c - a; + r[1] = c - abig; + r[0] = a - r[1]; + } + + static inline void prod_1_1(const double *a, const double *b, double *r) { + r[1] = a[0] * b[0]; + double a_sp[2]; split(a[0], a_sp); + double b_sp[2]; split(b[0], b_sp); + double err1 = r[1] - a_sp[1] * b_sp[1]; + double err2 = err1 - a_sp[0] * b_sp[1]; + double err3 = err2 - a_sp[1] * b_sp[0]; + r[0] = a_sp[0] * b_sp[0] - err3; + } + + static inline void prod_1_1s(const double *a, const double *b, const double *b_sp, double *r) { + r[1] = a[0] * b[0]; + double a_sp[2]; split(a[0], a_sp); + double err1 = r[1] - a_sp[1] * b_sp[1]; + double err2 = err1 - a_sp[0] * b_sp[1]; + double err3 = err2 - a_sp[1] * b_sp[0]; + r[0] = a_sp[0] * b_sp[0] - err3; + } + + static inline void prod_1s_1s(const double *a, const double *a_sp, const double *b, const double *b_sp, double *r) { + r[1] = a[0] * b[0]; + double err1 = r[1] - a_sp[1] * b_sp[1]; + double err2 = err1 - a_sp[0] * b_sp[1]; + double err3 = err2 - a_sp[1] * b_sp[0]; + r[0] = a_sp[0] * b_sp[0] - err3; + } + + static inline void prod_2_1(const double *a, const double *b, double *r) { + double b_sp[2]; split(b[0], b_sp); + double t1[2]; prod_1_1s(a+0, b, b_sp, t1); + r[0] = t1[0]; + double t2[2]; prod_1_1s(a+1, b, b_sp, t2); + double t3[2]; op<1,1>::add(t1+1, t2, t3); + r[1] = t3[0]; + double t4[2]; op<1,1>::add_fast(t2+1, t3+1, r + 2); + } + + static inline void prod_1_2(const double *a, const double *b, double *r) { + prod_2_1(b, a, r); + } + + static inline void prod_4_1(const double *a, const double *b, double *r) { + double b_sp[2]; split(b[0], b_sp); + double t1[2]; prod_1_1s(a+0, b, b_sp, t1); + r[0] = t1[0]; + double t2[2]; prod_1_1s(a+1, b, b_sp, t2); + double t3[2]; op<1,1>::add(t1+1, t2, t3); + r[1] = t3[0]; + double t4[2]; op<1,1>::add_fast(t2+1, t3+1, t4); + r[2] = t4[0]; + double t5[2]; prod_1_1s(a+2, b, b_sp, t5); + double t6[2]; op<1,1>::add(t4+1, t5, t6); + r[3] = t6[0]; + double t7[2]; op<1,1>::add_fast(t5+1, t6+1, t7); + r[4] = t7[0]; + double t8[2]; prod_1_1s(a+3, b, b_sp, t8); + double t9[2]; op<1,1>::add(t7+1, t8, t9); + r[5] = t9[0]; + op<1,1>::add_fast(t8+1, t9+1, r + 6); + } + + static inline void prod_1_4(const double *a, const double *b, double *r) { + prod_4_1(b, a, r); + } + + static inline void prod_2_2(const double *a, const double *b, double *r) { + double a1_sp[2]; split(a[1], a1_sp); + double a0_sp[2]; split(a[0], a0_sp); + double b1_sp[2]; split(b[1], b1_sp); + double b0_sp[2]; split(b[0], b0_sp); + + double t1[2]; prod_1s_1s(a+0, a0_sp, b+0, b0_sp, t1); + r[0] = t1[0]; + double t2[2]; prod_1s_1s(a+1, a1_sp, b+0, b0_sp, t2); + + double t3[2]; op<1,1>::add(t1+1, t2, t3); + double t4[2]; op<1,1>::add_fast(t2+1, t3+1, t4); + + double t5[2]; prod_1s_1s(a+0, a0_sp, b+1, b1_sp, t5); + + double t6[2]; op<1,1>::add(t3, t5, t6); + r[1] = t6[0]; + double t7[2]; op<1,1>::add(t4, t6+1, t7); + double t8[2]; op<1,1>::add(t4+1, t7+1, t8); + + double t9[2]; prod_1s_1s(a+1, a1_sp, b+1, b1_sp, t9); + + double t10[2]; op<1,1>::add(t5+1, t9, t10); + double t11[2]; op<1,1>::add(t7, t10, t11); + r[2] = t11[0]; + double t12[2]; op<1,1>::add(t8, t11+1, t12); + double t13[2]; op<1,1>::add(t8+1, t12+1, t13); + double t14[2]; op<1,1>::add(t9+1, t10+1, t14); + double t15[2]; op<1,1>::add(t12, t14, t15); + r[3] = t15[0]; + double t16[2]; op<1,1>::add(t13, t15+1, t16); + double t17[2]; op<1,1>::add(t13+1, t16+1, t17); + double t18[2]; op<1,1>::add(t16, t14+1, t18); + r[4] = t18[0]; + double t19[2]; op<1,1>::add(t17, t18+1, t19); + r[5] = t19[0]; + double t20[2]; op<1,1>::add(t17+1, t19+1, t20); + r[6] = t20[0]; + r[7] = t20[1]; + } + + + + static inline void square(const double a, double *r) { + r[1] = a * a; + double a_sp[2]; split(a, a_sp); + double err1 = r[1] - (a_sp[1] * a_sp[1]); + double err3 = err1 - ((a_sp[1] + a_sp[1]) * a_sp[0]); + r[0] = a_sp[0] * a_sp[0] - err3; + } + + static inline void square_2(const double *a, double *r) { + double t1[2]; square(a[0], t1); + r[0] = t1[0]; + double t2 = a[0] + a[0]; + double t3[2]; prod_1_1(a+1, &t2, t3); + double t4[3]; op<2,1>::add(t3, t1 + 1, t4); + r[1] = t4[0]; + double t5[2]; square(a[1], t5); + double t6[4]; op<2,2>::add(t5, t4 + 1, r + 2); + } + } + + + + void exact_t::compress() { + double sum[2]; + + int j = size() - 1; + double Q = (*this)[j]; + for (int i = (int)size()-2; i >= 0; --i) { + detail::op<1,1>::add_fast(&Q, &(*this)[i], sum); + if (sum[0] != 0) { + (*this)[j--] = sum[1]; + Q = sum[0]; + } else { + Q = sum[1]; + } + } + int j2 = 0; + for (int i = j + 1; i < (int)size(); ++i) { + detail::op<1,1>::add_fast(&(*this)[i], &Q, sum); + if (sum[0] != 0) { + (*this)[j2++] = sum[0]; + } + Q = sum[1]; + } + (*this)[j2++] = Q; + + erase(begin() + j2, end()); + } + + template<typename iter_t> + void negate(iter_t begin, iter_t end) { + while (begin != end) { *begin = -*begin; ++begin; } + } + + void negate(exact_t &e) { + negate(&e[0], &e[e.size()]); + } + + template<typename iter_t> + void scale_zeroelim(iter_t ebegin, + iter_t eend, + double b, + exact_t &h) { + double Q; + + h.clear(); + double b_sp[2]; detail::split(b, b_sp); + + double prod[2], sum[2]; + + detail::prod_1_1s((double *)ebegin++, &b, b_sp, prod); + Q = prod[1]; + if (prod[0] != 0.0) { + h.push_back(prod[0]); + } + while (ebegin != eend) { + double enow = *ebegin++; + detail::prod_1_1s(&enow, &b, b_sp, prod); + detail::op<1,1>::add(&Q, prod, sum); + if (sum[0] != 0) { + h.push_back(sum[0]); + } + detail::op<1,1>::add_fast(prod+1, sum+1, sum); + Q = sum[1]; + if (sum[0] != 0) { + h.push_back(sum[0]); + } + } + if ((Q != 0.0) || (h.size() == 0)) { + h.push_back(Q); + } + } + + void scale_zeroelim(const exact_t &e, + double b, + exact_t &h) { + scale_zeroelim(&e[0], &e[e.size()], b, h); + } + + template<typename iter_t> + void sum_zeroelim(iter_t ebegin, + iter_t eend, + iter_t fbegin, + iter_t fend, + exact_t &h) { + double Q; + double enow, fnow; + + double sum[2]; + + enow = *ebegin; + fnow = *fbegin; + + h.clear(); + + if ((fnow > enow) == (fnow > -enow)) { + Q = enow; + enow = *++ebegin; + } else { + Q = fnow; + fnow = *++fbegin; + } + + if (ebegin != eend && fbegin != fend) { + if ((fnow > enow) == (fnow > -enow)) { + detail::op<1,1>::add_fast(&enow, &Q, sum); + enow = *++ebegin; + } else { + detail::op<1,1>::add_fast(&fnow, &Q, sum); + fnow = *++fbegin; + } + Q = sum[1]; + if (sum[0] != 0.0) { + h.push_back(sum[0]); + } + while (ebegin != eend && fbegin != fend) { + if ((fnow > enow) == (fnow > -enow)) { + detail::op<1,1>::add(&Q, &enow, sum); + enow = *++ebegin; + } else { + detail::op<1,1>::add(&Q, &fnow, sum); + fnow = *++fbegin; + } + Q = sum[1]; + if (sum[0] != 0.0) { + h.push_back(sum[0]); + } + } + } + + while (ebegin != eend) { + detail::op<1,1>::add(&Q, &enow, sum); + enow = *++ebegin; + Q = sum[1]; + if (sum[0] != 0.0) { + h.push_back(sum[0]); + } + } + while (fbegin != fend) { + detail::op<1,1>::add(&Q, &fnow, sum); + fnow = *++fbegin; + Q = sum[1]; + if (sum[0] != 0.0) { + h.push_back(sum[0]); + } + } + + if (Q != 0.0 || !h.size()) { + h.push_back(Q); + } + } + + void sum_zeroelim(const exact_t &e, + const exact_t &f, + exact_t &h) { + sum_zeroelim(&e[0], &e[e.size()], &f[0], &f[f.size()], h); + } + + void sum_zeroelim(const double *ebegin, + const double *eend, + const exact_t &f, + exact_t &h) { + sum_zeroelim(ebegin, eend, &f[0], &f[f.size()], h); + } + + void sum_zeroelim(const exact_t &e, + const double *fbegin, + const double *fend, + exact_t &h) { + sum_zeroelim(&e[0], &e[e.size()], fbegin, fend, h); + } + + + // XXX: not implemented yet + //exact_t operator+(const exact_t &a, const exact_t &b) { + //} + + + + void diffprod(const double a, const double b, const double c, const double d, double *r) { + // return ab - cd; + double ab[2], cd[2]; + detail::prod_1_1(&a, &b, ab); + detail::prod_1_1(&c, &d, cd); + detail::op<2,2>::sub(ab, cd, r); + } + + double orient3dexact(const double *pa, + const double *pb, + const double *pc, + const double *pd) { + using namespace detail; + + double ab[4]; diffprod(pa[0], pb[1], pb[0], pa[1], ab); + double bc[4]; diffprod(pb[0], pc[1], pc[0], pb[1], bc); + double cd[4]; diffprod(pc[0], pd[1], pd[0], pc[1], cd); + double da[4]; diffprod(pd[0], pa[1], pa[0], pd[1], da); + double ac[4]; diffprod(pa[0], pc[1], pc[0], pa[1], ac); + double bd[4]; diffprod(pb[0], pd[1], pd[0], pb[1], bd); + + exact_t temp; + exact_t cda, dab, abc, bcd; + exact_t adet, bdet, cdet, ddet, abdet, cddet, det; + + sum_zeroelim(cd, cd + 4, da, da + 4, temp); + sum_zeroelim(temp, ac, ac + 4, cda); + + sum_zeroelim(da, da + 4, ab, ab + 4, temp); + sum_zeroelim(temp, bd, bd + 4, dab); + + negate(bd, bd + 4); + negate(ac, bd + 4); + + sum_zeroelim(ab, ab + 4, bc, bc + 4, temp); + sum_zeroelim(temp, ac, ac + 4, abc); + + sum_zeroelim(bc, bc + 4, cd, cd + 4, temp); + sum_zeroelim(temp, bd, bd + 4, bcd); + + scale_zeroelim(bcd, +pa[2], adet); + scale_zeroelim(cda, -pb[2], bdet); + scale_zeroelim(dab, +pc[2], cdet); + scale_zeroelim(abc, -pd[2], ddet); + + sum_zeroelim(adet, bdet, abdet); + sum_zeroelim(cdet, ddet, cddet); + + sum_zeroelim(abdet, cddet, det); + + return det[det.size() - 1]; + } + + } +} diff --git a/extern/carve/include/carve/face_decl.hpp b/extern/carve/include/carve/face_decl.hpp new file mode 100644 index 00000000000..bc7afd44adc --- /dev/null +++ b/extern/carve/include/carve/face_decl.hpp @@ -0,0 +1,208 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/geom2d.hpp> +#include <carve/vector.hpp> +#include <carve/matrix.hpp> +#include <carve/geom3d.hpp> +#include <carve/aabb.hpp> +#include <carve/tag.hpp> + +#include <vector> +#include <list> +#include <map> + +namespace carve { + namespace poly { + + + + struct Object; + + template<unsigned ndim> + class Edge; + + + + template<unsigned ndim> + struct p2_adapt_project { + typedef carve::geom2d::P2 (*proj_t)(const carve::geom::vector<ndim> &); + proj_t proj; + p2_adapt_project(proj_t _proj) : proj(_proj) { } + carve::geom2d::P2 operator()(const carve::geom::vector<ndim> &v) const { return proj(v); } + carve::geom2d::P2 operator()(const carve::geom::vector<ndim> *v) const { return proj(*v); } + carve::geom2d::P2 operator()(const Vertex<ndim> &v) const { return proj(v.v); } + carve::geom2d::P2 operator()(const Vertex<ndim> *v) const { return proj(v->v); } + }; + + + template<unsigned ndim> + class Face : public tagable { + public: + typedef Vertex<ndim> vertex_t; + typedef typename Vertex<ndim>::vector_t vector_t; + typedef Edge<ndim> edge_t; + typedef Object obj_t; + typedef carve::geom::aabb<ndim> aabb_t; + typedef carve::geom::plane<ndim> plane_t; + + typedef carve::geom2d::P2 (*project_t)(const vector_t &); + typedef vector_t (*unproject_t)(const carve::geom2d::P2 &, const plane_t &); + + protected: + std::vector<const vertex_t *> vertices; // pointer into polyhedron.vertices + std::vector<const edge_t *> edges; // pointer into polyhedron.edges + + project_t getProjector(bool positive_facing, int axis); + unproject_t getUnprojector(bool positive_facing, int axis); + + public: + typedef typename std::vector<const vertex_t *>::iterator vertex_iter_t; + typedef typename std::vector<const vertex_t *>::const_iterator const_vertex_iter_t; + + typedef typename std::vector<const edge_t *>::iterator edge_iter_t; + typedef typename std::vector<const edge_t *>::const_iterator const_edge_iter_t; + + obj_t *owner; + + aabb_t aabb; + plane_t plane_eqn; + int manifold_id; + int group_id; + + project_t project; + unproject_t unproject; + + Face(const std::vector<const vertex_t *> &_vertices, bool delay_recalc = false); + Face(const vertex_t *v1, const vertex_t *v2, const vertex_t *v3, bool delay_recalc = false); + Face(const vertex_t *v1, const vertex_t *v2, const vertex_t *v3, const vertex_t *v4, bool delay_recalc = false); + + template <typename iter_t> + Face(const Face *base, iter_t vbegin, iter_t vend, bool flipped) { + init(base, vbegin, vend, flipped); + } + + Face(const Face *base, const std::vector<const vertex_t *> &_vertices, bool flipped) { + init(base, _vertices, flipped); + } + + Face() {} + ~Face() {} + + bool recalc(); + + template<typename iter_t> + Face *init(const Face *base, iter_t vbegin, iter_t vend, bool flipped); + Face *init(const Face *base, const std::vector<const vertex_t *> &_vertices, bool flipped); + + template<typename iter_t> + Face *create(iter_t vbegin, iter_t vend, bool flipped) const; + Face *create(const std::vector<const vertex_t *> &_vertices, bool flipped) const; + + Face *clone(bool flipped = false) const; + void invert(); + + void getVertexLoop(std::vector<const vertex_t *> &loop) const; + + const vertex_t *&vertex(size_t idx); + const vertex_t *vertex(size_t idx) const; + size_t nVertices() const; + + vertex_iter_t vbegin() { return vertices.begin(); } + vertex_iter_t vend() { return vertices.end(); } + const_vertex_iter_t vbegin() const { return vertices.begin(); } + const_vertex_iter_t vend() const { return vertices.end(); } + + std::vector<carve::geom::vector<2> > projectedVertices() const; + + const edge_t *&edge(size_t idx); + const edge_t *edge(size_t idx) const; + size_t nEdges() const; + + edge_iter_t ebegin() { return edges.begin(); } + edge_iter_t eend() { return edges.end(); } + const_edge_iter_t ebegin() const { return edges.begin(); } + const_edge_iter_t eend() const { return edges.end(); } + + bool containsPoint(const vector_t &p) const; + bool containsPointInProjection(const vector_t &p) const; + bool simpleLineSegmentIntersection(const carve::geom::linesegment<ndim> &line, + vector_t &intersection) const; + IntersectionClass lineSegmentIntersection(const carve::geom::linesegment<ndim> &line, + vector_t &intersection) const; + vector_t centroid() const; + + p2_adapt_project<ndim> projector() const { + return p2_adapt_project<ndim>(project); + } + + void swap(Face<ndim> &other); + }; + + + + struct hash_face_ptr { + template<unsigned ndim> + size_t operator()(const Face<ndim> * const &f) const { + return (size_t)f; + } + }; + + + + namespace face { + + + + template<unsigned ndim> + static inline carve::geom2d::P2 project(const Face<ndim> *f, const typename Face<ndim>::vector_t &v) { + return f->project(v); + } + + + + template<unsigned ndim> + static inline carve::geom2d::P2 project(const Face<ndim> &f, const typename Face<ndim>::vector_t &v) { + return f.project(v); + } + + + + template<unsigned ndim> + static inline typename Face<ndim>::vector_t unproject(const Face<ndim> *f, const carve::geom2d::P2 &p) { + return f->unproject(p, f->plane_eqn); + } + + + + template<unsigned ndim> + static inline typename Face<ndim>::vector_t unproject(const Face<ndim> &f, const carve::geom2d::P2 &p) { + return f.unproject(p, f.plane_eqn); + } + + + + } + + + + } +} diff --git a/extern/carve/include/carve/face_impl.hpp b/extern/carve/include/carve/face_impl.hpp new file mode 100644 index 00000000000..771ba761111 --- /dev/null +++ b/extern/carve/include/carve/face_impl.hpp @@ -0,0 +1,140 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +namespace std { + template<unsigned ndim> + inline void swap(carve::poly::Face<ndim> &a, carve::poly::Face<ndim> &b) { + a.swap(b); + } +} + +namespace carve { + namespace poly { + template<unsigned ndim> + void Face<ndim>::swap(Face<ndim> &other) { + std::swap(vertices, other.vertices); + std::swap(edges, other.edges); + std::swap(owner, other.owner); + std::swap(aabb, other.aabb); + std::swap(plane_eqn, other.plane_eqn); + std::swap(manifold_id, other.manifold_id); + std::swap(group_id, other.group_id); + std::swap(project, other.project); + std::swap(unproject, other.unproject); + } + + template<unsigned ndim> + template<typename iter_t> + Face<ndim> *Face<ndim>::init(const Face<ndim> *base, iter_t vbegin, iter_t vend, bool flipped) { + vertices.reserve(std::distance(vbegin, vend)); + + if (flipped) { + std::reverse_copy(vbegin, vend, std::back_inserter(vertices)); + plane_eqn = -base->plane_eqn; + } else { + std::copy(vbegin, vend, std::back_inserter(vertices)); + plane_eqn = base->plane_eqn; + } + + edges.clear(); + edges.resize(nVertices(), NULL); + + aabb.fit(vertices.begin(), vertices.end(), vec_adapt_vertex_ptr()); + untag(); + + int da = carve::geom::largestAxis(plane_eqn.N); + + project = getProjector(plane_eqn.N.v[da] > 0, da); + unproject = getUnprojector(plane_eqn.N.v[da] > 0, da); + + return this; + } + + template<unsigned ndim> + template<typename iter_t> + Face<ndim> *Face<ndim>::create(iter_t vbegin, iter_t vend, bool flipped) const { + return (new Face)->init(this, vbegin, vend, flipped); + } + + template<unsigned ndim> + Face<ndim> *Face<ndim>::create(const std::vector<const vertex_t *> &_vertices, bool flipped) const { + return (new Face)->init(this, _vertices.begin(), _vertices.end(), flipped); + } + + template<unsigned ndim> + Face<ndim> *Face<ndim>::clone(bool flipped) const { + return (new Face)->init(this, vertices, flipped); + } + + template<unsigned ndim> + void Face<ndim>::getVertexLoop(std::vector<const vertex_t *> &loop) const { + loop.resize(nVertices(), NULL); + std::copy(vbegin(), vend(), loop.begin()); + } + + template<unsigned ndim> + const typename Face<ndim>::edge_t *&Face<ndim>::edge(size_t idx) { + return edges[idx]; + } + + template<unsigned ndim> + const typename Face<ndim>::edge_t *Face<ndim>::edge(size_t idx) const { + return edges[idx]; + } + + template<unsigned ndim> + size_t Face<ndim>::nEdges() const { + return edges.size(); + } + + template<unsigned ndim> + const typename Face<ndim>::vertex_t *&Face<ndim>::vertex(size_t idx) { + return vertices[idx]; + } + + template<unsigned ndim> + const typename Face<ndim>::vertex_t *Face<ndim>::vertex(size_t idx) const { + return vertices[idx]; + } + + template<unsigned ndim> + size_t Face<ndim>::nVertices() const { + return vertices.size(); + } + + template<unsigned ndim> + typename Face<ndim>::vector_t Face<ndim>::centroid() const { + vector_t c; + carve::geom::centroid(vertices.begin(), vertices.end(), vec_adapt_vertex_ptr(), c); + return c; + } + + template<unsigned ndim> + std::vector<carve::geom::vector<2> > Face<ndim>::projectedVertices() const { + p2_adapt_project<ndim> proj = projector(); + std::vector<carve::geom::vector<2> > result; + result.reserve(nVertices()); + for (size_t i = 0; i < nVertices(); ++i) { + result.push_back(proj(vertex(i)->v)); + } + return result; + } + + } +} diff --git a/extern/carve/include/carve/faceloop.hpp b/extern/carve/include/carve/faceloop.hpp new file mode 100644 index 00000000000..5df1d2080f3 --- /dev/null +++ b/extern/carve/include/carve/faceloop.hpp @@ -0,0 +1,103 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> +#include <carve/classification.hpp> +#include <carve/collection_types.hpp> + +namespace carve { + namespace csg { + + struct FaceLoopGroup; + + struct FaceLoop { + FaceLoop *next, *prev; + const carve::mesh::MeshSet<3>::face_t *orig_face; + std::vector<carve::mesh::MeshSet<3>::vertex_t *> vertices; + FaceLoopGroup *group; + + FaceLoop(const carve::mesh::MeshSet<3>::face_t *f, const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &v) : next(NULL), prev(NULL), orig_face(f), vertices(v), group(NULL) {} + }; + + + struct FaceLoopList { + FaceLoop *head, *tail; + unsigned count; + + FaceLoopList() : head(NULL), tail(NULL), count(0) { } + + void append(FaceLoop *f) { + f->prev = tail; + f->next = NULL; + if (tail) tail->next = f; + tail = f; + if (!head) head = f; + count++; + } + + void prepend(FaceLoop *f) { + f->next = head; + f->prev = NULL; + if (head) head->prev = f; + head = f; + if (!tail) tail = f; + count++; + } + + unsigned size() const { + return count; + } + + FaceLoop *remove(FaceLoop *f) { + FaceLoop *r = f->next; + if (f->prev) { f->prev->next = f->next; } else { head = f->next; } + if (f->next) { f->next->prev = f->prev; } else { tail = f->prev; } + f->next = f->prev = NULL; + count--; + return r; + } + + ~FaceLoopList() { + FaceLoop *a = head, *b; + while (a) { + b = a; + a = a->next; + delete b; + } + } + }; + + struct FaceLoopGroup { + const carve::mesh::MeshSet<3> *src; + FaceLoopList face_loops; + V2Set perimeter; + std::list<ClassificationInfo> classification; + + FaceLoopGroup(const carve::mesh::MeshSet<3> *_src) : src(_src) { + } + + FaceClass classificationAgainst(const carve::mesh::MeshSet<3>::mesh_t *mesh) const; + }; + + + + typedef std::list<FaceLoopGroup> FLGroupList; + + } +} diff --git a/extern/carve/include/carve/geom.hpp b/extern/carve/include/carve/geom.hpp new file mode 100644 index 00000000000..421083e3e3c --- /dev/null +++ b/extern/carve/include/carve/geom.hpp @@ -0,0 +1,363 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <vector> + +namespace carve { + namespace geom { + + template<unsigned ndim> struct aabb; + + // ======================================================================== + struct _uninitialized { }; + + template<unsigned ndim> + struct base { + double v[ndim]; + }; + + template<> struct base<2> {union { double v[2]; struct { double x, y; }; }; }; + template<> struct base<3> {union { double v[3]; struct { double x, y, z; }; }; }; + template<> struct base<4> {union { double v[4]; struct { double x, y, z, w; }; }; }; + + template<unsigned ndim> + struct vector : public base<ndim> { + enum { __ndim = ndim }; + + static vector ZERO(); + double length2() const; + double length() const; + vector<ndim> &normalize(); + vector<ndim> normalized() const; + bool exactlyZero() const; + bool isZero(double epsilon = EPSILON) const; + void setZero(); + void fill(double val); + vector<ndim> &scaleBy(double d); + vector<ndim> &invscaleBy(double d); + vector<ndim> scaled(double d) const; + vector<ndim> invscaled(double d) const; + vector<ndim> &negate(); + vector<ndim> negated() const; + double &operator[](unsigned i); + const double &operator[](unsigned i) const; + template<typename assign_t> + vector<ndim> &operator=(const assign_t &t); + std::string asStr() const; + + aabb<ndim> getAABB() const; + + vector() { setZero(); } + vector(noinit_t) { } + }; + + template<unsigned ndim> + vector<ndim> vector<ndim>::ZERO() { vector<ndim> r; r.setZero(); return r; } + + static inline vector<2> VECTOR(double x, double y) { vector<2> r; r.x = x; r.y = y; return r; } + static inline vector<3> VECTOR(double x, double y, double z) { vector<3> r; r.x = x; r.y = y; r.z = z; return r; } + static inline vector<4> VECTOR(double x, double y, double z, double w) { vector<4> r; r.x = x; r.y = y; r.z = z; r.w = w; return r; } + + template<unsigned ndim> vector<ndim> operator+(const vector<ndim> &a, const vector<ndim> &b); + template<unsigned ndim> vector<ndim> operator+(const vector<ndim> &a, double b); + template<unsigned ndim, typename val_t> vector<ndim> operator+(const vector<ndim> &a, const val_t &b); + template<unsigned ndim, typename val_t> vector<ndim> operator+(const val_t &a, const vector<ndim> &b); + + template<unsigned ndim> vector<ndim> &operator+=(vector<ndim> &a, const vector<ndim> &b); + template<unsigned ndim> vector<ndim> &operator+=(vector<ndim> &a, double b); + template<unsigned ndim, typename val_t> vector<ndim> &operator+=(vector<ndim> &a, const val_t &b); + + template<unsigned ndim> vector<ndim> operator-(const vector<ndim> &a); + + template<unsigned ndim> vector<ndim> operator-(const vector<ndim> &a, const vector<ndim> &b); + template<unsigned ndim> vector<ndim> operator-(const vector<ndim> &a, double b); + template<unsigned ndim, typename val_t> vector<ndim> operator-(const vector<ndim> &a, const val_t &b); + template<unsigned ndim, typename val_t> vector<ndim> operator-(const val_t &a, const vector<ndim> &b); + + template<unsigned ndim> vector<ndim> &operator-=(vector<ndim> &a, const vector<ndim> &b); + template<unsigned ndim> vector<ndim> &operator-=(vector<ndim> &a, double b); + template<unsigned ndim, typename val_t> vector<ndim> &operator-=(vector<ndim> &a, const val_t &b); + + template<unsigned ndim> vector<ndim> operator*(const vector<ndim> &a, double s); + template<unsigned ndim> vector<ndim> operator*(double s, const vector<ndim> &a); + template<unsigned ndim> vector<ndim> &operator*=(vector<ndim> &a, double s); + + template<unsigned ndim> vector<ndim> operator/(const vector<ndim> &a, double s); + template<unsigned ndim> vector<ndim> &operator/=(vector<ndim> &a, double s); + + template<unsigned ndim> bool operator==(const vector<ndim> &a, const vector<ndim> &b); + template<unsigned ndim> bool operator!=(const vector<ndim> &a, const vector<ndim> &b); + template<unsigned ndim> bool operator<(const vector<ndim> &a, const vector<ndim> &b); + template<unsigned ndim> bool operator<=(const vector<ndim> &a, const vector<ndim> &b); + template<unsigned ndim> bool operator>(const vector<ndim> &a, const vector<ndim> &b); + template<unsigned ndim> bool operator>=(const vector<ndim> &a, const vector<ndim> &b); + + template<unsigned ndim> vector<ndim> abs(const vector<ndim> &a); + + template<unsigned ndim> double distance2(const vector<ndim> &a, const vector<ndim> &b); + + template<unsigned ndim> double distance(const vector<ndim> &a, const vector<ndim> &b); + + template<unsigned ndim> bool equal(const vector<ndim> &a, const vector<ndim> &b); + + template<unsigned ndim> int smallestAxis(const vector<ndim> &a); + + template<unsigned ndim> int largestAxis(const vector<ndim> &a); + + template<unsigned ndim> vector<2> select(const vector<ndim> &a, int a1, int a2); + + template<unsigned ndim> vector<3> select(const vector<ndim> &a, int a1, int a2, int a3); + + template<unsigned ndim, typename assign_t, typename oper_t> + vector<ndim> &assign_op(vector<ndim> &a, const assign_t &t, oper_t op); + + template<unsigned ndim, typename assign1_t, typename assign2_t, typename oper_t> + vector<ndim> &assign_op(vector<ndim> &a, const assign1_t &t1, const assign2_t &t2, oper_t op); + + template<unsigned ndim, typename iter_t> + void bounds(iter_t begin, iter_t end, vector<ndim> &min, vector<ndim> &max); + + template<unsigned ndim, typename iter_t, typename adapt_t> + void bounds(iter_t begin, iter_t end, adapt_t adapt, vector<ndim> &min, vector<ndim> &max); + + template<unsigned ndim, typename iter_t, typename adapt_t> + void centroid(iter_t begin, iter_t end, adapt_t adapt, vector<ndim> &c); + + template<unsigned ndim, typename val_t> double dot(const vector<ndim> &a, const val_t &b); + + static inline vector<3> cross(const vector<3> &a, const vector<3> &b); + + static inline double cross(const vector<2> &a, const vector<2> &b); + + static inline double dotcross(const vector<3> &a, const vector<3> &b, const vector<3> &c); + + + + // ======================================================================== + struct axis_pos { + int axis; + double pos; + + axis_pos(int _axis, double _pos) : axis(_axis), pos(_pos) { } + }; + + template<unsigned ndim> + double distance(const axis_pos &a, const vector<ndim> &b); + + template<unsigned ndim> + double distance2(const axis_pos &a, const vector<ndim> &b); + + template<unsigned ndim> bool operator<(const axis_pos &a, const vector<ndim> &b); + template<unsigned ndim> bool operator<(const vector<ndim> &a, const axis_pos &b); + + template<unsigned ndim> bool operator<=(const axis_pos &a, const vector<ndim> &b); + template<unsigned ndim> bool operator<=(const vector<ndim> &a, const axis_pos &b); + + template<unsigned ndim> bool operator>(const axis_pos &a, const vector<ndim> &b); + template<unsigned ndim> bool operator>(const vector<ndim> &a, const axis_pos &b); + + template<unsigned ndim> bool operator>=(const axis_pos &a, const vector<ndim> &b); + template<unsigned ndim> bool operator>=(const vector<ndim> &a, const axis_pos &b); + + template<unsigned ndim> bool operator==(const axis_pos &a, const vector<ndim> &b); + template<unsigned ndim> bool operator==(const vector<ndim> &a, const axis_pos &b); + + template<unsigned ndim> bool operator!=(const axis_pos &a, const vector<ndim> &b); + template<unsigned ndim> bool operator!=(const vector<ndim> &a, const axis_pos &b); + + + + // ======================================================================== + template<unsigned ndim> + struct ray { + typedef vector<ndim> vector_t; + + vector_t D, v; + + bool OK() const; + + ray() { } + ray(vector_t _D, vector_t _v) : D(_D), v(_v) { } + }; + + template<unsigned ndim> + ray<ndim> rayThrough(const vector<ndim> &a, const vector<ndim> &b); + + static inline double distance2(const ray<3> &r, const vector<3> &v); + + static inline double distance(const ray<3> &r, const vector<3> &v); + + static inline double distance2(const ray<2> &r, const vector<2> &v); + + static inline double distance(const ray<2> &r, const vector<2> &v); + + + + // ======================================================================== + template<unsigned ndim> + struct linesegment { + typedef vector<ndim> vector_t; + + vector_t v1; + vector_t v2; + vector_t midpoint; + vector_t half_length; + + void update(); + bool OK() const; + void flip(); + + aabb<ndim> getAABB() const; + + linesegment(const vector_t &_v1, const vector_t &_v2); + }; + + template<unsigned ndim> + double distance2(const linesegment<ndim> &l, const vector<ndim> &v); + + template<unsigned ndim> + double distance(const linesegment<ndim> &l, const vector<ndim> &v); + + + + // ======================================================================== + template<unsigned ndim> + struct plane { + typedef vector<ndim> vector_t; + + vector_t N; + double d; + + void negate(); + + plane(); + plane(const vector_t &_N, vector_t _p); + plane(const vector_t &_N, double _d); + }; + + template<unsigned ndim> + inline plane<ndim> operator-(const plane<ndim> &p); + + template<unsigned ndim, typename val_t> + double distance(const plane<ndim> &plane, const val_t &point); + + template<unsigned ndim, typename val_t> + double distance2(const plane<ndim> &plane, const val_t &point); + + template<unsigned ndim> + static inline vector<ndim> closestPoint(const plane<ndim> &p, const vector<ndim> &v); + + + + // ======================================================================== + template<unsigned ndim> + struct sphere { + typedef vector<ndim> vector_t; + + vector_t C; + double r; + + aabb<ndim> getAABB() const; + + sphere(); + sphere(const vector_t &_C, double _r); + }; + + template<unsigned ndim, typename val_t> + double distance(const sphere<ndim> &sphere, const val_t &point); + + template<unsigned ndim, typename val_t> + double distance2(const sphere<ndim> &sphere, const val_t &point); + + template<unsigned ndim> + static inline vector<ndim> closestPoint(const sphere<ndim> &sphere, const vector<ndim> &point); + + + // ======================================================================== + template<unsigned ndim> + struct tri { + typedef vector<ndim> vector_t; + + vector_t v[3]; + + aabb<ndim> getAABB() const; + + tri(vector_t _v[3]); + tri(const vector_t &a, const vector_t &b, const vector_t &c); + + vector_t normal() const { + return cross(v[1] - v[0], v[2] - v[1]).normalized(); + } + }; + + + + template<unsigned ndim> std::ostream &operator<<(std::ostream &o, const vector<ndim> &v); + template<unsigned ndim> std::ostream &operator<<(std::ostream &o, const carve::geom::plane<ndim> &p); + template<unsigned ndim> std::ostream &operator<<(std::ostream &o, const carve::geom::sphere<ndim> &sphere); + template<unsigned ndim> std::ostream &operator<<(std::ostream &o, const carve::geom::tri<ndim> &tri); + + + + template<unsigned ndim> vector<ndim> closestPoint(const tri<ndim> &tri, const vector<ndim> &pt); + template<unsigned ndim> double distance(const tri<ndim> &tri, const vector<ndim> &pt); + template<unsigned ndim> double distance2(const tri<ndim> &tri, const vector<ndim> &pt); + + + + // ======================================================================== + struct distance_functor { + template<typename obj1_t, typename obj2_t> + double operator()(const obj1_t &o1, const obj2_t &o2) { + return distance(o1, o2); + } + }; + + + + // ======================================================================== + template<int base, int power> struct __pow__ { enum { val = __pow__<base, (power >> 1)>::val * __pow__<base, power - (power >> 1)>::val }; }; + template<int base> struct __pow__<base, 1> { enum { val = base }; }; + template<int base> struct __pow__<base, 0> { enum { val = 1 }; }; + + template<unsigned base, unsigned ndigits> + struct quantize { + typedef __pow__<base, ndigits> fac; + + double operator()(double in) { + return round(in * fac::val) / fac::val; + } + + template<unsigned ndim> + vector<ndim> operator()(const vector<ndim> &in) { + vector<ndim> r(NOINIT); + assign_op(r, in, *this); + return r; + } + }; + + + + } +} + + +#include <carve/geom_impl.hpp> diff --git a/extern/carve/include/carve/geom2d.hpp b/extern/carve/include/carve/geom2d.hpp new file mode 100644 index 00000000000..731e22919b6 --- /dev/null +++ b/extern/carve/include/carve/geom2d.hpp @@ -0,0 +1,403 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/math.hpp> +#include <carve/math_constants.hpp> + +#include <carve/geom.hpp> + +#include <vector> +#include <algorithm> + +#include <math.h> + +#if defined(CARVE_DEBUG) +# include <iostream> +#endif + +#if defined CARVE_USE_EXACT_PREDICATES +# include <carve/shewchuk_predicates.hpp> +#endif + +namespace carve { + namespace geom2d { + + typedef carve::geom::vector<2> P2; + typedef carve::geom::ray<2> Ray2; + typedef carve::geom::linesegment<2> LineSegment2; + + + + struct p2_adapt_ident { + P2 &operator()(P2 &p) const { return p; } + const P2 &operator()(const P2 &p) const { return p; } + }; + + + + typedef std::vector<P2> P2Vector; + + /** + * \brief Return the orientation of c with respect to the ray defined by a->b. + * + * (Can be implemented exactly) + * + * @param[in] a + * @param[in] b + * @param[in] c + * + * @return positive, if c to the left of a->b. + * zero, if c is colinear with a->b. + * negative, if c to the right of a->b. + */ +#if defined CARVE_USE_EXACT_PREDICATES + inline double orient2d(const P2 &a, const P2 &b, const P2 &c) { + return shewchuk::orient2d(a.v, b.v, c.v); + } +#else + inline double orient2d(const P2 &a, const P2 &b, const P2 &c) { + double acx = a.x - c.x; + double bcx = b.x - c.x; + double acy = a.y - c.y; + double bcy = b.y - c.y; + return acx * bcy - acy * bcx; + } +#endif + + /** + * \brief Determine whether p is internal to the anticlockwise + * angle abc, where b is the apex of the angle. + * + * @param[in] a + * @param[in] b + * @param[in] c + * @param[in] p + * + * @return true, if p is contained in the anticlockwise angle from + * b->a to b->c. Reflex angles contain p if p lies + * on b->a or on b->c. Acute angles do not contain p + * if p lies on b->a or on b->c. This is so that + * internalToAngle(a,b,c,p) = !internalToAngle(c,b,a,p) + */ + inline bool internalToAngle(const P2 &a, + const P2 &b, + const P2 &c, + const P2 &p) { + bool reflex = (a < c) ? orient2d(b, a, c) <= 0.0 : orient2d(b, c, a) > 0.0; + double d1 = orient2d(b, a, p); + double d2 = orient2d(b, c, p); + if (reflex) { + return d1 >= 0.0 || d2 <= 0.0; + } else { + return d1 > 0.0 && d2 < 0.0; + } + } + + /** + * \brief Determine whether p is internal to the anticlockwise + * angle ac, with apex at (0,0). + * + * @param[in] a + * @param[in] c + * @param[in] p + * + * @return true, if p is contained in a0c. + */ + inline bool internalToAngle(const P2 &a, + const P2 &c, + const P2 &p) { + return internalToAngle(a, P2::ZERO(), c, p); + } + + template<typename P2vec> + bool isAnticlockwise(const P2vec &tri) { + return orient2d(tri[0], tri[1], tri[2]) > 0.0; + } + + template<typename P2vec> + bool pointIntersectsTriangle(const P2 &p, const P2vec &tri) { + int orient = isAnticlockwise(tri) ? +1 : -1; + if (orient2d(tri[0], tri[1], p) * orient < 0) return false; + if (orient2d(tri[1], tri[2], p) * orient < 0) return false; + if (orient2d(tri[2], tri[0], p) * orient < 0) return false; + return true; + } + + template<typename P2vec> + bool lineIntersectsTriangle(const P2 &p1, const P2 &p2, const P2vec &tri) { + double s[3]; + // does tri lie on one side or the other of p1-p2? + s[0] = orient2d(p1, p2, tri[0]); + s[1] = orient2d(p1, p2, tri[1]); + s[2] = orient2d(p1, p2, tri[2]); + if (*std::max_element(s, s+3) < 0) return false; + if (*std::min_element(s, s+3) > 0) return false; + + // does line lie entirely to the right of a triangle edge? + int orient = isAnticlockwise(tri) ? +1 : -1; + if (orient2d(tri[0], tri[1], p1) * orient < 0 && orient2d(tri[0], tri[1], p2) * orient < 0) return false; + if (orient2d(tri[1], tri[2], p1) * orient < 0 && orient2d(tri[1], tri[2], p2) * orient < 0) return false; + if (orient2d(tri[2], tri[0], p1) * orient < 0 && orient2d(tri[2], tri[0], p2) * orient < 0) return false; + return true; + } + + template<typename P2vec> + int triangleLineOrientation(const P2 &p1, const P2 &p2, const P2vec &tri) { + double lo, hi, tmp; + lo = hi = orient2d(p1, p2, tri[0]); + tmp = orient2d(p1, p2, tri[1]); lo = std::min(lo, tmp); hi = std::max(hi, tmp); + tmp = orient2d(p1, p2, tri[2]); lo = std::min(lo, tmp); hi = std::max(hi, tmp); + if (hi < 0.0) return -1; + if (lo > 0.0) return +1; + return 0; + } + + template<typename P2vec> + bool triangleIntersectsTriangle(const P2vec &tri_b, const P2vec &tri_a) { + int orient_a = isAnticlockwise(tri_a) ? +1 : -1; + if (triangleLineOrientation(tri_a[0], tri_a[1], tri_b) * orient_a < 0) return false; + if (triangleLineOrientation(tri_a[1], tri_a[2], tri_b) * orient_a < 0) return false; + if (triangleLineOrientation(tri_a[2], tri_a[0], tri_b) * orient_a < 0) return false; + + int orient_b = isAnticlockwise(tri_b) ? +1 : -1; + if (triangleLineOrientation(tri_b[0], tri_b[1], tri_a) * orient_b < 0) return false; + if (triangleLineOrientation(tri_b[1], tri_b[2], tri_a) * orient_b < 0) return false; + if (triangleLineOrientation(tri_b[2], tri_b[0], tri_a) * orient_b < 0) return false; + + return true; + } + + + + static inline double atan2(const P2 &p) { + return ::atan2(p.y, p.x); + } + + + + struct LineIntersectionInfo { + LineIntersectionClass iclass; + P2 ipoint; + int p1, p2; + + LineIntersectionInfo(LineIntersectionClass _iclass, + P2 _ipoint = P2::ZERO(), + int _p1 = -1, + int _p2 = -1) : + iclass(_iclass), ipoint(_ipoint), p1(_p1), p2(_p2) { + } + }; + + struct PolyInclusionInfo { + PointClass iclass; + int iobjnum; + + PolyInclusionInfo(PointClass _iclass, + int _iobjnum = -1) : + iclass(_iclass), iobjnum(_iobjnum) { + } + }; + + struct PolyIntersectionInfo { + IntersectionClass iclass; + P2 ipoint; + size_t iobjnum; + + PolyIntersectionInfo(IntersectionClass _iclass, + const P2 &_ipoint, + size_t _iobjnum) : + iclass(_iclass), ipoint(_ipoint), iobjnum(_iobjnum) { + } + }; + + bool lineSegmentIntersection_simple(const P2 &l1v1, const P2 &l1v2, + const P2 &l2v1, const P2 &l2v2); + bool lineSegmentIntersection_simple(const LineSegment2 &l1, + const LineSegment2 &l2); + + LineIntersectionInfo lineSegmentIntersection(const P2 &l1v1, const P2 &l1v2, + const P2 &l2v1, const P2 &l2v2); + LineIntersectionInfo lineSegmentIntersection(const LineSegment2 &l1, + const LineSegment2 &l2); + + int lineSegmentPolyIntersections(const std::vector<P2> &points, + LineSegment2 line, + std::vector<PolyInclusionInfo> &out); + + int sortedLineSegmentPolyIntersections(const std::vector<P2> &points, + LineSegment2 line, + std::vector<PolyInclusionInfo> &out); + + + + static inline bool quadIsConvex(const P2 &a, const P2 &b, const P2 &c, const P2 &d) { + double s_1, s_2; + + s_1 = carve::geom2d::orient2d(a, c, b); + s_2 = carve::geom2d::orient2d(a, c, d); + if ((s_1 < 0.0 && s_2 < 0.0) || (s_1 > 0.0 && s_2 > 0.0)) return false; + + s_1 = carve::geom2d::orient2d(b, d, a); + s_2 = carve::geom2d::orient2d(b, d, c); + if ((s_1 < 0.0 && s_2 < 0.0) || (s_1 > 0.0 && s_2 > 0.0)) return false; + + return true; + } + + template<typename T, typename adapt_t> + inline bool quadIsConvex(const T &a, const T &b, const T &c, const T &d, adapt_t adapt) { + return quadIsConvex(adapt(a), adapt(b), adapt(c), adapt(d)); + } + + + + double signedArea(const std::vector<P2> &points); + + static inline double signedArea(const P2 &a, const P2 &b, const P2 &c) { + return ((b.y + a.y) * (b.x - a.x) + (c.y + b.y) * (c.x - b.x) + (a.y + c.y) * (a.x - c.x)) / 2.0; + } + + template<typename T, typename adapt_t> + double signedArea(const std::vector<T> &points, adapt_t adapt) { + P2Vector::size_type l = points.size(); + double A = 0.0; + + for (P2Vector::size_type i = 0; i < l - 1; i++) { + A += (adapt(points[i + 1]).y + adapt(points[i]).y) * (adapt(points[i + 1]).x - adapt(points[i]).x); + } + A += (adapt(points[0]).y + adapt(points[l - 1]).y) * (adapt(points[0]).x - adapt(points[l - 1]).x); + + return A / 2.0; + } + + + + template<typename iter_t, typename adapt_t> + double signedArea(iter_t begin, iter_t end, adapt_t adapt) { + double A = 0.0; + P2 p, n; + + if (begin == end) return 0.0; + + p = adapt(*begin); + for (iter_t c = begin; ++c != end; ) { + P2 n = adapt(*c); + A += (n.y + p.y) * (n.x - p.x); + p = n; + } + n = adapt(*begin); + A += (n.y + p.y) * (n.x - p.x); + + return A / 2.0; + } + + + + bool pointInPolySimple(const std::vector<P2> &points, const P2 &p); + + template<typename T, typename adapt_t> + bool pointInPolySimple(const std::vector<T> &points, adapt_t adapt, const P2 &p) { + CARVE_ASSERT(points.size() > 0); + P2Vector::size_type l = points.size(); + double s = 0.0; + double rp, r0, d; + + rp = r0 = atan2(adapt(points[0]) - p); + + for (P2Vector::size_type i = 1; i < l; i++) { + double r = atan2(adapt(points[i]) - p); + d = r - rp; + if (d > M_PI) d -= M_TWOPI; + if (d < -M_PI) d += M_TWOPI; + s = s + d; + rp = r; + } + + d = r0 - rp; + if (d > M_PI) d -= M_TWOPI; + if (d < -M_PI) d += M_TWOPI; + s = s + d; + + return !carve::math::ZERO(s); + } + + + + PolyInclusionInfo pointInPoly(const std::vector<P2> &points, const P2 &p); + + template<typename T, typename adapt_t> + PolyInclusionInfo pointInPoly(const std::vector<T> &points, adapt_t adapt, const P2 &p) { + P2Vector::size_type l = points.size(); + for (unsigned i = 0; i < l; i++) { + if (equal(adapt(points[i]), p)) return PolyInclusionInfo(POINT_VERTEX, i); + } + + for (unsigned i = 0; i < l; i++) { + unsigned j = (i + 1) % l; + + if (std::min(adapt(points[i]).x, adapt(points[j]).x) - EPSILON < p.x && + std::max(adapt(points[i]).x, adapt(points[j]).x) + EPSILON > p.x && + std::min(adapt(points[i]).y, adapt(points[j]).y) - EPSILON < p.y && + std::max(adapt(points[i]).y, adapt(points[j]).y) + EPSILON > p.y && + distance2(carve::geom::rayThrough(adapt(points[i]), adapt(points[j])), p) < EPSILON2) { + return PolyInclusionInfo(POINT_EDGE, i); + } + } + + if (pointInPolySimple(points, adapt, p)) { + return PolyInclusionInfo(POINT_IN); + } + + return PolyInclusionInfo(POINT_OUT); + } + + + + bool pickContainedPoint(const std::vector<P2> &poly, P2 &result); + + template<typename T, typename adapt_t> + bool pickContainedPoint(const std::vector<T> &poly, adapt_t adapt, P2 &result) { +#if defined(CARVE_DEBUG) + std::cerr << "pickContainedPoint "; + for (unsigned i = 0; i < poly.size(); ++i) std::cerr << " " << adapt(poly[i]); + std::cerr << std::endl; +#endif + + const size_t S = poly.size(); + P2 a, b, c; + for (unsigned i = 0; i < S; ++i) { + a = adapt(poly[i]); + b = adapt(poly[(i + 1) % S]); + c = adapt(poly[(i + 2) % S]); + + if (cross(a - b, c - b) < 0) { + P2 p = (a + b + c) / 3; + if (pointInPolySimple(poly, adapt, p)) { + result = p; + return true; + } + } + } + return false; + } + + } +} diff --git a/extern/carve/include/carve/geom3d.hpp b/extern/carve/include/carve/geom3d.hpp new file mode 100644 index 00000000000..90d0672b81e --- /dev/null +++ b/extern/carve/include/carve/geom3d.hpp @@ -0,0 +1,310 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> +#include <carve/geom.hpp> + +#include <math.h> +#include <carve/math_constants.hpp> + +#include <vector> +#include <list> +#include <map> + +#if defined(CARVE_DEBUG) +# include <iostream> +#endif + +#if defined CARVE_USE_EXACT_PREDICATES +# include <carve/shewchuk_predicates.hpp> +#endif + +namespace carve { + namespace geom3d { + + typedef carve::geom::plane<3> Plane; + typedef carve::geom::ray<3> Ray; + typedef carve::geom::linesegment<3> LineSegment; + typedef carve::geom::vector<3> Vector; + + template<typename iter_t, typename adapt_t> + bool fitPlane(iter_t begin, iter_t end, adapt_t adapt, Plane &plane) { + Vector centroid; + carve::geom::centroid(begin, end, adapt, centroid); + iter_t i; + + Vector n = Vector::ZERO(); + Vector v; + Vector p1, p2, p3, c1, c2; + if (begin == end) return false; + + i = begin; + p1 = c1 = adapt(*i); if (++i == end) return false; + p2 = c2 = adapt(*i); if (++i == end) return false; + +#if defined(CARVE_DEBUG) + size_t N = 2; +#endif + do { + p3 = adapt(*i); + v = cross(p3 - p2, p1 - p2); + if (v.v[largestAxis(v)]) v.negate(); + n += v; + p1 = p2; p2 = p3; +#if defined(CARVE_DEBUG) + ++N; +#endif + } while (++i != end); + + p1 = p2; p2 = p3; p3 = c1; + v = cross(p3 - p2, p1 - p2); + if (v.v[largestAxis(v)]) v.negate(); + n += v; + + p1 = p2; p2 = p3; p3 = c2; + v = cross(p3 - p2, p1 - p2); + if (v.v[largestAxis(v)]) v.negate(); + n += v; + + n.normalize(); + plane.N = n; + plane.d = -dot(n, centroid); +#if defined(CARVE_DEBUG) + if (N > 3) { + std::cerr << "N = " << N << " fitted distance:"; + for (i = begin; i != end; ++i) { + Vector p = adapt(*i); + std::cerr << " {" << p << "} " << distance(plane, p); + } + std::cerr << std::endl; + } +#endif + return true; + } + + bool planeIntersection(const Plane &a, const Plane &b, Ray &r); + + IntersectionClass rayPlaneIntersection(const Plane &p, + const Vector &v1, + const Vector &v2, + Vector &v, + double &t); + + IntersectionClass lineSegmentPlaneIntersection(const Plane &p, + const LineSegment &line, + Vector &v); + + RayIntersectionClass rayRayIntersection(const Ray &r1, + const Ray &r2, + Vector &v1, + Vector &v2, + double &mu1, + double &mu2); + + + + // test whether point d is above, below or on the plane formed by the triangle a,b,c. + // return: +ve = d is below a,b,c + // -ve = d is above a,b,c + // 0 = d is on a,b,c +#if defined CARVE_USE_EXACT_PREDICATES + inline double orient3d(const Vector &a, + const Vector &b, + const Vector &c, + const Vector &d) { + return shewchuk::orient3d(a.v, b.v, c.v, d.v); + } +#else + inline double orient3d(const Vector &a, + const Vector &b, + const Vector &c, + const Vector &d) { + return dotcross((a - d), (b - d), (c - d)); + } +#endif + + // Volume of a tetrahedron described by 4 points. Will be + // positive if the anticlockwise normal of a,b,c is oriented out + // of the tetrahedron. + // + // see: http://mathworld.wolfram.com/Tetrahedron.html + inline double tetrahedronVolume(const Vector &a, + const Vector &b, + const Vector &c, + const Vector &d) { + return dotcross((a - d), (b - d), (c - d)) / 6.0; + } + + /** + * \brief Determine whether p is internal to the wedge defined by + * the area between the planes defined by a,b,c and a,b,d + * angle abc, where ab is the apex of the angle. + * + * @param[in] a + * @param[in] b + * @param[in] c + * @param[in] d + * @param[in] p + * + * @return true, if p is contained in the wedge defined by the + * area between the planes defined by a,b,c and + * a,b,d. If the wedge is reflex, p is considered to + * be contained if it lies on either plane. Acute + * wdges do not contain p if p lies on either + * plane. This is so that internalToWedge(a,b,c,d,p) = + * !internalToWedge(a,b,d,c,p) + */ + inline bool internalToWedge(const Vector &a, + const Vector &b, + const Vector &c, + const Vector &d, + const Vector &p) { + bool reflex = (c < d) ? + orient3d(a, b, c, d) >= 0.0 : + orient3d(a, b, d, c) < 0.0; + + double d1 = orient3d(a, b, c, p); + double d2 = orient3d(a, b, d, p); + + if (reflex) { + // above a,b,c or below a,b,d (or coplanar with either) + return d1 <= 0.0 || d2 >= 0.0; + } else { + // above a,b,c and below a,b,d + return d1 < 0.0 && d2 > 0.0; + } + } + + /** + * \brief Determine the ordering relationship of a and b, when + * rotating around direction, starting from base. + * + * @param[in] adirection + * @param[in] base + * @param[in] a + * @param[in] b + * + * @return + * * -1, if a is ordered before b around, rotating about direction. + * * 0, if a and b are equal in angle. + * * +1, if a is ordered after b around, rotating about direction. + */ + inline int compareAngles(const Vector &direction, const Vector &base, const Vector &a, const Vector &b) { + const double d1 = carve::geom3d::orient3d(carve::geom::VECTOR(0,0,0), direction, a, b); + const double d2 = carve::geom3d::orient3d(carve::geom::VECTOR(0,0,0), direction, base, a); + const double d3 = carve::geom3d::orient3d(carve::geom::VECTOR(0,0,0), direction, base, b); + + // CASE: a and b are coplanar wrt. direction. + if (d1 == 0.0) { + // a and b point in the same direction. + if (dot(a, b) > 0.0) { + // Neither is less than the other. + return 0; + } + + // a and b point in opposite directions. + // * if d2 < 0.0, a is above plane(direction, base) and is less + // than b. + // * if d2 == 0.0 a is coplanar with plane(direction, base) and is + // less than b if it points in the same direction as base. + // * if d2 > 0.0, a is below plane(direction, base) and is greater + // than b. + + if (d2 == 0.0) { return dot(a, base) > 0.0 ? -1 : +1; } + if (d3 == 0.0) { return dot(b, base) > 0.0 ? +1 : -1; } + if (d2 < 0.0 && d3 > 0.0) return -1; + if (d2 > 0.0 && d3 < 0.0) return +1; + + // both a and b are to one side of plane(direction, base) - + // rounding error (if a and b are truly coplanar with + // direction, one should be above, and one should be below any + // other plane that is not itself coplanar with + // plane(direction, a|b) - which would imply d2 and d3 == 0.0). + + // If both are below plane(direction, base) then the one that + // points in the same direction as base is greater. + // If both are above plane(direction, base) then the one that + // points in the same direction as base is lesser. + if (d2 > 0.0) { return dot(a, base) > 0.0 ? +1 : -1; } + else { return dot(a, base) > 0.0 ? -1 : +1; } + } + + // CASE: a and b are not coplanar wrt. direction + + if (d2 < 0.0) { + // if a is above plane(direction,base), then a is less than b if + // b is below plane(direction,base) or b is above plane(direction,a) + return (d3 > 0.0 || d1 < 0.0) ? -1 : +1; + } else if (d2 == 0.0) { + // if a is on plane(direction,base) then a is less than b if a + // points in the same direction as base, or b is below + // plane(direction,base) + return (dot(a, base) > 0.0 || d3 > 0.0) ? -1 : +1; + } else { + // if a is below plane(direction,base), then a is less than b if b + // is below plane(direction,base) and b is above plane(direction,a) + return (d3 > 0.0 && d1 < 0.0) ? -1 : +1; + } + } + + // The anticlockwise angle from vector "from" to vector "to", oriented around the vector "orient". + static inline double antiClockwiseAngle(const Vector &from, const Vector &to, const Vector &orient) { + double dp = dot(from, to); + Vector cp = cross(from, to); + if (cp.isZero()) { + if (dp < 0) { + return M_PI; + } else { + return 0.0; + } + } else { + if (dot(cp, orient) > 0.0) { + return acos(dp); + } else { + return M_TWOPI - acos(dp); + } + } + } + + + + static inline double antiClockwiseOrdering(const Vector &from, const Vector &to, const Vector &orient) { + double dp = dot(from, to); + Vector cp = cross(from, to); + if (cp.isZero()) { + if (dp < 0) { + return 2.0; + } else { + return 0.0; + } + } else { + if (dot(cp, orient) > 0.0) { + // 1..-1 -> 0..2 + return 1.0 - dp; + } else { + // -1..1 -> 2..4 + return dp + 1.0; + } + } + } + + + + } +} diff --git a/extern/carve/include/carve/geom_impl.hpp b/extern/carve/include/carve/geom_impl.hpp new file mode 100644 index 00000000000..4463ba2bd88 --- /dev/null +++ b/extern/carve/include/carve/geom_impl.hpp @@ -0,0 +1,651 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/math.hpp> + +namespace carve { + namespace geom { + + + + template<unsigned ndim> + double vector<ndim>::length2() const { return dot(*this, *this); } + template<unsigned ndim> + double vector<ndim>::length() const { return sqrt(dot(*this, *this)); } + + template<unsigned ndim> + vector<ndim> &vector<ndim>::normalize() { *this /= length(); return *this; } + template<unsigned ndim> + vector<ndim> vector<ndim>::normalized() const { return *this / length(); } + + template<unsigned ndim> + bool vector<ndim>::exactlyZero() const { + for (unsigned i = 0; i < ndim; ++i) if (this->v[i]) return false; + return true; + } + template<unsigned ndim> + bool vector<ndim>::isZero(double epsilon) const { + return length2() < epsilon * epsilon; + } + + template<unsigned ndim> + void vector<ndim>::setZero() { for (size_t i = 0; i < ndim; ++i) this->v[i] = 0.0; } + + template<unsigned ndim> + void vector<ndim>::fill(double val) { for (size_t i = 0; i < ndim; ++i) this->v[i] = val; } + + template<unsigned ndim> + vector<ndim> &vector<ndim>::scaleBy(double d) { for (unsigned i = 0; i < ndim; ++i) this->v[i] *= d; return *this; } + template<unsigned ndim> + vector<ndim> &vector<ndim>::invscaleBy(double d) { for (unsigned i = 0; i < ndim; ++i) this->v[i] /= d; return *this; } + + template<unsigned ndim> + vector<ndim> vector<ndim>::scaled(double d) const { return *this * d; } + template<unsigned ndim> + vector<ndim> vector<ndim>::invscaled(double d) const { return *this / d; } + + template<unsigned ndim> + vector<ndim> &vector<ndim>::negate() { for (unsigned i = 0; i < ndim; ++i) this->v[i] = -this->v[i]; return *this; } + template<unsigned ndim> + vector<ndim> vector<ndim>::negated() const { return -*this; } + + template<unsigned ndim> + double &vector<ndim>::operator[](unsigned i) { return this->v[i]; } + template<unsigned ndim> + const double &vector<ndim>::operator[](unsigned i) const { return this->v[i]; } + + template<unsigned ndim> + template<typename assign_t> + vector<ndim> &vector<ndim>::operator=(const assign_t &t) { + for (unsigned i = 0; i < ndim; ++i) this->v[i] = t[i]; + return *this; + } + + template<unsigned ndim> + std::string vector<ndim>::asStr() const { + std::ostringstream out; + out << '<'; + out << std::setprecision(24); + for (unsigned i = 0; i < ndim; ++i) { if (i) out << ','; out << this->v[i]; } + out << '>'; + return out.str(); + } + + + + template<unsigned ndim> + vector<ndim> operator+(const vector<ndim> &a, const vector<ndim> &b) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = a[i] + b[i]; + return c; + } + + template<unsigned ndim> + vector<ndim> operator+(const vector<ndim> &a, double b) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = a[i] + b; + return c; + } + + template<unsigned ndim, typename val_t> + vector<ndim> operator+(const vector<ndim> &a, const val_t &b) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = a[i] + b[i]; + return c; + } + + template<unsigned ndim, typename val_t> + vector<ndim> operator+(const val_t &a, const vector<ndim> &b) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = a[i] + b[i]; + return c; + } + + template<unsigned ndim> + vector<ndim> &operator+=(vector<ndim> &a, const vector<ndim> &b) { + for (unsigned i = 0; i < ndim; ++i) a[i] += b[i]; + return a; + } + + template<unsigned ndim> + vector<ndim> &operator+=(vector<ndim> &a, double b) { + for (unsigned i = 0; i < ndim; ++i) a[i] += b; + return a; + } + + template<unsigned ndim, typename val_t> + vector<ndim> &operator+=(vector<ndim> &a, const val_t &b) { + for (unsigned i = 0; i < ndim; ++i) a[i] += b[i]; + return a; + } + + + + template<unsigned ndim> + vector<ndim> operator-(const vector<ndim> &a) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = -a[i]; + return c; + } + + + + template<unsigned ndim> + vector<ndim> operator-(const vector<ndim> &a, double b) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = a[i] - b; + return c; + } + + template<unsigned ndim> + vector<ndim> operator-(const vector<ndim> &a, const vector<ndim> &b) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = a[i] - b[i]; + return c; + } + + template<unsigned ndim, typename val_t> + vector<ndim> operator-(const vector<ndim> &a, const val_t &b) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = a[i] - b[i]; + return c; + } + + template<unsigned ndim, typename val_t> + vector<ndim> operator-(const val_t &a, const vector<ndim> &b) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = a[i] - b[i]; + return c; + } + + template<unsigned ndim> + vector<ndim> &operator-=(vector<ndim> &a, const vector<ndim> &b) { + for (unsigned i = 0; i < ndim; ++i) a[i] -= b[i]; + return a; + } + + template<unsigned ndim> + vector<ndim> &operator-=(vector<ndim> &a, double b) { + for (unsigned i = 0; i < ndim; ++i) a[i] -= b; + return a; + } + + template<unsigned ndim, typename val_t> + vector<ndim> &operator-=(vector<ndim> &a, const val_t &b) { + for (unsigned i = 0; i < ndim; ++i) a[i] -= b[i]; + return a; + } + + + + template<unsigned ndim> + vector<ndim> operator*(const vector<ndim> &a, double s) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = a[i] * s; + return c; + } + + template<unsigned ndim> + vector<ndim> operator*(double s, const vector<ndim> &a) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = a[i] * s; + return c; + } + + template<unsigned ndim> + vector<ndim> &operator*=(vector<ndim> &a, double s) { + for (unsigned i = 0; i < ndim; ++i) a[i] *= s; + return a; + } + + + + template<unsigned ndim> + vector<ndim> operator/(const vector<ndim> &a, double s) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = a[i] / s; + return c; + } + + template<unsigned ndim> + vector<ndim> &operator/=(vector<ndim> &a, double s) { + for (unsigned i = 0; i < ndim; ++i) a[i] /= s; + return a; + } + + + + template<unsigned ndim> + bool operator==(const vector<ndim> &a, const vector<ndim> &b) { + for (unsigned i = 0; i < ndim; ++i) { if (a[i] != b[i]) return false; } + return true; + } + + template<unsigned ndim> + bool operator!=(const vector<ndim> &a, const vector<ndim> &b) { + return !(a == b); + } + + template<unsigned ndim> + bool operator<(const vector<ndim> &a, const vector<ndim> &b) { + for (unsigned i = 0; i < ndim; ++i) { if (a[i] < b[i]) return true; if (a[i] > b[i]) return false; } + return false; + } + + template<unsigned ndim> + bool operator<=(const vector<ndim> &a, const vector<ndim> &b) { + return !(b < a); + } + + template<unsigned ndim> + bool operator>(const vector<ndim> &a, const vector<ndim> &b) { + return b < a; + } + + template<unsigned ndim> + bool operator>=(const vector<ndim> &a, const vector<ndim> &b) { + return !(a < b); + } + + + + template<unsigned ndim> + vector<ndim> abs(const vector<ndim> &a) { + vector<ndim> c(NOINIT); + for (unsigned i = 0; i < ndim; ++i) c[i] = fabs(a[i]); + return c; + } + + template<unsigned ndim> + double distance2(const vector<ndim> &a, const vector<ndim> &b) { + return (b - a).length2(); + } + + template<unsigned ndim> + double distance(const vector<ndim> &a, const vector<ndim> &b) { + return (b - a).length(); + } + + template<unsigned ndim> + bool equal(const vector<ndim> &a, const vector<ndim> &b) { + return (b - a).isZero(); + } + + template<unsigned ndim> + int smallestAxis(const vector<ndim> &a) { + int x = 0; + double y = fabs(a[0]); + for (unsigned i = 1; i < ndim; ++i) { + double z = fabs(a[i]); + if (z <= y) { y = z; x = i; } + } + return x; + } + + template<unsigned ndim> + int largestAxis(const vector<ndim> &a) { + int x = 0; + double y = fabs(a[0]); + for (unsigned i = 1; i < ndim; ++i) { + double z = fabs(a[i]); + if (z > y) { y = z; x = i; } + } + return x; + } + + template<unsigned ndim> + vector<2> select(const vector<ndim> &a, int a1, int a2) { + vector<2> r(NOINIT); + r.v[0] = a.v[a1]; r.v[1] = a.v[a2]; + return r; + } + + template<unsigned ndim> + vector<3> select(const vector<ndim> &a, int a1, int a2, int a3) { + vector<3> r(NOINIT); + r.v[0] = a.v[a1]; r.v[1] = a.v[a2]; r.v[2] = a.v[a3]; + return r; + } + + template<unsigned ndim, typename assign_t, typename oper_t> + vector<ndim> &assign_op(vector<ndim> &a, const assign_t &t, oper_t op) { + for (unsigned i = 0; i < ndim; ++i) a[i] = op(t[i]); + return a; + } + + template<unsigned ndim, typename assign1_t, typename assign2_t, typename oper_t> + vector<ndim> &assign_op(vector<ndim> &a, const assign1_t &t1, const assign2_t &t2, oper_t op) { + for (unsigned i = 0; i < ndim; ++i) a[i] = op(t1[i], t2[i]); + return a; + } + + template<unsigned ndim, typename iter_t> + void bounds(iter_t begin, iter_t end, vector<ndim> &min, vector<ndim> &max) { + if (begin == end) { + min.setZero(); + max.setZero(); + } else { + min = max = *begin; + while (++begin != end) { + vector<ndim> v = *begin; + assign_op(min, min, v, carve::util::min_functor()); + assign_op(max, max, v, carve::util::max_functor()); + } + } + } + + template<unsigned ndim, typename iter_t, typename adapt_t> + void bounds(iter_t begin, iter_t end, adapt_t adapt, vector<ndim> &min, vector<ndim> &max) { + if (begin == end) { + min.setZero(); + max.setZero(); + } else { + min = max = adapt(*begin); + while (++begin != end) { + vector<ndim> v = adapt(*begin); + assign_op(min, min, v, carve::util::min_functor()); + assign_op(max, max, v, carve::util::max_functor()); + } + } + } + + template<unsigned ndim, typename iter_t, typename adapt_t> + void centroid(iter_t begin, iter_t end, adapt_t adapt, vector<ndim> &c) { + c.setZero(); + int n = 0; + while (begin != end) { c += adapt(*begin++); ++n; } + c /= double(n); + } + + template<unsigned ndim, typename val_t> + double dot(const vector<ndim> &a, const val_t &b) { + double r = 0.0; + for (unsigned i = 0; i < ndim; ++i) r += a[i] * b[i]; + return r; + } + + static inline vector<3> cross(const vector<3> &a, const vector<3> &b) { + // Compute a x b + return VECTOR(+(a.y * b.z - a.z * b.y), + -(a.x * b.z - a.z * b.x), + +(a.x * b.y - a.y * b.x)); + } + + static inline double cross(const vector<2> &a, const vector<2> &b) { + // Compute a x b + return a.x * b.y - b.x * a.y; + } + + static inline double dotcross(const vector<3> &a, const vector<3> &b, const vector<3> &c) { + // Compute a . (b x c) + return + (a.x * b.y * c.z + a.y * b.z * c.x + a.z * b.x * c.y) - + (a.x * b.z * c.y + a.y * b.x * c.z + a.z * b.y * c.x); + } + + + + template<unsigned ndim> + double distance(const axis_pos &a, const vector<ndim> &b) { + return fabs(b[a.axis] - a.pos); + } + + template<unsigned ndim> + double distance2(const axis_pos &a, const vector<ndim> &b) { + double r = fabs(b[a.axis] - a.pos); + return r * r; + } + + template<unsigned ndim> bool operator<(const axis_pos &a, const vector<ndim> &b) { return a.pos < b[a.axis]; } + template<unsigned ndim> bool operator<(const vector<ndim> &a, const axis_pos &b) { return a[b.axis] < b.pos; } + + template<unsigned ndim> bool operator<=(const axis_pos &a, const vector<ndim> &b) { return a.pos <= b[a.axis]; } + template<unsigned ndim> bool operator<=(const vector<ndim> &a, const axis_pos &b) { return a[b.axis] <= b.pos; } + + template<unsigned ndim> bool operator>(const axis_pos &a, const vector<ndim> &b) { return a.pos > b[a.axis]; } + template<unsigned ndim> bool operator>(const vector<ndim> &a, const axis_pos &b) { return a[b.axis] > b.pos; } + + template<unsigned ndim> bool operator>=(const axis_pos &a, const vector<ndim> &b) { return a.pos >= b[a.axis]; } + template<unsigned ndim> bool operator>=(const vector<ndim> &a, const axis_pos &b) { return a[b.axis] >= b.pos; } + + template<unsigned ndim> bool operator==(const axis_pos &a, const vector<ndim> &b) { return a.pos == b[a.axis]; } + template<unsigned ndim> bool operator==(const vector<ndim> &a, const axis_pos &b) { return a[b.axis] == b.pos; } + + template<unsigned ndim> bool operator!=(const axis_pos &a, const vector<ndim> &b) { return a.pos != b[a.axis]; } + template<unsigned ndim> bool operator!=(const vector<ndim> &a, const axis_pos &b) { return a[b.axis] != b.pos; } + + + + template<unsigned ndim> + bool ray<ndim>::OK() const { + return !D.isZero(); + } + + template<unsigned ndim> + ray<ndim> rayThrough(const vector<ndim> &a, const vector<ndim> &b) { + return ray<ndim>(b - a, a); + } + + static inline double distance2(const ray<3> &r, const vector<3> &v) { + return cross(r.D, v - r.v).length2() / r.D.length2(); + } + + static inline double distance(const ray<3> &r, const vector<3> &v) { + return sqrt(distance2(r, v)); + } + + static inline double distance2(const ray<2> &r, const vector<2> &v) { + double t = cross(r.D, v - r.v); + return (t * t) / r.D.length2(); + } + + static inline double distance(const ray<2> &r, const vector<2> &v) { + return sqrt(distance2(r, v)); + } + + + + template<unsigned ndim> + void linesegment<ndim>::update() { + midpoint = (v2 + v1) / 2.0; + half_length = (v2 - v1) / 2.0; + } + + template<unsigned ndim> + bool linesegment<ndim>::OK() const { + return !half_length.isZero(); + } + + template<unsigned ndim> + void linesegment<ndim>::flip() { + std::swap(v1, v2); + half_length = (v2 - v1) / 2.0; + } + + template<unsigned ndim> + aabb<ndim> linesegment<ndim>::getAABB() const { + aabb<ndim> r; + r.fit(v1, v2); + return r; + } + + template<unsigned ndim> + linesegment<ndim>::linesegment(const vector_t &_v1, const vector_t &_v2) : v1(_v1), v2(_v2) { + update(); + } + + + + template<unsigned ndim> + double distance2(const linesegment<ndim> &l, const vector<ndim> &v) { + vector<ndim> D = l.v2 - l.v1; + double t = dot(v - l.v1, D) / dot(D, D); + if (t <= 0.0) return (v - l.v1).length2(); + if (t >= 1.0) return (v - l.v2).length2(); + vector<ndim> vc = D * t + l.v1; + return (v - vc).length2(); + } + + template<unsigned ndim> + double distance(const linesegment<ndim> &l, const vector<ndim> &v) { + return sqrt(distance2(l, v)); + } + + + template<unsigned ndim> + void plane<ndim>::negate() { + N.negate(); + d = -d; + } + + template<unsigned ndim> + plane<ndim>::plane() { + N.setZero(); + N[0] = 1.0; + d= 0.0; + } + + template<unsigned ndim> + plane<ndim>::plane(const vector_t &_N, vector_t _p) : N(_N), d(-dot(_p, _N)) { + } + + template<unsigned ndim> + plane<ndim>::plane(const vector_t &_N, double _d) : N(_N), d(_d) { + } + + + + template<unsigned ndim> + plane<ndim> operator-(const plane<ndim> &p) { + return plane<ndim>(-p.N, -p.d); + } + + template<unsigned ndim, typename val_t> + double distance(const plane<ndim> &plane, const val_t &point) { + return dot(plane.N, point) + plane.d; + } + + template<unsigned ndim, typename val_t> + double distance2(const plane<ndim> &plane, const val_t &point) { + double d = distance(plane, point); + return d * d; + } + + template<unsigned ndim> + vector<ndim> closestPoint(const plane<ndim> &p, const vector<ndim> &v) { + return v - p.N * (p.d + dot(p.N, v)) / dot(p.N, p.N); + } + + + + template<unsigned ndim> + aabb<ndim> sphere<ndim>::getAABB() const { + aabb<ndim> r; + r.fit(C - r, C + r); + } + + template<unsigned ndim> + sphere<ndim>::sphere() { + C.setZero(); + r = 1.0; + } + + template<unsigned ndim> + sphere<ndim>::sphere(const vector_t &_C, double _r) : C(_C), r(_r) { + } + + + + template<unsigned ndim, typename val_t> + double distance(const sphere<ndim> &sphere, const val_t &point) { + return std::max(0.0, distance(sphere.C, point) - sphere.r); + } + + template<unsigned ndim, typename val_t> + double distance2(const sphere<ndim> &sphere, const val_t &point) { + return std::max(0.0, distance2(sphere.C, point) - sphere.r * sphere.r); + } + + template<unsigned ndim> + vector<ndim> closestPoint(const sphere<ndim> &sphere, const vector<ndim> &point) { + return (point - sphere.C).normalized() * sphere.r; + } + + + + template<unsigned ndim> + aabb<ndim> tri<ndim>::getAABB() const { + aabb<ndim> aabb; + aabb.fit(v[0], v[1], v[2]); + return aabb; + } + + template<unsigned ndim> + tri<ndim>::tri(vector_t _v[3]) { + std::copy(v, v+3, _v); + } + + template<unsigned ndim> + tri<ndim>::tri(const vector_t &a, const vector_t &b, const vector_t &c) { + v[0] = a; + v[1] = b; + v[2] = c; + } + + + + template<unsigned ndim> + std::ostream &operator<<(std::ostream &o, const vector<ndim> &v) { + o << v.asStr(); + return o; + } + + template<unsigned ndim> + std::ostream &operator<<(std::ostream &o, const carve::geom::plane<ndim> &p) { + o << p.N << ";" << p.d; + return o; + } + + template<unsigned ndim> + std::ostream &operator<<(std::ostream &o, const carve::geom::sphere<ndim> &sphere) { + o << "{sphere " << sphere.C << ";" << sphere.r << "}"; + return o; + } + + template<unsigned ndim> + std::ostream &operator<<(std::ostream &o, const carve::geom::tri<ndim> &tri) { + o << "{tri " << tri.v[0] << ";" << tri.v[1] << ";" << tri.v[2] << "}"; + return o; + } + + + + template<unsigned ndim> + double distance(const tri<ndim> &tri, const vector<ndim> &pt) { + return distance(closestPoint(tri, pt), pt); + } + + + + template<unsigned ndim> + double distance2(const tri<ndim> &tri, const vector<ndim> &pt) { + return distance2(closestPoint(tri, pt), pt); + } + } +} diff --git a/extern/carve/include/carve/gnu_cxx.h b/extern/carve/include/carve/gnu_cxx.h new file mode 100644 index 00000000000..280fa360478 --- /dev/null +++ b/extern/carve/include/carve/gnu_cxx.h @@ -0,0 +1,4 @@ +// Copyright 2006 Tobias Sargeant (toby@permuted.net) +// All rights reserved. + +#pragma once diff --git a/extern/carve/include/carve/heap.hpp b/extern/carve/include/carve/heap.hpp new file mode 100644 index 00000000000..20bdcf003e5 --- /dev/null +++ b/extern/carve/include/carve/heap.hpp @@ -0,0 +1,425 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#pragma once +namespace carve { + namespace heap { + namespace detail { + + + + struct ignore_position_t { + template<typename value_t> + void operator()(value_t &val, size_t idx) const {} + }; + + + + template<typename random_access_iter_t, + typename distance_t, + typename value_t, + typename pred_t, + typename pos_notifier_t> + void _adjust_heap(random_access_iter_t begin, + distance_t pos, + distance_t len, + value_t val, + pred_t pred, + pos_notifier_t notify) { + const distance_t top = pos; + + distance_t child = pos * 2 + 2; + while (child < len) { + if (pred(begin[child], begin[child - 1])) child--; + + begin[pos] = begin[child]; + notify(begin[pos], pos); + pos = child; + child = pos * 2 + 2; + } + + if (child == len) { + child--; + begin[pos] = begin[child]; + notify(begin[pos], pos); + pos = child; + } + + distance_t parent = (pos - 1) / 2; + while (pos > top && pred(begin[parent], val)) { + begin[pos] = begin[parent]; + notify(begin[pos], pos); + pos = parent; + parent = (pos - 1) / 2; + } + + begin[pos] = val; + notify(begin[pos], pos); + } + + + + template<typename random_access_iter_t, + typename distance_t, + typename value_t, + typename pred_t, + typename pos_notifier_t> + void _push_heap(random_access_iter_t begin, + distance_t pos, + value_t val, + pred_t pred, + pos_notifier_t notify) { + distance_t parent = (pos - 1) / 2; + while (pos > 0 && pred(begin[parent], val)) { + begin[pos] = begin[parent]; + notify(begin[pos], pos); + pos = parent; + parent = (pos - 1) / 2; + } + begin[pos] = val; + notify(begin[pos], pos); + } + + + + template<typename random_access_iter_t, + typename distance_t, + typename pred_t, + typename pos_notifier_t> + void _remove_heap(random_access_iter_t begin, + distance_t pos, + distance_t len, + pred_t pred, + pos_notifier_t notify) { + --len; + if (pos != len) { + typedef typename std::iterator_traits<random_access_iter_t>::value_type value_t; + value_t removed = begin[pos]; + _adjust_heap(begin, pos, len, begin[len], pred, notify); + begin[len] = removed; + notify(begin[len], len); + } + } + + + + template<typename random_access_iter_t, + typename distance_t, + typename pred_t, + typename pos_notifier_t> + void _make_heap(random_access_iter_t begin, + distance_t len, + pred_t pred, + pos_notifier_t notify) { + for (distance_t pos = len / 2; pos > 0; ) { + --pos; + _adjust_heap(begin, pos, len, begin[pos], pred, ignore_position_t()); + } + for (distance_t pos = 0; pos < len; ++pos) { + notify(begin[pos], pos); + } + } + + + + template<typename random_access_iter_t, + typename distance_t, + typename pred_t> + void _make_heap(random_access_iter_t begin, + distance_t len, + pred_t pred, + ignore_position_t) { + for (distance_t pos = len / 2; pos > 0; ) { + --pos; + _adjust_heap(begin, pos, len, begin[pos], pred, ignore_position_t()); + } + } + + + + template<typename random_access_iter_t, + typename distance_t, + typename pred_t> + bool _is_heap(random_access_iter_t begin, + distance_t len, + pred_t pred) { + distance_t parent = 0; + + for (distance_t child = 1; child < len; ++child) { + if (pred(begin[parent], begin[child])) { + return false; + } + if (++child == len) break; + if (pred(begin[parent], begin[child])) { + return false; + } + ++parent; + } + + return true; + } + + + + } + + + + template<typename random_access_iter_t> + void adjust_heap(random_access_iter_t begin, + random_access_iter_t end, + random_access_iter_t pos) { + typedef typename std::iterator_traits<random_access_iter_t>::value_type value_t; + + detail::_adjust_heap(begin, pos - begin, end - begin, *pos, std::less<value_t>()); + } + + + + template<typename random_access_iter_t, + typename pred_t> + void adjust_heap(random_access_iter_t begin, + random_access_iter_t end, + random_access_iter_t pos, + pred_t pred) { + detail::_adjust_heap(begin, pos - begin, end - begin, *pos, pred); + } + + + + template<typename random_access_iter_t, + typename pred_t, + typename pos_notifier_t> + void adjust_heap(random_access_iter_t begin, + random_access_iter_t end, + random_access_iter_t pos, + pred_t pred, + pos_notifier_t notify) { + detail::_adjust_heap(begin, pos - begin, end - begin, *pos, pred, notify); + } + + + + template<typename random_access_iter_t> + void remove_heap(random_access_iter_t begin, + random_access_iter_t end, + random_access_iter_t pos) { + typedef typename std::iterator_traits<random_access_iter_t>::value_type value_t; + + detail::_remove_heap(begin, pos - begin, end - begin, std::less<value_t>(), detail::ignore_position_t()); + } + + + + template<typename random_access_iter_t, + typename pred_t> + void remove_heap(random_access_iter_t begin, + random_access_iter_t end, + random_access_iter_t pos, + pred_t pred) { + detail::_remove_heap(begin, pos - begin, end - begin, pred, detail::ignore_position_t()); + } + + + + template<typename random_access_iter_t, + typename pred_t, + typename pos_notifier_t> + void remove_heap(random_access_iter_t begin, + random_access_iter_t end, + random_access_iter_t pos, + pred_t pred, + pos_notifier_t notify) { + detail::_remove_heap(begin, pos - begin, end - begin, pred, notify); + } + + + + template<typename random_access_iter_t> + void pop_heap(random_access_iter_t begin, + random_access_iter_t end) { + typedef typename std::iterator_traits<random_access_iter_t>::value_type value_t; + typedef typename std::iterator_traits<random_access_iter_t>::difference_type distance_t; + + detail::_remove_heap(begin, distance_t(0), end - begin, std::less<value_t>(), detail::ignore_position_t()); + } + + + + template<typename random_access_iter_t, + typename pred_t> + void pop_heap(random_access_iter_t begin, + random_access_iter_t end, + pred_t pred) { + typedef typename std::iterator_traits<random_access_iter_t>::difference_type distance_t; + + detail::_remove_heap(begin, distance_t(0), end - begin, pred, detail::ignore_position_t()); + } + + + + template<typename random_access_iter_t, + typename pred_t, + typename pos_notifier_t> + void pop_heap(random_access_iter_t begin, + random_access_iter_t end, + pred_t pred, + pos_notifier_t notify) { + typedef typename std::iterator_traits<random_access_iter_t>::difference_type distance_t; + + detail::_remove_heap(begin, distance_t(0), end - begin, pred, notify); + } + + + + template<typename random_access_iter_t> + void push_heap(random_access_iter_t begin, + random_access_iter_t end) { + typedef typename std::iterator_traits<random_access_iter_t>::value_type value_t; + typedef typename std::iterator_traits<random_access_iter_t>::difference_type distance_t; + + distance_t pos = end - begin - 1; + detail::_push_heap(begin, pos, begin[pos], std::less<value_t>(), detail::ignore_position_t()); + } + + + + template<typename random_access_iter_t, + typename pred_t> + void push_heap(random_access_iter_t begin, + random_access_iter_t end, + pred_t pred) { + typedef typename std::iterator_traits<random_access_iter_t>::difference_type distance_t; + + distance_t pos = end - begin - 1; + detail::_push_heap(begin, pos, begin[pos], pred, detail::ignore_position_t()); + } + + + + template<typename random_access_iter_t, + typename pred_t, + typename pos_notifier_t> + void push_heap(random_access_iter_t begin, + random_access_iter_t end, + pred_t pred, + pos_notifier_t notify) { + typedef typename std::iterator_traits<random_access_iter_t>::difference_type distance_t; + + distance_t pos = end - begin - 1; + detail::_push_heap(begin, pos, begin[pos], pred, notify); + } + + + + template<typename random_access_iter_t> + void make_heap(random_access_iter_t begin, + random_access_iter_t end) { + typedef typename std::iterator_traits<random_access_iter_t>::value_type value_t; + + detail::_make_heap(begin, end - begin, std::less<value_t>(), detail::ignore_position_t()); + } + + + + template<typename random_access_iter_t, + typename pred_t> + void make_heap(random_access_iter_t begin, + random_access_iter_t end, + pred_t pred) { + detail::_make_heap(begin, end - begin, pred, detail::ignore_position_t()); + } + + + + template<typename random_access_iter_t, + typename pred_t, + typename pos_notifier_t> + void make_heap(random_access_iter_t begin, + random_access_iter_t end, + pred_t pred, + pos_notifier_t notify) { + detail::_make_heap(begin, end - begin, pred, notify); + } + + + + template<typename random_access_iter_t> + bool is_heap(random_access_iter_t begin, + random_access_iter_t end) { + typedef typename std::iterator_traits<random_access_iter_t>::value_type value_t; + + return detail::_is_heap(begin, end - begin, std::less<value_t>()); + } + + + + template<typename random_access_iter_t, typename pred_t> + bool is_heap(random_access_iter_t begin, + random_access_iter_t end, + pred_t pred) { + return detail::_is_heap(begin, end - begin, pred); + } + + + + template<typename random_access_iter_t> + void sort_heap(random_access_iter_t begin, + random_access_iter_t end) { + typedef typename std::iterator_traits<random_access_iter_t>::difference_type distance_t; + typedef typename std::iterator_traits<random_access_iter_t>::value_type value_t; + + for (distance_t len = end - begin; len > 1; --len) { + detail::_remove_heap(begin, distance_t(0), len, std::less<value_t>(), detail::ignore_position_t()); + } + } + + + + template<typename random_access_iter_t, + typename pred_t> + void sort_heap(random_access_iter_t begin, + random_access_iter_t end, + pred_t pred) { + typedef typename std::iterator_traits<random_access_iter_t>::difference_type distance_t; + + for (distance_t len = end - begin; len > 1; --len) { + detail::_remove_heap(begin, distance_t(0), len, pred, detail::ignore_position_t()); + } + } + + + + template<typename random_access_iter_t, + typename pred_t, + typename pos_notifier_t> + void sort_heap(random_access_iter_t begin, + random_access_iter_t end, + pred_t pred, + pos_notifier_t notify) { + typedef typename std::iterator_traits<random_access_iter_t>::difference_type distance_t; + + for (distance_t len = end - begin; len > 1; --len) { + detail::_remove_heap(begin, distance_t(0), len, pred, detail::ignore_position_t()); + notify(begin[len], len); + } + notify(begin[0], 0); + } + + + + } +} diff --git a/extern/carve/include/carve/input.hpp b/extern/carve/include/carve/input.hpp new file mode 100644 index 00000000000..a8bc8137d6c --- /dev/null +++ b/extern/carve/include/carve/input.hpp @@ -0,0 +1,251 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> +#include <carve/poly.hpp> +#include <carve/mesh.hpp> +#include <carve/polyline.hpp> +#include <carve/pointset.hpp> + + + +namespace carve { + namespace input { + + struct Data { + Data() { + } + + virtual ~Data() { + } + + virtual void transform(const carve::math::Matrix & /* transform */) { + } + }; + + + + struct VertexData : public Data { + std::vector<carve::geom3d::Vector> points; + + VertexData() : Data() { + } + + virtual ~VertexData() { + } + + virtual void transform(const carve::math::Matrix &transform) { + for (size_t i = 0; i < points.size(); ++i) { + points[i] *= transform; + } + } + + size_t addVertex(carve::geom3d::Vector point) { + size_t index = points.size(); + points.push_back(point); + return index; + } + + inline void reserveVertices(int count) { + points.reserve(count); + } + + size_t getVertexCount() const { + return points.size(); + } + + const carve::geom3d::Vector &getVertex(int index) const { + return points[index]; + } + }; + + + + struct PolyhedronData : public VertexData { + std::vector<int> faceIndices; + int faceCount; + + PolyhedronData() : VertexData(), faceIndices(), faceCount(0) { + } + + virtual ~PolyhedronData() { + } + + void reserveFaces(int count, int avgFaceSize) { + faceIndices.reserve(faceIndices.size() + count * (1 + avgFaceSize)); + } + + int getFaceCount() const { + return faceCount; + } + + template <typename Iter> + void addFace(Iter begin, Iter end) { + size_t n = std::distance(begin, end); + faceIndices.reserve(faceIndices.size() + n + 1); + faceIndices.push_back(n); + std::copy(begin, end, std::back_inserter(faceIndices)); + ++faceCount; + } + + void addFace(int a, int b, int c) { + faceIndices.push_back(3); + faceIndices.push_back(a); + faceIndices.push_back(b); + faceIndices.push_back(c); + ++faceCount; + } + + void addFace(int a, int b, int c, int d) { + faceIndices.push_back(4); + faceIndices.push_back(a); + faceIndices.push_back(b); + faceIndices.push_back(c); + faceIndices.push_back(d); + ++faceCount; + } + + void clearFaces() { + faceIndices.clear(); + faceCount = 0; + } + + carve::poly::Polyhedron *create() const { + return new carve::poly::Polyhedron(points, faceCount, faceIndices); + } + + carve::mesh::MeshSet<3> *createMesh() const { + return new carve::mesh::MeshSet<3>(points, faceCount, faceIndices); + } + }; + + + + struct PolylineSetData : public VertexData { + typedef std::pair<bool, std::vector<int> > polyline_data_t; + std::list<polyline_data_t> polylines; + + PolylineSetData() : VertexData(), polylines() { + } + + virtual ~PolylineSetData() { + } + + void beginPolyline(bool closed = false) { + polylines.push_back(std::make_pair(closed, std::vector<int>())); + } + + void reservePolyline(size_t len) { + polylines.back().second.reserve(len); + } + + void addPolylineIndex(int idx) { + polylines.back().second.push_back(idx); + } + + carve::line::PolylineSet *create() const { + carve::line::PolylineSet *p = new carve::line::PolylineSet(points); + + for (std::list<polyline_data_t>::const_iterator i = polylines.begin(); + i != polylines.end(); + ++i) { + p->addPolyline((*i).first, (*i).second.begin(), (*i).second.end()); + } + return p; + } + }; + + + + struct PointSetData : public VertexData { + + PointSetData() : VertexData() { + } + + virtual ~PointSetData() { + } + + carve::point::PointSet *create() const { + carve::point::PointSet *p = new carve::point::PointSet(points); + return p; + } + }; + + + + class Input { + public: + std::list<Data *> input; + + Input() { + } + + ~Input() { + for (std::list<Data *>::iterator i = input.begin(); i != input.end(); ++i) { + delete (*i); + } + } + + void addDataBlock(Data *data) { + input.push_back(data); + } + + void transform(const carve::math::Matrix &transform) { + if (transform == carve::math::Matrix::IDENT()) return; + for (std::list<Data *>::iterator i = input.begin(); i != input.end(); ++i) { + (*i)->transform(transform); + } + } + + template<typename T> + static inline T *create(Data *d) { + return NULL; + } + }; + + template<> + inline carve::mesh::MeshSet<3> *Input::create(Data *d) { + PolyhedronData *p = dynamic_cast<PolyhedronData *>(d); + if (p == NULL) return NULL; + return p->createMesh(); + } + + template<> + inline carve::poly::Polyhedron *Input::create(Data *d) { + PolyhedronData *p = dynamic_cast<PolyhedronData *>(d); + if (p == NULL) return NULL; + return p->create(); + } + + template<> + inline carve::line::PolylineSet *Input::create(Data *d) { + PolylineSetData *p = dynamic_cast<PolylineSetData *>(d); + if (p == NULL) return NULL; + return p->create(); + } + + template<> + inline carve::point::PointSet *Input::create(Data *d) { + PointSetData *p = dynamic_cast<PointSetData *>(d); + if (p == NULL) return NULL; + return p->create(); + } + + } +} diff --git a/extern/carve/include/carve/interpolator.hpp b/extern/carve/include/carve/interpolator.hpp new file mode 100644 index 00000000000..e1555105435 --- /dev/null +++ b/extern/carve/include/carve/interpolator.hpp @@ -0,0 +1,332 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> +#include <carve/geom2d.hpp> +#include <carve/poly.hpp> +#include <carve/mesh.hpp> +#include <carve/csg.hpp> + +namespace carve { + namespace interpolate { + + static inline std::vector<double> polyInterpolate(const std::vector<carve::geom2d::P2> &s, + const carve::geom2d::P2 &v) { + // see hormann et al. 2006 + const size_t SZ = s.size(); + std::vector<double> r; + std::vector<double> A; + std::vector<double> D; + + std::vector<double> result; + + r.resize(SZ); + A.resize(SZ); + D.resize(SZ); + + result.resize(SZ, 0.0); + + for (size_t i = 0; i < SZ; ++i) { + size_t i2 = (i + 1) % SZ; + carve::geom2d::P2 si = s[i] - v; + carve::geom2d::P2 si2 = s[i2] - v; + + r[i] = sqrt(dot(si, si)); + A[i] = cross(si, si2) / 2.0; + D[i] = dot(si, si2); + if (fabs(r[i]) < 1e-16) { + result[i] = 1.0; + return result; + } else if (fabs(A[i]) < 1e-16 && D[i] < 0.0) { + double r2 = sqrt(dot(si2, si2)); + result[i2] = r[i] / (r[i] + r2); + result[i] = r2 / (r[i] + r2); + return result; + } + } + + double w_sum = 0.0; + + for (size_t i = 0; i < SZ; ++i) { + size_t i_m = (i + SZ - 1) % SZ; + size_t i_p = (i + 1) % SZ; + + double w = 0.0; + if (fabs(A[i_m]) > 1e-16) + w += (r[i_m] - D[i_m] / r[i]) / A[i_m]; + if (fabs(A[i]) > 1e-16) + w += (r[i_p] - D[i] / r[i]) / A[i]; + + result[i] = w; + w_sum += w; + } + + for (size_t i = 0; i < SZ; ++i) { + result[i] /= w_sum; + } + +// carve::geom2d::P2 test; +// for (size_t i = 0; i < SZ; ++i) { +// test = test + result[i] * s[i]; +// } + + return result; + } + + template<typename iter_t, + typename adapt_t, + typename val_t, + typename mod_t> + val_t interp(iter_t begin, + iter_t end, + adapt_t adapt, + const std::vector<val_t> &vals, + double x, + double y, + mod_t mod = mod_t()) { + std::vector<carve::geom2d::P2> s; + s.reserve(std::distance(begin, end)); + std::transform(begin, end, std::back_inserter(s), adapt); + std::vector<double> weight = polyInterpolate(s, carve::geom::VECTOR(x, y)); + + val_t v; + for (size_t z = 0; z < weight.size(); z++) { + v += weight[z] * vals[z]; + } + + return mod(v); + } + + template<typename iter_t, + typename adapt_t, + typename val_t> + val_t interp(iter_t begin, + iter_t end, + adapt_t adapt, + const std::vector<val_t> &vals, + double x, + double y) { + return interp(begin, end, adapt, vals, x, y, identity_t<val_t>()); + } + + template<typename vertex_t, + typename adapt_t, + typename val_t, + typename mod_t> + val_t interp(const std::vector<vertex_t> &poly, + adapt_t adapt, + const std::vector<val_t> &vals, + double x, + double y, + mod_t mod = mod_t()) { + return interp(poly.begin(), poly.end(), adapt, vals, x, y, mod); + } + + template<typename vertex_t, + typename adapt_t, + typename val_t> + val_t interp(const std::vector<vertex_t> &poly, + adapt_t adapt, + const std::vector<val_t> &vals, + double x, + double y) { + return interp(poly.begin(), poly.end(), adapt, vals, x, y, identity_t<val_t>()); + } + + template<typename val_t, + typename mod_t> + val_t interp(const std::vector<carve::geom2d::P2> &poly, + const std::vector<val_t> &vals, + double x, + double y, + mod_t mod = mod_t()) { + std::vector<double> weight = polyInterpolate(poly, carve::geom::VECTOR(x, y)); + + val_t v; + for (size_t z = 0; z < weight.size(); z++) { + v += weight[z] * vals[z]; + } + + return mod(v); + } + + template<typename val_t> + val_t interp(const std::vector<carve::geom2d::P2> &poly, + const std::vector<val_t> &vals, + double x, + double y) { + return interp(poly, vals, x, y, identity_t<val_t>()); + } + + class Interpolator { + public: + virtual void interpolate(const carve::mesh::MeshSet<3>::face_t *new_face, + const carve::mesh::MeshSet<3>::face_t *orig_face, + bool flipped) =0; + + Interpolator() { + } + + virtual ~Interpolator() { + } + + class Hook : public carve::csg::CSG::Hook { + Interpolator *interpolator; + public: + virtual void resultFace(const carve::mesh::MeshSet<3>::face_t *new_face, + const carve::mesh::MeshSet<3>::face_t *orig_face, + bool flipped) { + interpolator->interpolate(new_face, orig_face, flipped); + } + + Hook(Interpolator *_interpolator) : interpolator(_interpolator) { + } + + virtual ~Hook() { + } + }; + + void installHooks(carve::csg::CSG &csg) { + csg.hooks.registerHook(new Hook(this), carve::csg::CSG::Hooks::RESULT_FACE_BIT); + } + }; + + template<typename attr_t> + class FaceVertexAttr : public Interpolator { + + protected: + struct fv_hash { + size_t operator()(const std::pair<const carve::mesh::MeshSet<3>::face_t *, unsigned> &v) const { + return size_t(v.first) ^ size_t(v.second); + } + }; + + typedef std::unordered_map<const carve::mesh::MeshSet<3>::vertex_t *, attr_t> attrvmap_t; + typedef std::unordered_map<std::pair<const carve::mesh::MeshSet<3>::face_t *, unsigned>, attr_t, fv_hash> attrmap_t; + + attrmap_t attrs; + + public: + bool hasAttribute(const carve::mesh::MeshSet<3>::face_t *f, unsigned v) { + return attrs.find(std::make_pair(f, v)) != attrs.end(); + } + + attr_t getAttribute(const carve::mesh::MeshSet<3>::face_t *f, unsigned v, const attr_t &def = attr_t()) { + typename attrmap_t::const_iterator fv = attrs.find(std::make_pair(f, v)); + if (fv != attrs.end()) { + return (*fv).second; + } + return def; + } + + void setAttribute(const carve::mesh::MeshSet<3>::face_t *f, unsigned v, const attr_t &attr) { + attrs[std::make_pair(f, v)] = attr; + } + + virtual void interpolate(const carve::mesh::MeshSet<3>::face_t *new_face, + const carve::mesh::MeshSet<3>::face_t *orig_face, + bool flipped) { + std::vector<attr_t> vertex_attrs; + attrvmap_t base_attrs; + vertex_attrs.reserve(orig_face->nVertices()); + + for (carve::mesh::MeshSet<3>::face_t::const_edge_iter_t e = orig_face->begin(); e != orig_face->end(); ++e) { + typename attrmap_t::const_iterator a = attrs.find(std::make_pair(orig_face, e.idx())); + if (a == attrs.end()) return; + vertex_attrs.push_back((*a).second); + base_attrs[e->vert] = vertex_attrs.back(); + } + + for (carve::mesh::MeshSet<3>::face_t::const_edge_iter_t e = new_face->begin(); e != new_face->end(); ++e) { + const carve::mesh::MeshSet<3>::vertex_t *vertex = e->vert; + typename attrvmap_t::const_iterator b = base_attrs.find(vertex); + if (b != base_attrs.end()) { + attrs[std::make_pair(new_face, e.idx())] = (*b).second; + } else { + carve::geom2d::P2 p = orig_face->project(e->vert->v); + attr_t attr = interp(orig_face->begin(), + orig_face->end(), + orig_face->projector(), + vertex_attrs, + p.x, + p.y); + attrs[std::make_pair(new_face, e.idx())] = attr; + } + } + } + + FaceVertexAttr() : Interpolator() { + } + + virtual ~FaceVertexAttr() { + } + + }; + + + template<typename attr_t> + class FaceAttr : public Interpolator { + + protected: + struct f_hash { + size_t operator()(const carve::mesh::MeshSet<3>::face_t * const &f) const { + return size_t(f); + } + }; + + typedef std::unordered_map<const carve::mesh::MeshSet<3>::face_t *, attr_t, f_hash> attrmap_t; + + attrmap_t attrs; + + public: + bool hasAttribute(const carve::mesh::MeshSet<3>::face_t *f) { + return attrs.find(f) != attrs.end(); + } + + attr_t getAttribute(const carve::mesh::MeshSet<3>::face_t *f, const attr_t &def = attr_t()) { + typename attrmap_t::const_iterator i = attrs.find(f); + if (i != attrs.end()) { + return (*i).second; + } + return def; + } + + void setAttribute(const carve::mesh::MeshSet<3>::face_t *f, const attr_t &attr) { + attrs[f] = attr; + } + + virtual void interpolate(const carve::mesh::MeshSet<3>::face_t *new_face, + const carve::mesh::MeshSet<3>::face_t *orig_face, + bool flipped) { + typename attrmap_t::const_iterator i = attrs.find(orig_face); + if (i != attrs.end()) { + attrs[new_face] = (*i).second; + } + } + + FaceAttr() : Interpolator() { + } + + virtual ~FaceAttr() { + } + + }; + + } +} diff --git a/extern/carve/include/carve/intersection.hpp b/extern/carve/include/carve/intersection.hpp new file mode 100644 index 00000000000..1862a366abb --- /dev/null +++ b/extern/carve/include/carve/intersection.hpp @@ -0,0 +1,267 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> +#include <carve/collection_types.hpp> +#include <carve/iobj.hpp> + +namespace carve { + namespace csg { + + /** + * \class Intersections + * \brief Storage for computed intersections between vertices, edges and faces. + * + */ + struct Intersections : public std::unordered_map<IObj, IObjVMapSmall, IObj_hash> { + typedef carve::mesh::MeshSet<3>::vertex_t vertex_t; + typedef carve::mesh::MeshSet<3>::edge_t edge_t; + typedef carve::mesh::MeshSet<3>::face_t face_t; + + typedef std::unordered_map<IObj, IObjVMapSmall, IObj_hash> super; + + ~Intersections() { + } + + /** + * \brief Record the position of intersection between a pair of intersection objects. + * + * @param a The first intersecting object. + * @param b The second intersecting object. + * @param p The point of intersection. + */ + void record(IObj a, IObj b, vertex_t *p) { + if (a > b) std::swap(a, b); + (*this)[a][b] = p; + (*this)[b][a] = p; + } + + /** + * \brief Test whether vertex \a v intersects face \a f. + * + * @param v The vertex to test. + * @param f The face to test. + * + * @return true, if \a v intersects \a f. + */ + bool intersectsFace(vertex_t *v, face_t *f) const; + + /** + * \brief Collect sets of vertices, edges and faces that intersect \a obj + * + * @param[in] obj The intersection object to search for intersections. + * @param[out] collect_v A vector of vertices intersecting \a obj. + * @param[out] collect_e A vector of edges intersecting \a obj. + * @param[out] collect_f A vector of faces intersecting \a obj. + */ + void collect(const IObj &obj, + std::vector<vertex_t *> *collect_v, + std::vector<edge_t *> *collect_e, + std::vector<face_t *> *collect_f) const; + + + /** + * \brief Determine whether two intersection objects intersect. + * + * @param a The first intersection object. + * @param b The second intersection object. + * + * @return true, if \a a and \a b intersect. + */ + bool intersectsExactly(const IObj &a, const IObj &b) { + Intersections::const_iterator i = find(a); + if (i == end()) return false; + return i->second.find(b) != i->second.end(); + } + + /** + * \brief Determine whether an intersection object intersects a vertex. + * + * @param a The intersection object. + * @param v The vertex. + * + * @return true, if \a a and \a v intersect. + */ + bool intersects(const IObj &a, vertex_t *v) { + Intersections::const_iterator i = find(a); + if (i == end()) return false; + if (i->second.find(v) != i->second.end()) return true; + return false; + } + + /** + * \brief Determine whether an intersection object intersects an edge. + * + * @param a The intersection object. + * @param e The edge. + * + * @return true, if \a a and \a e intersect (either on the edge, + * or at either endpoint). + */ + bool intersects(const IObj &a, edge_t *e) { + Intersections::const_iterator i = find(a); + if (i == end()) return false; + for (super::data_type::const_iterator j = i->second.begin(); j != i->second.end(); ++j) { + const IObj &obj = j->first; + switch (obj.obtype) { + case IObj::OBTYPE_VERTEX: + if (obj.vertex == e->v1() || obj.vertex == e->v2()) return true; + break; + case IObj::OBTYPE_EDGE: + if (obj.edge == e) return true; + break; + default: + break; + } + } + return false; + } + + /** + * \brief Determine whether an intersection object intersects a face. + * + * @param a The intersection object. + * @param f The face. + * + * @return true, if \a a and \a f intersect (either on the face, + * or at any associated edge or vertex). + */ + bool intersects(const IObj &a, face_t *f) { + Intersections::const_iterator i = find(a); + if (i == end()) return false; + if (i->second.find(f) != i->second.end()) return true; + edge_t *e = f->edge; + do { + if (i->second.find(e) != i->second.end()) return true; + if (i->second.find(e->vert) != i->second.end()) return true; + e = e->next; + } while (e != f->edge); + return false; + } + + /** + * \brief Determine whether an edge intersects another edge. + * + * @param e The edge. + * @param f The face. + * + * @return true, if \a e and \a f intersect. + */ + bool intersects(edge_t *e1, edge_t *e2) { + if (intersects(e1->v1(), e2) || intersects(e1->v2(), e2) || intersects(IObj(e1), e2)) return true; + return false; + } + + /** + * \brief Determine whether an edge intersects a face. + * + * @param e The edge. + * @param f The face. + * + * @return true, if \a e and \a f intersect. + */ + bool intersects(edge_t *e, face_t *f) { + if (intersects(e->v1(), f) || intersects(e->v2(), f) || intersects(IObj(e), f)) return true; + return false; + } + + /** + * \brief Determine the faces intersected by an edge. + * + * @tparam face_set_t A collection type holding face_t * + * @param[in] e The edge. + * @param[out] f The resulting set of faces. + */ + template<typename face_set_t> + void intersectedFaces(edge_t *e, face_set_t &f) const { + std::vector<face_t *> intersected_faces; + std::vector<edge_t *> intersected_edges; + std::vector<vertex_t *> intersected_vertices; + + collect(e, &intersected_vertices, &intersected_edges, &intersected_faces); + + for (unsigned i = 0; i < intersected_vertices.size(); ++i) { + facesForVertex(intersected_vertices[i], f); + } + for (unsigned i = 0; i < intersected_edges.size(); ++i) { + facesForEdge(intersected_edges[i], f); + } + f.insert(intersected_faces.begin(), intersected_faces.end()); + } + + /** + * \brief Determine the faces intersected by a vertex. + * + * @tparam face_set_t A collection type holding face_t * + * @param[in] v The vertex. + * @param[out] f The resulting set of faces. + */ + template<typename face_set_t> + void intersectedFaces(vertex_t *v, face_set_t &f) const { + std::vector<face_t *> intersected_faces; + std::vector<edge_t *> intersected_edges; + std::vector<vertex_t *> intersected_vertices; + + collect(v, &intersected_vertices, &intersected_edges, &intersected_faces); + + for (unsigned i = 0; i < intersected_vertices.size(); ++i) { + facesForVertex(intersected_vertices[i], f); + } + for (unsigned i = 0; i < intersected_edges.size(); ++i) { + facesForEdge(intersected_edges[i], f); + } + f.insert(intersected_faces.begin(), intersected_faces.end()); + } + + /** + * \brief Collect the set of faces that contain all vertices in \a verts. + * + * @tparam vertex_set_t A collection type holding vertex_t * + * @tparam face_set_t A collection type holding face_t * + * @param[in] verts A set of vertices. + * @param[out] result The resulting set of faces. + */ + template<typename vertex_set_t, typename face_set_t> + void commonFaces(const vertex_set_t &verts, face_set_t &result) { + + std::set<face_t *> ifaces, temp, out; + typename vertex_set_t::const_iterator i = verts.begin(); + if (i == verts.end()) return; + intersectedFaces((*i), ifaces); + while (++i != verts.end()) { + temp.clear(); + intersectedFaces((*i), temp); + + out.clear(); + std::set_intersection(temp.begin(), temp.end(), + ifaces.begin(), ifaces.end(), + set_inserter(out)); + ifaces.swap(out); + } + std::copy(ifaces.begin(), ifaces.end(), set_inserter(result)); + } + + void clear() { + super::clear(); + } + + }; + + } +} diff --git a/extern/carve/include/carve/iobj.hpp b/extern/carve/include/carve/iobj.hpp new file mode 100644 index 00000000000..13d88ec820b --- /dev/null +++ b/extern/carve/include/carve/iobj.hpp @@ -0,0 +1,106 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +namespace carve { + namespace csg { + struct IObj { + enum { + OBTYPE_NONE = 0, + OBTYPE_VERTEX = 1, + OBTYPE_EDGE = 2, + OBTYPE_FACE = 4 + } obtype; + + union { + carve::mesh::MeshSet<3>::vertex_t *vertex; + carve::mesh::MeshSet<3>::edge_t *edge; + carve::mesh::MeshSet<3>::face_t *face; + intptr_t val; + }; + + IObj() : obtype(OBTYPE_NONE), val(0) { } + IObj(carve::mesh::MeshSet<3>::vertex_t *v) : obtype(OBTYPE_VERTEX), vertex(v) { } + IObj(carve::mesh::MeshSet<3>::edge_t *e) : obtype(OBTYPE_EDGE), edge(e) { } + IObj(carve::mesh::MeshSet<3>::face_t *f) : obtype(OBTYPE_FACE), face(f) { } + char typeChar() const { return "NVExF"[obtype]; } + }; + + + + struct IObj_hash { + inline size_t operator()(const IObj &i) const { + return (size_t)i.val; + } + inline size_t operator()(const std::pair<const IObj, const IObj> &i) const { + return (size_t)i.first.val ^ (size_t)i.second.val; + } + }; + + + + typedef std::unordered_set<std::pair<const IObj, const IObj>, IObj_hash> IObjPairSet; + + typedef std::unordered_map<IObj, carve::mesh::MeshSet<3>::vertex_t *, IObj_hash> IObjVMap; + typedef std::map<IObj, carve::mesh::MeshSet<3>::vertex_t *> IObjVMapSmall; + + class VertexIntersections : + public std::unordered_map<carve::mesh::MeshSet<3>::vertex_t *, IObjPairSet> { + }; + + + + static inline bool operator==(const carve::csg::IObj &a, const carve::csg::IObj &b) { + return a.obtype == b.obtype && a.val == b.val; + } + + static inline bool operator!=(const carve::csg::IObj &a, const carve::csg::IObj &b) { + return a.obtype != b.obtype || a.val != b.val; + } + + static inline bool operator<(const carve::csg::IObj &a, const carve::csg::IObj &b) { + return a.obtype < b.obtype || (a.obtype == b.obtype && a.val < b.val); + } + + static inline bool operator<=(const carve::csg::IObj &a, const carve::csg::IObj &b) { + return a.obtype < b.obtype || (a.obtype == b.obtype && a.val <= b.val); + } + + static inline bool operator>(const carve::csg::IObj &a, const carve::csg::IObj &b) { + return a.obtype > b.obtype || (a.obtype == b.obtype && a.val > b.val); + } + + static inline bool operator>=(const carve::csg::IObj &a, const carve::csg::IObj &b) { + return a.obtype > b.obtype || (a.obtype == b.obtype && a.val >= b.val); + } + + static inline std::ostream &operator<<(std::ostream &o, const carve::csg::IObj &a) { + switch (a.obtype) { + case carve::csg::IObj::OBTYPE_NONE: o << "NONE{}"; break; + case carve::csg::IObj::OBTYPE_VERTEX: o << "VERT{" << a.vertex << "}"; break; + case carve::csg::IObj::OBTYPE_EDGE: o << "EDGE{" << a.edge << "}"; break; + case carve::csg::IObj::OBTYPE_FACE: o << "FACE{" << a.face << "}"; break; + } + return o; + } + + } +} + diff --git a/extern/carve/include/carve/kd_node.hpp b/extern/carve/include/carve/kd_node.hpp new file mode 100644 index 00000000000..f62584d50c2 --- /dev/null +++ b/extern/carve/include/carve/kd_node.hpp @@ -0,0 +1,308 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> +#include <carve/geom.hpp> +#include <carve/aabb.hpp> + +#include <queue> +#include <list> +#include <limits> + +namespace carve { + namespace geom { + template<unsigned ndim, + typename data_t, + typename inserter_t, + typename aabb_calc_t> + class kd_node { + kd_node(const kd_node &); + kd_node &operator=(const kd_node &); + + public: + kd_node *c_neg; + kd_node *c_pos; + kd_node *parent; + axis_pos splitpos; + + typedef vector<ndim> vector_t; + typedef std::list<data_t> container_t; + + container_t data; + + kd_node(kd_node *_parent = NULL) : c_neg(NULL), c_pos(NULL), parent(_parent), splitpos(0, 0.0) { + } + + ~kd_node() { + if (c_neg) delete c_neg; + if (c_pos) delete c_pos; + } + + template<typename visitor_t> + void closeNodes(const vector_t &p, double d, visitor_t &visit) const { + if (c_neg) { + double delta = splitpos.pos - p[splitpos.axis]; + if (delta <= d) c_neg->closeNodes(p, d, visit); + if (delta >= -d) c_pos->closeNodes(p, d, visit); + } else { + visit(this); + } + } + + void removeData(const data_t &d) { + typename container_t::iterator i = std::find(data.begin(), data.end(), d); + + if (i != data.end()) { + data.erase(i); + } + } + + void addData(const data_t &d) { + data.push_back(d); + } + + void insert(const data_t &data, inserter_t &inserter) { + inserter.insert(this, data); + } + + void insert(const data_t &data) { + inserter_t inserter; + insert(data, inserter); + } + + void remove(const data_t &data, inserter_t &inserter) { + inserter.remove(this, data); + } + + void remove(const data_t &data) { + inserter_t inserter; + remove(data, inserter); + } + + carve::geom::aabb<ndim> nodeAABB() const { + carve::geom::aabb<ndim> aabb; + if (c_neg) { + aabb = c_neg->nodeAABB(); + aabb.unionAABB(c_pos->nodeAABB()); + } else { + if (data.size()) { + typename container_t::const_iterator i = data.begin(); + aabb = aabb_calc_t()(*i); + while (i != data.end()) { + aabb.unionAABB(aabb_calc_t()(*i)); + ++i; + } + } + } + return aabb; + } + + bool split(axis_pos split_at, inserter_t &inserter) { + if (c_neg) { + // already split + return false; + } + + c_neg = new kd_node(this); + c_pos = new kd_node(this); + + // choose an axis and split point. + splitpos = split_at; + + carve::geom::aabb<ndim> aabb; + + if (splitpos.axis < 0 || + splitpos.axis >= ndim || + splitpos.pos == std::numeric_limits<double>::max()) { + // need an aabb + if (data.size()) { + typename container_t::const_iterator i = data.begin(); + aabb = aabb_calc_t()(*i); + while (i != data.end()) { + aabb.unionAABB(aabb_calc_t()(*i)); + ++i; + } + } + } + + if (splitpos.axis < 0 || splitpos.axis >= ndim) { + + // choose an axis; + + // if no axis was specified, force calculation of the split position. + splitpos.pos = std::numeric_limits<double>::max(); + + // choose the axis of the AABB with the biggest extent. + splitpos.axis = largestAxis(aabb.extent); + + if (parent && splitpos.axis == parent->splitpos.axis) { + // but don't choose the same axis as the parent node; + // choose the axis with the second greatest AABB extent. + double e = -1.0; + int a = -1; + for (unsigned i = 0; i < ndim; ++i) { + if (i == splitpos.axis) continue; + if (e < aabb.extent[i]) { a = i; e = aabb.extent[i]; } + } + if (a != -1) { + splitpos.axis = a; + } + } + } + + if (splitpos.pos == std::numeric_limits<double>::max()) { + carve::geom::vector<ndim> min = aabb.min(); + carve::geom::vector<ndim> max = aabb.max(); + splitpos.pos = aabb.pos.v[splitpos.axis]; + } + + inserter.propagate(this); + + return true; + } + + bool split(axis_pos split_at = axis_pos(-1, std::numeric_limits<double>::max())) { + inserter_t inserter; + return split(split_at, inserter); + } + + void splitn(int num, inserter_t &inserter) { + if (num <= 0) return; + if (!c_neg) { + split(inserter); + } + if (c_pos) c_pos->splitn(num-1, inserter); + if (c_neg) c_neg->splitn(num-1, inserter); + } + + void splitn(int num) { + inserter_t inserter; + splitn(num, inserter); + } + + template<typename split_t> + void splitn(int num, split_t splitter, inserter_t &inserter) { + if (num <= 0) return; + if (!c_neg) { + split(inserter, splitter(this)); + } + if (c_pos) c_pos->splitn(num-1, inserter, splitter); + if (c_neg) c_neg->splitn(num-1, inserter, splitter); + } + + template<typename split_t> + void splitn(int num, split_t splitter) { + inserter_t inserter; + splitn(num, splitter, inserter); + } + + template<typename pred_t> + void splitpred(pred_t pred, inserter_t &inserter, int depth = 0) { + if (!c_neg) { + axis_pos splitpos(-1, std::numeric_limits<double>::max()); + if (!pred(this, depth, splitpos)) return; + split(splitpos, inserter); + } + if (c_pos) c_pos->splitpred(pred, inserter, depth + 1); + if (c_neg) c_neg->splitpred(pred, inserter, depth + 1); + } + + template<typename pred_t> + void splitpred(pred_t pred, int depth = 0) { + inserter_t inserter; + splitpred(pred, inserter, depth); + } + + // distance_t must provide: + // double operator()(kd_node::data_t, vector<ndim>); + // double operator()(axis_pos, vector<ndim>); + template<typename distance_t> + struct near_point_query { + + // q_t - the priority queue value type. + // q_t.first: distance from object to query point. + // q_t.second: pointer to object + typedef std::pair<double, const typename kd_node::data_t *> q_t; + + // the queue priority should sort from smallest distance to largest, and on equal distance, by object pointer. + struct pcmp { + bool operator()(const q_t &a, const q_t &b) { + return (a.first > b.first) || ((a.first == b.first) && (a.second < b.second)); + } + }; + + vector<ndim> point; + const kd_node *node; + std::priority_queue<q_t, std::vector<q_t>, pcmp> pq; + + distance_t dist; + double dist_to_parent_split; + + void addToPQ(kd_node *node) { + if (node->c_neg) { + addToPQ(node->c_neg); + addToPQ(node->c_pos); + } else { + for (typename kd_node::container_t::const_iterator i = node->data.begin(); i != node->data.end(); ++i) { + double d = dist((*i), point); + pq.push(std::make_pair(d, &(*i))); + } + } + } + + const typename kd_node::data_t *next() { + while (1) { + if (pq.size()) { + q_t t = pq.top(); + if (!node->parent || t.first < dist_to_parent_split) { + pq.pop(); + return t.second; + } + } + + if (!node->parent) return NULL; + + if (node->parent->c_neg == node) { + addToPQ(node->parent->c_pos); + } else { + addToPQ(node->parent->c_neg); + } + + node = node->parent; + dist_to_parent_split = dist(node->splitpos, point); + } + } + + near_point_query(const vector<ndim> _point, const kd_node *_node) : point(_point), node(_node), pq(), dist() { + while (node->c_neg) { + node = (point[node->axis] < node->pos) ? node->c_neg : node->c_pos; + } + if (node->parent) { + dist_to_parent_split = dist(node->parent->splitpos, point); + } else { + dist_to_parent_split = HUGE_VAL; + } + addToPQ(node); + } + }; + + }; + + } +} diff --git a/extern/carve/include/carve/math.hpp b/extern/carve/include/carve/math.hpp new file mode 100644 index 00000000000..ec9ff0a9663 --- /dev/null +++ b/extern/carve/include/carve/math.hpp @@ -0,0 +1,60 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/math_constants.hpp> + +#include <math.h> + +namespace carve { + namespace geom { + template<unsigned ndim> struct vector; + } +} + +namespace carve { + namespace math { + struct Matrix3; + int cubic_roots(double c3, double c2, double c1, double c0, double *roots); + + void eigSolveSymmetric(const Matrix3 &m, + double &l1, carve::geom::vector<3> &e1, + double &l2, carve::geom::vector<3> &e2, + double &l3, carve::geom::vector<3> &e3); + + void eigSolve(const Matrix3 &m, double &l1, double &l2, double &l3); + + static inline bool ZERO(double x) { return fabs(x) < carve::EPSILON; } + + static inline double radians(double deg) { return deg * M_PI / 180.0; } + static inline double degrees(double rad) { return rad * 180.0 / M_PI; } + + static inline double ANG(double x) { + return (x < 0) ? x + M_TWOPI : x; + } + + template<typename T> + static inline const T &clamp(const T &val, const T &min, const T &max) { + if (val < min) return min; + if (val > max) return max; + return val; + } + } +} diff --git a/extern/carve/include/carve/math_constants.hpp b/extern/carve/include/carve/math_constants.hpp new file mode 100644 index 00000000000..9d2c4fe0a46 --- /dev/null +++ b/extern/carve/include/carve/math_constants.hpp @@ -0,0 +1,33 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <math.h> + +#ifndef M_SQRT_3 +#define M_SQRT_3 1.73205080756887729352 +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifndef M_TWOPI +#define M_TWOPI (M_PI + M_PI) +#endif + diff --git a/extern/carve/include/carve/matrix.hpp b/extern/carve/include/carve/matrix.hpp new file mode 100644 index 00000000000..feb0cd72b97 --- /dev/null +++ b/extern/carve/include/carve/matrix.hpp @@ -0,0 +1,262 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <cstring> + +#include <carve/carve.hpp> + +#include <carve/math.hpp> +#include <carve/geom.hpp> + +namespace carve { + namespace math { + + struct Quaternion { + double x, y, z, w; + + Quaternion(double _x, double _y, double _z, double _w) : x(_x), y(_y), z(_z), w(_w) { + } + + Quaternion(double angle, const carve::geom::vector<3> &axis) { + double s = axis.length(); + if (!carve::math::ZERO(s)) { + double c = 1.0 / s; + double omega = -0.5 * angle; + s = sin(omega); + x = axis.x * c * s; + y = axis.y * c * s; + z = axis.z * c * s; + w = cos(omega); + normalize(); + } else { + x = y = z = 0.0; + w = 1.0; + } + } + + double lengthSquared() const { + return x * x + y * y + z * z + w * w; + } + + double length() const { + return sqrt(lengthSquared()); + } + + Quaternion normalized() const { + return Quaternion(*this).normalize(); + } + + Quaternion &normalize() { + double l = length(); + if (l == 0.0) { + x = 1.0; y = 0.0; z = 0.0; w = 0.0; + } else { + x /= l; y /= l; z /= l; w /= l; + } + return *this; + } + }; + + struct Matrix3 { + // access: .m[col][row], .v[col * 4 + row], ._cr + union { + double m[3][3]; + double v[9]; + struct { + // transposed + double _11, _12, _13; + double _21, _22, _23; + double _31, _32, _33; + }; + }; + Matrix3(double __11, double __21, double __31, + double __12, double __22, double __32, + double __13, double __23, double __33) { + // nb, args are row major, storage is column major. + _11 = __11; _12 = __12; _13 = __13; + _21 = __21; _22 = __22; _23 = __23; + _31 = __31; _32 = __32; _33 = __33; + } + Matrix3(double _m[3][3]) { + std::memcpy(m, _m, sizeof(m)); + } + Matrix3(double _v[9]) { + std::memcpy(v, _v, sizeof(v)); + } + Matrix3() { + _11 = 1.00; _12 = 0.00; _13 = 0.00; + _21 = 0.00; _22 = 1.00; _23 = 0.00; + _31 = 0.00; _32 = 0.00; _33 = 1.00; + } + }; + + struct Matrix { + // access: .m[col][row], .v[col * 4 + row], ._cr + union { + double m[4][4]; + double v[16]; + struct { + // transposed + double _11, _12, _13, _14; + double _21, _22, _23, _24; + double _31, _32, _33, _34; + double _41, _42 ,_43, _44; + }; + }; + Matrix(double __11, double __21, double __31, double __41, + double __12, double __22, double __32, double __42, + double __13, double __23, double __33, double __43, + double __14, double __24, double __34, double __44) { + // nb, args are row major, storage is column major. + _11 = __11; _12 = __12; _13 = __13; _14 = __14; + _21 = __21; _22 = __22; _23 = __23; _24 = __24; + _31 = __31; _32 = __32; _33 = __33; _34 = __34; + _41 = __41; _42 = __42; _43 = __43; _44 = __44; + } + Matrix(double _m[4][4]) { + std::memcpy(m, _m, sizeof(m)); + } + Matrix(double _v[16]) { + std::memcpy(v, _v, sizeof(v)); + } + Matrix() { + _11 = 1.00; _12 = 0.00; _13 = 0.00; _14 = 0.00; + _21 = 0.00; _22 = 1.00; _23 = 0.00; _24 = 0.00; + _31 = 0.00; _32 = 0.00; _33 = 1.00; _34 = 0.00; + _41 = 0.00; _42 = 0.00; _43 = 0.00; _44 = 1.00; + } + + static Matrix ROT(const Quaternion &q) { + const double w = q.w; + const double x = q.x; + const double y = q.y; + const double z = q.z; + return Matrix(1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0.0, + 2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0.0, + 2*x*z - 2*y*w, 2*y*z + 2*x*w, 1 - 2*x*x - 2*y*y, 0.0, + 0.0, 0.0, 0.0, 1.0); + } + static Matrix ROT(double angle, const carve::geom::vector<3> &axis) { + return ROT(Quaternion(angle, axis)); + } + static Matrix ROT(double angle, double x, double y, double z) { + return ROT(Quaternion(angle, carve::geom::VECTOR(x, y, z))); + } + static Matrix TRANS(double x, double y, double z) { + return Matrix(1.0, 0.0, 0.0, x, + 0.0, 1.0, 0.0, y, + 0.0, 0.0, 1.0, z, + 0.0, 0.0, 0.0, 1.0); + } + static Matrix TRANS(const carve::geom::vector<3> &v) { + return TRANS(v.x, v.y, v.z); + } + static Matrix SCALE(double x, double y, double z) { + return Matrix(x, 0.0, 0.0, 0.0, + 0.0, y, 0.0, 0.0, + 0.0, 0.0, z, 0.0, + 0.0, 0.0, 0.0, 1.0); + } + static Matrix SCALE(const carve::geom::vector<3> &v) { + return SCALE(v.x, v.y, v.z); + } + static Matrix IDENT() { + return Matrix(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0); + } + }; + + static inline bool operator==(const Matrix &A, const Matrix &B) { + for (size_t i = 0; i < 16; ++i) if (A.v[i] != B.v[i]) return false; + return true; + } + static inline bool operator!=(const Matrix &A, const Matrix &B) { + return !(A == B); + } + static inline carve::geom::vector<3> operator*(const Matrix &A, const carve::geom::vector<3> &b) { + return carve::geom::VECTOR( + A._11 * b.x + A._21 * b.y + A._31 * b.z + A._41, + A._12 * b.x + A._22 * b.y + A._32 * b.z + A._42, + A._13 * b.x + A._23 * b.y + A._33 * b.z + A._43 + ); + } + + static inline carve::geom::vector<3> &operator*=(carve::geom::vector<3> &b, const Matrix &A) { + b = A * b; + return b; + } + + static inline carve::geom::vector<3> operator*(const Matrix3 &A, const carve::geom::vector<3> &b) { + return carve::geom::VECTOR( + A._11 * b.x + A._21 * b.y + A._31 * b.z, + A._12 * b.x + A._22 * b.y + A._32 * b.z, + A._13 * b.x + A._23 * b.y + A._33 * b.z + ); + } + + static inline carve::geom::vector<3> &operator*=(carve::geom::vector<3> &b, const Matrix3 &A) { + b = A * b; + return b; + } + + static inline Matrix operator*(const Matrix &A, const Matrix &B) { + Matrix c; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + c.m[i][j] = 0.0; + for (int k = 0; k < 4; k++) { + c.m[i][j] += A.m[k][j] * B.m[i][k]; + } + } + } + return c; + } + + static inline Matrix3 operator*(const Matrix3 &A, const Matrix3 &B) { + Matrix3 c; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + c.m[i][j] = 0.0; + for (int k = 0; k < 3; k++) { + c.m[i][j] += A.m[k][j] * B.m[i][k]; + } + } + } + return c; + } + + + + struct matrix_transformation { + Matrix matrix; + + matrix_transformation(const Matrix &_matrix) : matrix(_matrix) { + } + + carve::geom::vector<3> operator()(const carve::geom::vector<3> &vector) const { + return matrix * vector; + } + }; + + + + } +} diff --git a/extern/carve/include/carve/mesh.hpp b/extern/carve/include/carve/mesh.hpp new file mode 100644 index 00000000000..d4170e55133 --- /dev/null +++ b/extern/carve/include/carve/mesh.hpp @@ -0,0 +1,845 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/geom.hpp> +#include <carve/geom3d.hpp> +#include <carve/tag.hpp> +#include <carve/djset.hpp> +#include <carve/aabb.hpp> +#include <carve/rtree.hpp> + +#include <iostream> + +namespace carve { + namespace poly { + class Polyhedron; + } + + namespace mesh { + + + template<unsigned ndim> class Edge; + template<unsigned ndim> class Face; + template<unsigned ndim> class Mesh; + template<unsigned ndim> class MeshSet; + + + + // A Vertex may participate in several meshes. If the Mesh belongs + // to a MeshSet, then the vertices come from the vertex_storage + // member of the MeshSet. This allows one to construct one or more + // Meshes out of sets of connected faces (possibly using vertices + // from a variety of MeshSets, and other storage), then create a + // MeshSet from the Mesh(es), causing the vertices to be + // collected, cloned and repointed into the MeshSet. + + // Normally, in a half-edge structure, Vertex would have a member + // pointing to an incident edge, allowing the enumeration of + // adjacent faces and edges. Because we want to support vertex + // sharing between Meshes and groups of Faces, this is made more + // complex. If Vertex contained a list of incident edges, one from + // each disjoint face set, then this could be done (with the + // caveat that you'd need to pass in a Mesh pointer to the + // adjacency queries). However, it seems that this would + // unavoidably complicate the process of incorporating or removing + // a vertex into an edge. + + // In most cases it is expected that a vertex will be arrived at + // via an edge or face in the mesh (implicit or explicit) of + // interest, so not storing this information will not hurt, + // overly. + template<unsigned ndim> + class Vertex : public tagable { + public: + typedef carve::geom::vector<ndim> vector_t; + typedef MeshSet<ndim> owner_t; + typedef carve::geom::aabb<ndim> aabb_t; + + carve::geom::vector<ndim> v; + + Vertex(const vector_t &_v) : tagable(), v(_v) { + } + + Vertex() : tagable(), v() { + } + + aabb_t getAABB() const { + return aabb_t(v, carve::geom::vector<ndim>::ZERO()); + } + }; + + + + struct hash_vertex_pair { + template<unsigned ndim> + size_t operator()(const std::pair<Vertex<ndim> *, Vertex<ndim> *> &pair) const { + size_t r = (size_t)pair.first; + size_t s = (size_t)pair.second; + return r ^ ((s >> 16) | (s << 16)); + } + template<unsigned ndim> + size_t operator()(const std::pair<const Vertex<ndim> *, const Vertex<ndim> *> &pair) const { + size_t r = (size_t)pair.first; + size_t s = (size_t)pair.second; + return r ^ ((s >> 16) | (s << 16)); + } + }; + + + + struct vertex_distance { + template<unsigned ndim> + double operator()(const Vertex<ndim> &a, const Vertex<ndim> &b) const { + return carve::geom::distance(a.v, b.v); + } + + template<unsigned ndim> + double operator()(const Vertex<ndim> *a, const Vertex<ndim> *b) const { + return carve::geom::distance(a->v, b->v); + } + }; + + + + namespace detail { + template<typename list_t> struct list_iter_t; + template<typename list_t, typename mapping_t> struct mapped_list_iter_t; + } + + + + // The half-edge structure proper (Edge) is maintained by Face + // instances. Together with Face instances, the half-edge + // structure defines a simple mesh (either one or two faces + // incident on each edge). + template<unsigned ndim> + class Edge : public tagable { + public: + typedef Vertex<ndim> vertex_t; + typedef Face<ndim> face_t; + + vertex_t *vert; + face_t *face; + Edge *prev, *next, *rev; + + private: + static void _link(Edge *a, Edge *b) { + a->next = b; b->prev = a; + } + + static void _freeloop(Edge *s) { + Edge *e = s; + do { + Edge *n = e->next; + delete e; + e = n; + } while (e != s); + } + + static void _setloopface(Edge *s, face_t *f) { + Edge *e = s; + do { + e->face = f; + e = e->next; + } while (e != s); + } + + static size_t _looplen(Edge *s) { + Edge *e = s; + face_t *f = s->face; + size_t c = 0; + do { + ++c; + CARVE_ASSERT(e->rev->rev == e); + CARVE_ASSERT(e->next->prev == e); + CARVE_ASSERT(e->face == f); + e = e->next; + } while (e != s); + return c; + } + + public: + void validateLoop() { + Edge *e = this; + face_t *f = face; + size_t c = 0; + do { + ++c; + CARVE_ASSERT(e->rev == NULL || e->rev->rev == e); + CARVE_ASSERT(e->next == e || e->next->vert != e->vert); + CARVE_ASSERT(e->prev == e || e->prev->vert != e->vert); + CARVE_ASSERT(e->next->prev == e); + CARVE_ASSERT(e->prev->next == e); + CARVE_ASSERT(e->face == f); + e = e->next; + } while (e != this); + CARVE_ASSERT(f == NULL || c == f->n_edges); + } + + size_t loopLen() { + return _looplen(this); + } + + Edge *mergeFaces(); + + Edge *removeHalfEdge(); + + // Remove and delete this edge. + Edge *removeEdge(); + + // Unlink this edge from its containing edge loop. disconnect + // rev links. The rev links of the previous edge also change, as + // its successor vertex changes. + void unlink(); + + // Insert this edge into a loop before other. If edge was + // already in a loop, it needs to be removed first. + void insertBefore(Edge *other); + + // Insert this edge into a loop after other. If edge was + // already in a loop, it needs to be removed first. + void insertAfter(Edge *other); + + size_t loopSize() const; + + vertex_t *v1() { return vert; } + vertex_t *v2() { return next->vert; } + + const vertex_t *v1() const { return vert; } + const vertex_t *v2() const { return next->vert; } + + Edge *perimNext() const; + Edge *perimPrev() const; + + double length2() const { + return (v1()->v - v2()->v).length2(); + } + + double length() const { + return (v1()->v - v2()->v).length(); + } + + Edge(vertex_t *_vert, face_t *_face); + + ~Edge(); + }; + + + + // A Face contains a pointer to the beginning of the half-edge + // circular list that defines its boundary. + template<unsigned ndim> + class Face : public tagable { + public: + typedef Vertex<ndim> vertex_t; + typedef Edge<ndim> edge_t; + typedef Mesh<ndim> mesh_t; + + typedef typename Vertex<ndim>::vector_t vector_t; + typedef carve::geom::aabb<ndim> aabb_t; + typedef carve::geom::plane<ndim> plane_t; + typedef carve::geom::vector<2> (*project_t)(const vector_t &); + typedef vector_t (*unproject_t)(const carve::geom::vector<2> &, const plane_t &); + + struct vector_mapping { + typedef typename vertex_t::vector_t value_type; + + value_type operator()(const carve::geom::vector<ndim> &v) const { return v; } + value_type operator()(const carve::geom::vector<ndim> *v) const { return *v; } + value_type operator()(const Edge<ndim> &e) const { return e.vert->v; } + value_type operator()(const Edge<ndim> *e) const { return e->vert->v; } + value_type operator()(const Vertex<ndim> &v) const { return v.v; } + value_type operator()(const Vertex<ndim> *v) const { return v->v; } + }; + + struct projection_mapping { + typedef carve::geom::vector<2> value_type; + project_t proj; + projection_mapping(project_t _proj) : proj(_proj) { } + value_type operator()(const carve::geom::vector<ndim> &v) const { return proj(v); } + value_type operator()(const carve::geom::vector<ndim> *v) const { return proj(*v); } + value_type operator()(const Edge<ndim> &e) const { return proj(e.vert->v); } + value_type operator()(const Edge<ndim> *e) const { return proj(e->vert->v); } + value_type operator()(const Vertex<ndim> &v) const { return proj(v.v); } + value_type operator()(const Vertex<ndim> *v) const { return proj(v->v); } + }; + + edge_t *edge; + size_t n_edges; + mesh_t *mesh; + size_t id; + + plane_t plane; + project_t project; + unproject_t unproject; + + private: + Face &operator=(const Face &other); + + protected: + Face() : edge(NULL), n_edges(0), mesh(NULL), id(0), plane(), project(NULL), unproject(NULL) { + } + + Face(const Face &other) : + edge(NULL), n_edges(other.n_edges), mesh(NULL), id(other.id), + plane(other.plane), project(other.project), unproject(other.unproject) { + } + + project_t getProjector(bool positive_facing, int axis) const; + unproject_t getUnprojector(bool positive_facing, int axis) const; + + public: + typedef detail::list_iter_t<Edge<ndim> > edge_iter_t; + typedef detail::list_iter_t<const Edge<ndim> > const_edge_iter_t; + + edge_iter_t begin() { return edge_iter_t(edge, 0); } + edge_iter_t end() { return edge_iter_t(edge, n_edges); } + + const_edge_iter_t begin() const { return const_edge_iter_t(edge, 0); } + const_edge_iter_t end() const { return const_edge_iter_t(edge, n_edges); } + + bool containsPoint(const vector_t &p) const; + bool containsPointInProjection(const vector_t &p) const; + bool simpleLineSegmentIntersection( + const carve::geom::linesegment<ndim> &line, + vector_t &intersection) const; + IntersectionClass lineSegmentIntersection( + const carve::geom::linesegment<ndim> &line, + vector_t &intersection) const; + + aabb_t getAABB() const; + + bool recalc(); + + void clearEdges(); + + // build an edge loop in forward orientation from an iterator pair + template<typename iter_t> + void loopFwd(iter_t vbegin, iter_t vend); + + // build an edge loop in reverse orientation from an iterator pair + template<typename iter_t> + void loopRev(iter_t vbegin, iter_t vend); + + // initialize a face from an ordered list of vertices. + template<typename iter_t> + void init(iter_t begin, iter_t end); + + // initialization of a triangular face. + void init(vertex_t *a, vertex_t *b, vertex_t *c); + + // initialization of a quad face. + void init(vertex_t *a, vertex_t *b, vertex_t *c, vertex_t *d); + + void getVertices(std::vector<vertex_t *> &verts) const; + void getProjectedVertices(std::vector<carve::geom::vector<2> > &verts) const; + + projection_mapping projector() const { + return projection_mapping(project); + } + + std::pair<double, double> rangeInDirection(const vector_t &v, const vector_t &b) const { + edge_t *e = edge; + double lo, hi; + lo = hi = carve::geom::dot(v, e->vert->v - b); + e = e->next; + for (; e != edge; e = e->next) { + double d = carve::geom::dot(v, e->vert->v - b); + lo = std::min(lo, d); + hi = std::max(hi, d); + } + return std::make_pair(lo, hi); + } + + size_t nVertices() const { + return n_edges; + } + + size_t nEdges() const { + return n_edges; + } + + vector_t centroid() const; + + static Face *closeLoop(edge_t *open_edge); + + Face(edge_t *e) : edge(e), n_edges(0), mesh(NULL) { + do { + e->face = this; + n_edges++; + e = e->next; + } while (e != edge); + recalc(); + } + + Face(vertex_t *a, vertex_t *b, vertex_t *c) : edge(NULL), n_edges(0), mesh(NULL) { + init(a, b, c); + recalc(); + } + + Face(vertex_t *a, vertex_t *b, vertex_t *c, vertex_t *d) : edge(NULL), n_edges(0), mesh(NULL) { + init(a, b, c, d); + recalc(); + } + + template<typename iter_t> + Face(iter_t begin, iter_t end) : edge(NULL), n_edges(0), mesh(NULL) { + init(begin, end); + recalc(); + } + + template<typename iter_t> + Face *create(iter_t beg, iter_t end, bool reversed) const; + + Face *clone(const vertex_t *old_base, vertex_t *new_base, std::unordered_map<const edge_t *, edge_t *> &edge_map) const; + + void remove() { + edge_t *e = edge; + do { + if (e->rev) e->rev->rev = NULL; + e = e->next; + } while (e != edge); + } + + void invert() { + // We invert the direction of the edges of the face in this + // way so that the edge rev pointers (if any) are still + // correct. It is expected that invert() will be called on + // every other face in the mesh, too, otherwise everything + // will get messed up. + + { + // advance vertices. + edge_t *e = edge; + vertex_t *va = e->vert; + do { + e->vert = e->next->vert; + e = e->next; + } while (e != edge); + edge->prev->vert = va; + } + + { + // swap prev and next pointers. + edge_t *e = edge; + do { + edge_t *n = e->next; + std::swap(e->prev, e->next); + e = n; + } while (e != edge); + } + + plane.negate(); + + int da = carve::geom::largestAxis(plane.N); + + project = getProjector(plane.N.v[da] > 0, da); + unproject = getUnprojector(plane.N.v[da] > 0, da); + } + + void canonicalize(); + + ~Face() { + clearEdges(); + } + }; + + + + namespace detail { + class FaceStitcher { + typedef Vertex<3> vertex_t; + typedef Edge<3> edge_t; + typedef Face<3> face_t; + + typedef std::pair<const vertex_t *, const vertex_t *> vpair_t; + typedef std::list<edge_t *> edgelist_t; + typedef std::unordered_map<vpair_t, edgelist_t, carve::mesh::hash_vertex_pair> edge_map_t; + typedef std::unordered_map<const vertex_t *, std::set<const vertex_t *> > edge_graph_t; + + edge_map_t edges; + edge_map_t complex_edges; + + carve::djset::djset face_groups; + std::vector<bool> is_open; + + edge_graph_t edge_graph; + + struct EdgeOrderData { + size_t group_id; + bool is_reversed; + carve::geom::vector<3> face_dir; + edge_t *edge; + + EdgeOrderData(edge_t *_edge, size_t _group_id, bool _is_reversed) : + group_id(_group_id), + is_reversed(_is_reversed) { + if (is_reversed) { + face_dir = -(_edge->face->plane.N); + } else { + face_dir = (_edge->face->plane.N); + } + edge = _edge; + } + + struct TestGroups { + size_t fwd, rev; + + TestGroups(size_t _fwd, size_t _rev) : fwd(_fwd), rev(_rev) { + } + + bool operator()(const EdgeOrderData &eo) const { + return eo.group_id == (eo.is_reversed ? rev : fwd); + } + }; + + struct Cmp { + carve::geom::vector<3> edge_dir; + carve::geom::vector<3> base_dir; + + Cmp(const carve::geom::vector<3> &_edge_dir, + const carve::geom::vector<3> &_base_dir) : + edge_dir(_edge_dir), + base_dir(_base_dir) { + } + bool operator()(const EdgeOrderData &a, const EdgeOrderData &b) const; + }; + }; + + void extractConnectedEdges(std::vector<const vertex_t *>::iterator begin, + std::vector<const vertex_t *>::iterator end, + std::vector<std::vector<Edge<3> *> > &efwd, + std::vector<std::vector<Edge<3> *> > &erev); + + size_t faceGroupID(const Face<3> *face); + size_t faceGroupID(const Edge<3> *edge); + + void resolveOpenEdges(); + + void fuseEdges(std::vector<Edge<3> *> &fwd, + std::vector<Edge<3> *> &rev); + + void joinGroups(std::vector<std::vector<Edge<3> *> > &efwd, + std::vector<std::vector<Edge<3> *> > &erev, + size_t fwd_grp, + size_t rev_grp); + + void matchOrderedEdges(const std::vector<std::vector<EdgeOrderData> >::iterator begin, + const std::vector<std::vector<EdgeOrderData> >::iterator end, + std::vector<std::vector<Edge<3> *> > &efwd, + std::vector<std::vector<Edge<3> *> > &erev); + + void reorder(std::vector<EdgeOrderData> &ordering, size_t fwd_grp); + + void orderForwardAndReverseEdges(std::vector<std::vector<Edge<3> *> > &efwd, + std::vector<std::vector<Edge<3> *> > &erev, + std::vector<std::vector<EdgeOrderData> > &result); + + void edgeIncidentGroups(const vpair_t &e, + const edge_map_t &all_edges, + std::pair<std::set<size_t>, std::set<size_t> > &groups); + + void buildEdgeGraph(const edge_map_t &all_edges); + void extractPath(std::vector<const vertex_t *> &path); + void removePath(const std::vector<const vertex_t *> &path); + void matchSimpleEdges(); + void construct(); + + template<typename iter_t> + void initEdges(iter_t begin, iter_t end); + + template<typename iter_t> + void build(iter_t begin, iter_t end, std::vector<Mesh<3> *> &meshes); + + public: + template<typename iter_t> + void create(iter_t begin, iter_t end, std::vector<Mesh<3> *> &meshes); + }; + } + + + + // A Mesh is a connected set of faces. It may be open (some edges + // have NULL rev members), or closed. On destruction, a Mesh + // should free its Faces (which will in turn free Edges, but not + // Vertices). A Mesh is edge-connected, which is to say that each + // face in the mesh shares an edge with at least one other face in + // the mesh. Touching at a vertex is not sufficient. This means + // that the perimeter of an open mesh visits each vertex no more + // than once. + template<unsigned ndim> + class Mesh { + public: + typedef Vertex<ndim> vertex_t; + typedef Edge<ndim> edge_t; + typedef Face<ndim> face_t; + typedef carve::geom::aabb<ndim> aabb_t; + typedef MeshSet<ndim> meshset_t; + + std::vector<face_t *> faces; + + // open_edges is a vector of all the edges in the mesh that + // don't have a matching edge in the opposite direction. + std::vector<edge_t *> open_edges; + + // closed_edges is a vector of all the edges in the mesh that + // have a matching edge in the opposite direction, and whose + // address is lower than their counterpart. (i.e. for each pair + // of adjoining faces, one of the two half edges is stored in + // closed_edges). + std::vector<edge_t *> closed_edges; + + bool is_negative; + + meshset_t *meshset; + + protected: + Mesh(std::vector<face_t *> &_faces, + std::vector<edge_t *> &_open_edges, + std::vector<edge_t *> &_closed_edges, + bool _is_negative); + + public: + Mesh(std::vector<face_t *> &_faces); + + ~Mesh(); + + template<typename iter_t> + static void create(iter_t begin, iter_t end, std::vector<Mesh<ndim> *> &meshes); + + aabb_t getAABB() const { + return aabb_t(faces.begin(), faces.end()); + } + + bool isClosed() const { + return open_edges.size() == 0; + } + + bool isNegative() const { + return is_negative; + } + + double volume() const { + if (is_negative || !faces.size()) return 0.0; + + double vol = 0.0; + typename vertex_t::vector_t origin = faces[0]->edge->vert->v; + + for (size_t f = 0; f < faces.size(); ++f) { + face_t *face = faces[f]; + edge_t *e1 = face->edge; + for (edge_t *e2 = e1->next ;e2->next != e1; e2 = e2->next) { + vol += carve::geom3d::tetrahedronVolume(e1->vert->v, e2->vert->v, e2->next->vert->v, origin); + } + } + return vol; + } + + struct IsClosed { + bool operator()(const Mesh &mesh) const { return mesh.isClosed(); } + bool operator()(const Mesh *mesh) const { return mesh->isClosed(); } + }; + + struct IsNegative { + bool operator()(const Mesh &mesh) const { return mesh.isNegative(); } + bool operator()(const Mesh *mesh) const { return mesh->isNegative(); } + }; + + void cacheEdges(); + + void calcOrientation(); + + void recalc() { + for (size_t i = 0; i < faces.size(); ++i) faces[i]->recalc(); + calcOrientation(); + } + + void invert() { + for (size_t i = 0; i < faces.size(); ++i) { + faces[i]->invert(); + } + if (isClosed()) is_negative = !is_negative; + } + + Mesh *clone(const vertex_t *old_base, vertex_t *new_base) const; + }; + + // A MeshSet manages vertex storage, and a collection of meshes. + // It should be easy to turn a vertex pointer into its index in + // its MeshSet vertex_storage. + template<unsigned ndim> + class MeshSet { + MeshSet(); + MeshSet(const MeshSet &); + MeshSet &operator=(const MeshSet &); + + template<typename iter_t> + void _init_from_faces(iter_t begin, iter_t end); + + public: + typedef Vertex<ndim> vertex_t; + typedef Edge<ndim> edge_t; + typedef Face<ndim> face_t; + typedef Mesh<ndim> mesh_t; + typedef carve::geom::aabb<ndim> aabb_t; + + std::vector<vertex_t> vertex_storage; + std::vector<mesh_t *> meshes; + + public: + template<typename face_type> + struct FaceIter : public std::iterator<std::random_access_iterator_tag, face_type> { + typedef std::iterator<std::random_access_iterator_tag, face_type> super; + typedef typename super::difference_type difference_type; + + const MeshSet<ndim> *obj; + size_t mesh, face; + + FaceIter(const MeshSet<ndim> *_obj, size_t _mesh, size_t _face); + + void fwd(size_t n); + void rev(size_t n); + void adv(int n); + + FaceIter operator++(int) { FaceIter tmp = *this; tmp.fwd(1); return tmp; } + FaceIter operator+(int v) { FaceIter tmp = *this; tmp.adv(v); return tmp; } + FaceIter &operator++() { fwd(1); return *this; } + FaceIter &operator+=(int v) { adv(v); return *this; } + + FaceIter operator--(int) { FaceIter tmp = *this; tmp.rev(1); return tmp; } + FaceIter operator-(int v) { FaceIter tmp = *this; tmp.adv(-v); return tmp; } + FaceIter &operator--() { rev(1); return *this; } + FaceIter &operator-=(int v) { adv(-v); return *this; } + + difference_type operator-(const FaceIter &other) const; + + bool operator==(const FaceIter &other) const { + return obj == other.obj && mesh == other.mesh && face == other.face; + } + bool operator!=(const FaceIter &other) const { + return !(*this == other); + } + bool operator<(const FaceIter &other) const { + CARVE_ASSERT(obj == other.obj); + return mesh < other.mesh || (mesh == other.mesh && face < other.face); + } + bool operator>(const FaceIter &other) const { + return other < *this; + } + bool operator<=(const FaceIter &other) const { + return !(other < *this); + } + bool operator>=(const FaceIter &other) const { + return !(*this < other); + } + + face_type operator*() const { + return obj->meshes[mesh]->faces[face]; + } + }; + + typedef FaceIter<const face_t *> const_face_iter; + typedef FaceIter<face_t *> face_iter; + + face_iter faceBegin() { return face_iter(this, 0, 0); } + face_iter faceEnd() { return face_iter(this, meshes.size(), 0); } + + const_face_iter faceBegin() const { return const_face_iter(this, 0, 0); } + const_face_iter faceEnd() const { return const_face_iter(this, meshes.size(), 0); } + + aabb_t getAABB() const { + return aabb_t(meshes.begin(), meshes.end()); + } + + template<typename func_t> + void transform(func_t func) { + for (size_t i = 0; i < vertex_storage.size(); ++i) { + vertex_storage[i].v = func(vertex_storage[i].v); + } + for (size_t i = 0; i < meshes.size(); ++i) { + meshes[i]->recalc(); + } + } + + MeshSet(const std::vector<typename vertex_t::vector_t> &points, + size_t n_faces, + const std::vector<int> &face_indices); + + // Construct a mesh set from a set of disconnected faces. Takes + // posession of the face pointers. + MeshSet(std::vector<face_t *> &faces); + + MeshSet(std::list<face_t *> &faces); + + MeshSet(std::vector<vertex_t> &_vertex_storage, + std::vector<mesh_t *> &_meshes); + + // This constructor consolidates and rewrites vertex pointers in + // each mesh, repointing them to local storage. + MeshSet(std::vector<mesh_t *> &_meshes); + + MeshSet *clone() const; + + ~MeshSet(); + + bool isClosed() const { + for (size_t i = 0; i < meshes.size(); ++i) { + if (!meshes[i]->isClosed()) return false; + } + return true; + } + + + void invert() { + for (size_t i = 0; i < meshes.size(); ++i) { + meshes[i]->invert(); + } + } + + void collectVertices(); + + void canonicalize(); + }; + + + + carve::PointClass classifyPoint( + const carve::mesh::MeshSet<3> *meshset, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *face_rtree, + const carve::geom::vector<3> &v, + bool even_odd = false, + const carve::mesh::Mesh<3> *mesh = NULL, + const carve::mesh::Face<3> **hit_face = NULL); + + + + } + + + + mesh::MeshSet<3> *meshFromPolyhedron(const poly::Polyhedron *, int manifold_id); + poly::Polyhedron *polyhedronFromMesh(const mesh::MeshSet<3> *, int manifold_id); + + + +}; + +#include <carve/mesh_impl.hpp> diff --git a/extern/carve/include/carve/mesh_impl.hpp b/extern/carve/include/carve/mesh_impl.hpp new file mode 100644 index 00000000000..23b0a436573 --- /dev/null +++ b/extern/carve/include/carve/mesh_impl.hpp @@ -0,0 +1,1015 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/geom2d.hpp> +#include <carve/geom3d.hpp> +#include <carve/djset.hpp> + +#include <iostream> +#include <deque> + +#include <stddef.h> + +namespace carve { + namespace mesh { + + + + namespace detail { + template<typename list_t> + struct list_iter_t { + typedef std::bidirectional_iterator_tag iterator_category; + typedef list_t value_type; + typedef ptrdiff_t difference_type; + typedef value_type & reference; + typedef value_type * pointer; + + list_t *curr; + int pos; + + list_iter_t() { } + list_iter_t(list_t *_curr, int _pos) : curr(_curr), pos(_pos) { } + + list_iter_t operator++(int) { list_iter_t result(*this); ++pos; curr = curr->next; return result; } + list_iter_t operator--(int) { list_iter_t result(*this); --pos; curr = curr->prev; return result; } + + list_iter_t operator++() { ++pos; curr = curr->next; return *this; } + list_iter_t operator--() { --pos; curr = curr->prev; return *this; } + + bool operator==(const list_iter_t &other) const { return curr == other.curr && pos == other.pos; } + bool operator!=(const list_iter_t &other) const { return curr != other.curr || pos != other.pos; } + + reference operator*() { return *curr; } + pointer operator->() { return curr; } + + int idx() const { return pos; } + }; + } + + + + template<unsigned ndim> + Edge<ndim> *Edge<ndim>::mergeFaces() { + if (rev == NULL) return NULL; + + face_t *fwdface = face; + face_t *revface = rev->face; + + size_t n_removed = 0; + + Edge *splice_beg = this; + do { + splice_beg = splice_beg->prev; + ++n_removed; + } while (splice_beg != this && + splice_beg->rev && + splice_beg->next->rev->prev == splice_beg->rev); + + if (splice_beg == this) { + // edge loops are completely matched. + return NULL; + } + + Edge *splice_end = this; + do { + splice_end = splice_end->next; + ++n_removed; + } while (splice_end->rev && + splice_end->prev->rev->next == splice_end->rev); + + --n_removed; + + Edge *link1_p = splice_beg; + Edge *link1_n = splice_beg->next->rev->next; + + Edge *link2_p = splice_end->prev->rev->prev; + Edge *link2_n = splice_end; + + CARVE_ASSERT(link1_p->face == fwdface); + CARVE_ASSERT(link1_n->face == revface); + + CARVE_ASSERT(link2_p->face == revface); + CARVE_ASSERT(link2_n->face == fwdface); + + Edge *left_loop = link1_p->next; + + CARVE_ASSERT(left_loop->rev == link1_n->prev); + + _link(link2_n->prev, link1_p->next); + _link(link1_n->prev, link2_p->next); + + _link(link1_p, link1_n); + _link(link2_p, link2_n); + + fwdface->edge = link1_p; + + for (Edge *e = link1_n; e != link2_n; e = e->next) { + CARVE_ASSERT(e->face == revface); + e->face = fwdface; + fwdface->n_edges++; + } + for (Edge *e = link2_n; e != link1_n; e = e->next) { + CARVE_ASSERT(e->face == fwdface); + } + + fwdface->n_edges -= n_removed; + + revface->n_edges = 0; + revface->edge = NULL; + + _setloopface(left_loop, NULL); + _setloopface(left_loop->rev, NULL); + + return left_loop; + } + + + + template<unsigned ndim> + Edge<ndim> *Edge<ndim>::removeHalfEdge() { + Edge *n = NULL; + if (face) { + face->n_edges--; + } + + if (next == this) { + if (face) face->edge = NULL; + } else { + if (face && face->edge == this) face->edge = next; + next->prev = prev; + prev->next = next; + n = next; + } + delete this; + return n; + } + + + + template<unsigned ndim> + Edge<ndim> *Edge<ndim>::removeEdge() { + if (rev) { + rev->removeHalfEdge(); + } + return removeHalfEdge(); + } + + + + template<unsigned ndim> + void Edge<ndim>::unlink() { + if (rev) { rev->rev = NULL; rev = NULL; } + if (prev->rev) { prev->rev->rev = NULL; prev->rev = NULL; } + + if (face) { + face->n_edges--; + if (face->edge == this) face->edge = next; + face = NULL; + } + + next->prev = prev; + prev->next = next; + + prev = next = this; + } + + + + template<unsigned ndim> + void Edge<ndim>::insertBefore(Edge<ndim> *other) { + if (prev != this) unlink(); + prev = other->prev; + next = other; + next->prev = this; + prev->next = this; + + if (prev->rev) { prev->rev->rev = NULL; prev->rev = NULL; } + } + + + + template<unsigned ndim> + void Edge<ndim>::insertAfter(Edge<ndim> *other) { + if (prev != this) unlink(); + next = other->next; + prev = other; + next->prev = this; + prev->next = this; + + if (prev->rev) { prev->rev->rev = NULL; prev->rev = NULL; } + } + + + + template<unsigned ndim> + size_t Edge<ndim>::loopSize() const { + const Edge *e = this; + size_t n = 0; + do { e = e->next; ++n; } while (e != this); + return n; + } + + + + template<unsigned ndim> + Edge<ndim> *Edge<ndim>::perimNext() const { + if (rev) return NULL; + Edge *e = next; + while(e->rev) { + e = e->rev->next; + } + return e; + } + + + + template<unsigned ndim> + Edge<ndim> *Edge<ndim>::perimPrev() const { + if (rev) return NULL; + Edge *e = prev; + while(e->rev) { + e = e->rev->prev; + } + return e; + } + + + + template<unsigned ndim> + Edge<ndim>::Edge(vertex_t *_vert, face_t *_face) : + vert(_vert), face(_face), prev(NULL), next(NULL), rev(NULL) { + prev = next = this; + } + + + + template<unsigned ndim> + Edge<ndim>::~Edge() { + } + + + + template<unsigned ndim> + typename Face<ndim>::aabb_t Face<ndim>::getAABB() const { + aabb_t aabb; + aabb.fit(begin(), end(), vector_mapping()); + return aabb; + } + + + + template<unsigned ndim> + bool Face<ndim>::recalc() { + if (!carve::geom3d::fitPlane(begin(), end(), vector_mapping(), plane)) { + return false; + } + + int da = carve::geom::largestAxis(plane.N); + double A = carve::geom2d::signedArea(begin(), end(), projection_mapping(getProjector(false, da))); + + if ((A < 0.0) ^ (plane.N.v[da] < 0.0)) { + plane.negate(); + } + + project = getProjector(plane.N.v[da] > 0, da); + unproject = getUnprojector(plane.N.v[da] > 0, da); + + return true; + } + + + + template<unsigned ndim> + void Face<ndim>::clearEdges() { + if (!edge) return; + + edge_t *curr = edge; + do { + edge_t *next = curr->next; + delete curr; + curr = next; + } while (curr != edge); + + edge = NULL; + + n_edges = 0; + } + + + + template<unsigned ndim> + template<typename iter_t> + void Face<ndim>::loopFwd(iter_t begin, iter_t end) { + clearEdges(); + if (begin == end) return; + edge = new edge_t(*begin, this); ++n_edges; ++begin; + while (begin != end) { + edge_t *e = new edge_t(*begin, this); + e->insertAfter(edge->prev); + ++n_edges; + ++begin; + } + } + + + + template<unsigned ndim> + template<typename iter_t> + void Face<ndim>::loopRev(iter_t begin, iter_t end) { + clearEdges(); + if (begin == end) return; + edge = new edge_t(*begin, this); ++n_edges; ++begin; + while (begin != end) { + edge_t *e = new edge_t(*begin, this); + e->insertBefore(edge->next); + ++n_edges; + ++begin; + } + } + + + + template<unsigned ndim> + template<typename iter_t> + void Face<ndim>::init(iter_t begin, iter_t end) { + loopFwd(begin, end); + } + + + + template<unsigned ndim> + void Face<ndim>::init(vertex_t *a, vertex_t *b, vertex_t *c) { + clearEdges(); + edge_t *ea = new edge_t(a, this); + edge_t *eb = new edge_t(b, this); + edge_t *ec = new edge_t(c, this); + eb->insertAfter(ea); + ec->insertAfter(eb); + edge = ea; + n_edges = 3; + } + + + + template<unsigned ndim> + void Face<ndim>::init(vertex_t *a, vertex_t *b, vertex_t *c, vertex_t *d) { + clearEdges(); + edge_t *ea = new edge_t(a, this); + edge_t *eb = new edge_t(b, this); + edge_t *ec = new edge_t(c, this); + edge_t *ed = new edge_t(d, this); + eb->insertAfter(ea); + ec->insertAfter(eb); + ed->insertAfter(ec); + edge = ea; + n_edges = 4; + } + + + + template<unsigned ndim> + void Face<ndim>::getVertices(std::vector<vertex_t *> &verts) const { + verts.clear(); + verts.reserve(n_edges); + const edge_t *e = edge; + do { verts.push_back(e->vert); e = e->next; } while (e != edge); + } + + + + template<unsigned ndim> + void Face<ndim>::getProjectedVertices(std::vector<carve::geom::vector<2> > &verts) const { + verts.clear(); + verts.reserve(n_edges); + const edge_t *e = edge; + do { verts.push_back(project(e->vert->v)); e = e->next; } while (e != edge); + } + + + + template<unsigned ndim> + typename Face<ndim>::vector_t Face<ndim>::centroid() const { + vector_t v; + edge_t *e = edge; + do { + v += e->vert->v; + e = e->next; + } while(e != edge); + v /= n_edges; + return v; + } + + + + template<unsigned ndim> + void Face<ndim>::canonicalize() { + edge_t *min = edge; + edge_t *e = edge; + + do { + if (e->vert < min->vert) min = e; + e = e->next; + } while (e != edge); + + edge = min; + } + + + + template<unsigned ndim> + template<typename iter_t> + Face<ndim> *Face<ndim>::create(iter_t beg, iter_t end, bool reversed) const { + Face *r = new Face(); + + if (reversed) { + r->loopRev(beg, end); + r->plane = -plane; + } else { + r->loopFwd(beg, end); + r->plane = plane; + } + + int da = carve::geom::largestAxis(r->plane.N); + + r->project = r->getProjector(r->plane.N.v[da] > 0, da); + r->unproject = r->getUnprojector(r->plane.N.v[da] > 0, da); + + return r; + } + + + + template<unsigned ndim> + Face<ndim> *Face<ndim>::clone(const vertex_t *old_base, + vertex_t *new_base, + std::unordered_map<const edge_t *, edge_t *> &edge_map) const { + Face *r = new Face(*this); + + edge_t *e = edge; + edge_t *r_p = NULL; + edge_t *r_e; + do { + r_e = new edge_t(e->vert - old_base + new_base, r); + edge_map[e] = r_e; + if (r_p) { + r_p->next = r_e; + r_e->prev = r_p; + } else { + r->edge = r_e; + } + r_p = r_e; + + if (e->rev) { + typename std::unordered_map<const edge_t *, edge_t *>::iterator rev_i = edge_map.find(e->rev); + if (rev_i != edge_map.end()) { + r_e->rev = (*rev_i).second; + (*rev_i).second->rev = r_e; + } + } + + e = e->next; + } while (e != edge); + r_e->next = r->edge; + r->edge->prev = r_e; + return r; + } + + + + template<unsigned ndim> + Mesh<ndim>::Mesh(std::vector<face_t *> &_faces, + std::vector<edge_t *> &_open_edges, + std::vector<edge_t *> &_closed_edges, + bool _is_negative) { + std::swap(faces, _faces); + std::swap(open_edges, _open_edges); + std::swap(closed_edges, _closed_edges); + is_negative = _is_negative; + meshset = NULL; + + for (size_t i = 0; i < faces.size(); ++i) { + faces[i]->mesh = this; + } + } + + + + namespace detail { + template<typename iter_t> + void FaceStitcher::initEdges(iter_t begin, + iter_t end) { + size_t c = 0; + for (iter_t i = begin; i != end; ++i) { + face_t *face = *i; + CARVE_ASSERT(face->mesh == NULL); // for the moment, can only insert a face into a mesh once. + + face->id = c++; + edge_t *e = face->edge; + do { + edges[vpair_t(e->v1(), e->v2())].push_back(e); + e = e->next; + if (e->rev) { e->rev->rev = NULL; e->rev = NULL; } + } while (e != face->edge); + } + face_groups.init(c); + is_open.clear(); + is_open.resize(c, false); + } + + template<typename iter_t> + void FaceStitcher::build(iter_t begin, + iter_t end, + std::vector<Mesh<3> *> &meshes) { + // work out what set each face belongs to, and then construct + // mesh instances for each set of faces. + std::vector<size_t> index_set; + std::vector<size_t> set_size; + face_groups.get_index_to_set(index_set, set_size); + + std::vector<std::vector<face_t *> > mesh_faces; + mesh_faces.resize(set_size.size()); + for (size_t i = 0; i < set_size.size(); ++i) { + mesh_faces[i].reserve(set_size[i]); + } + + for (iter_t i = begin; i != end; ++i) { + face_t *face = *i; + mesh_faces[index_set[face->id]].push_back(face); + } + + meshes.clear(); + meshes.reserve(mesh_faces.size()); + for (size_t i = 0; i < mesh_faces.size(); ++i) { + meshes.push_back(new Mesh<3>(mesh_faces[i])); + } + } + + template<typename iter_t> + void FaceStitcher::create(iter_t begin, + iter_t end, + std::vector<Mesh<3> *> &meshes) { + initEdges(begin, end); + construct(); + build(begin, end, meshes); + } + } + + + + template<unsigned ndim> + void Mesh<ndim>::cacheEdges() { + closed_edges.clear(); + open_edges.clear(); + + for (size_t i = 0; i < faces.size(); ++i) { + face_t *face = faces[i]; + edge_t *e = face->edge; + do { + if (e->rev == NULL) { + open_edges.push_back(e); + } else if (e < e->rev) { + closed_edges.push_back(e); + } + e = e->next; + } while (e != face->edge); + } + } + + + + template<unsigned ndim> + Mesh<ndim>::Mesh(std::vector<face_t *> &_faces) : faces(), open_edges(), closed_edges(), meshset(NULL) { + faces.swap(_faces); + for (size_t i = 0; i < faces.size(); ++i) { + faces[i]->mesh = this; + } + cacheEdges(); + calcOrientation(); + } + + + + template<unsigned ndim> + void Mesh<ndim>::calcOrientation() { + if (open_edges.size() || !closed_edges.size()) { + is_negative = false; + } else { + edge_t *emin = closed_edges[0]; + if (emin->rev->v1()->v < emin->v1()->v) emin = emin->rev; + for (size_t i = 1; i < closed_edges.size(); ++i) { + if (closed_edges[i]->v1()->v < emin->v1()->v) emin = closed_edges[i]; + if (closed_edges[i]->rev->v1()->v < emin->v1()->v) emin = closed_edges[i]->rev; + } + + std::vector<face_t *> min_faces; + edge_t *e = emin; + do { + min_faces.push_back(e->face); + CARVE_ASSERT(e->rev != NULL); + e = e->rev->next; + CARVE_ASSERT(e->v1() == emin->v1()); + CARVE_ASSERT(e->v1()->v < e->v2()->v); + CARVE_ASSERT(e->v1()->v.x <= e->v2()->v.x); + } while (e != emin); + + double max_abs_x = 0.0; + for (size_t f = 0; f < min_faces.size(); ++f) { + if (fabs(min_faces[f]->plane.N.x) > fabs(max_abs_x)) max_abs_x = min_faces[f]->plane.N.x; + } + is_negative = max_abs_x > 0.0; + } + } + + + + template<unsigned ndim> + Mesh<ndim> *Mesh<ndim>::clone(const vertex_t *old_base, + vertex_t *new_base) const { + std::vector<face_t *> r_faces; + std::vector<edge_t *> r_open_edges; + std::vector<edge_t *> r_closed_edges; + std::unordered_map<const edge_t *, edge_t *> edge_map; + + r_faces.reserve(faces.size()); + r_open_edges.reserve(r_open_edges.size()); + r_closed_edges.reserve(r_closed_edges.size()); + + for (size_t i = 0; i < faces.size(); ++i) { + r_faces.push_back(faces[i]->clone(old_base, new_base, edge_map)); + } + for (size_t i = 0; i < closed_edges.size(); ++i) { + r_closed_edges.push_back(edge_map[closed_edges[i]]); + r_closed_edges.back()->rev = edge_map[closed_edges[i]->rev]; + } + for (size_t i = 0; i < open_edges.size(); ++i) { + r_open_edges.push_back(edge_map[open_edges[i]]); + } + + return new Mesh(r_faces, r_open_edges, r_closed_edges, is_negative); + } + + + + template<unsigned ndim> + Mesh<ndim>::~Mesh() { + for (size_t i = 0; i < faces.size(); ++i) { + delete faces[i]; + } + } + + + + template<unsigned ndim> + template<typename iter_t> + void Mesh<ndim>::create(iter_t begin, iter_t end, std::vector<Mesh<ndim> *> &meshes) { + meshes.clear(); + } + + + + template<> + template<typename iter_t> + void Mesh<3>::create(iter_t begin, iter_t end, std::vector<Mesh<3> *> &meshes) { + detail::FaceStitcher().create(begin, end, meshes); + } + + + + template<unsigned ndim> + template<typename iter_t> + void MeshSet<ndim>::_init_from_faces(iter_t begin, iter_t end) { + typedef std::unordered_map<const vertex_t *, size_t> map_t; + map_t vmap; + + for (iter_t i = begin; i != end; ++i) { + face_t *f = *i; + edge_t *e = f->edge; + do { + typename map_t::const_iterator j = vmap.find(e->vert); + if (j == vmap.end()) { + size_t idx = vmap.size(); + vmap[e->vert] = idx; + } + e = e->next; + } while (e != f->edge); + } + + vertex_storage.resize(vmap.size()); + for (typename map_t::const_iterator i = vmap.begin(); i != vmap.end(); ++i) { + vertex_storage[(*i).second].v = (*i).first->v; + } + + for (iter_t i = begin; i != end; ++i) { + face_t *f = *i; + edge_t *e = f->edge; + do { + e->vert = &vertex_storage[vmap[e->vert]]; + e = e->next; + } while (e != f->edge); + } + + mesh_t::create(begin, end, meshes); + + for (size_t i = 0; i < meshes.size(); ++i) { + meshes[i]->meshset = this; + } + } + + + + template<unsigned ndim> + MeshSet<ndim>::MeshSet(const std::vector<typename MeshSet<ndim>::vertex_t::vector_t> &points, + size_t n_faces, + const std::vector<int> &face_indices) { + vertex_storage.reserve(points.size()); + std::vector<face_t *> faces; + faces.reserve(n_faces); + for (size_t i = 0; i < points.size(); ++i) { + vertex_storage.push_back(vertex_t(points[i])); + } + + std::vector<vertex_t *> v; + size_t p = 0; + for (size_t i = 0; i < n_faces; ++i) { + const size_t N = face_indices[p++]; + v.clear(); + v.reserve(N); + for (size_t j = 0; j < N; ++j) { + v.push_back(&vertex_storage[face_indices[p++]]); + } + faces.push_back(new face_t(v.begin(), v.end())); + } + CARVE_ASSERT(p == face_indices.size()); + mesh_t::create(faces.begin(), faces.end(), meshes); + + for (size_t i = 0; i < meshes.size(); ++i) { + meshes[i]->meshset = this; + } + } + + + + template<unsigned ndim> + MeshSet<ndim>::MeshSet(std::vector<face_t *> &faces) { + _init_from_faces(faces.begin(), faces.end()); + } + + + + template<unsigned ndim> + MeshSet<ndim>::MeshSet(std::list<face_t *> &faces) { + _init_from_faces(faces.begin(), faces.end()); + } + + + + template<unsigned ndim> + MeshSet<ndim>::MeshSet(std::vector<vertex_t> &_vertex_storage, + std::vector<mesh_t *> &_meshes) { + vertex_storage.swap(_vertex_storage); + meshes.swap(_meshes); + + for (size_t i = 0; i < meshes.size(); ++i) { + meshes[i]->meshset = this; + } + } + + + + template<unsigned ndim> + MeshSet<ndim>::MeshSet(std::vector<typename MeshSet<ndim>::mesh_t *> &_meshes) { + meshes.swap(_meshes); + std::unordered_map<vertex_t *, size_t> vert_idx; + + for (size_t m = 0; m < meshes.size(); ++m) { + mesh_t *mesh = meshes[m]; + CARVE_ASSERT(mesh->meshset == NULL); + mesh->meshset = this; + for (size_t f = 0; f < mesh->faces.size(); ++f) { + face_t *face = mesh->faces[f]; + edge_t *edge = face->edge; + do { + vert_idx[edge->vert] = 0; + edge = edge->next; + } while (edge != face->edge); + } + } + + vertex_storage.reserve(vert_idx.size()); + for (typename std::unordered_map<vertex_t *, size_t>::iterator i = vert_idx.begin(); i != vert_idx.end(); ++i) { + (*i).second = vertex_storage.size(); + vertex_storage.push_back(*(*i).first); + } + + for (size_t m = 0; m < meshes.size(); ++m) { + mesh_t *mesh = meshes[m]; + for (size_t f = 0; f < mesh->faces.size(); ++f) { + face_t *face = mesh->faces[f]; + edge_t *edge = face->edge; + do { + size_t i = vert_idx[edge->vert]; + edge->vert = &vertex_storage[i]; + edge = edge->next; + } while (edge != face->edge); + } + } + } + + + + template<unsigned ndim> + MeshSet<ndim> *MeshSet<ndim>::clone() const { + std::vector<vertex_t> r_vertex_storage = vertex_storage; + std::vector<mesh_t *> r_meshes; + for (size_t i = 0; i < meshes.size(); ++i) { + r_meshes.push_back(meshes[i]->clone(&vertex_storage[0], &r_vertex_storage[0])); + } + + return new MeshSet(r_vertex_storage, r_meshes); + } + + + + template<unsigned ndim> + MeshSet<ndim>::~MeshSet() { + for (size_t i = 0; i < meshes.size(); ++i) { + delete meshes[i]; + } + } + + + + template<unsigned ndim> + template<typename face_type> + MeshSet<ndim>::FaceIter<face_type>::FaceIter(const MeshSet<ndim> *_obj, size_t _mesh, size_t _face) : obj(_obj), mesh(_mesh), face(_face) { + } + + + + template<unsigned ndim> + template<typename face_type> + void MeshSet<ndim>::FaceIter<face_type>::fwd(size_t n) { + if (mesh < obj->meshes.size()) { + face += n; + while (face >= obj->meshes[mesh]->faces.size()) { + face -= obj->meshes[mesh++]->faces.size(); + if (mesh == obj->meshes.size()) { face = 0; break; } + } + } + } + + + + template<unsigned ndim> + template<typename face_type> + void MeshSet<ndim>::FaceIter<face_type>::rev(size_t n) { + while (n > face) { + n -= face; + if (mesh == 0) { face = 0; return; } + face = obj->meshes[--mesh]->faces.size() - 1; + } + face -= n; + } + + + + template<unsigned ndim> + template<typename face_type> + void MeshSet<ndim>::FaceIter<face_type>::adv(int n) { + if (n > 0) { + fwd((size_t)n); + } else if (n < 0) { + rev((size_t)-n); + } + } + + + + template<unsigned ndim> + template<typename face_type> + typename MeshSet<ndim>::template FaceIter<face_type>::difference_type + MeshSet<ndim>::FaceIter<face_type>::operator-(const FaceIter &other) const { + CARVE_ASSERT(obj == other.obj); + if (mesh == other.mesh) return face - other.face; + + size_t m = 0; + for (size_t i = std::min(mesh, other.mesh) + 1; i < std::max(mesh, other.mesh); ++i) { + m += obj->meshes[i]->faces.size(); + } + + if (mesh < other.mesh) { + return -(difference_type)((obj->meshes[mesh]->faces.size() - face) + m + other.face); + } else { + return +(difference_type)((obj->meshes[other.mesh]->faces.size() - other.face) + m + face); + } + } + + + + template<typename order_t> + struct VPtrSort { + order_t order; + + VPtrSort(const order_t &_order = order_t()) : order(_order) {} + + template<unsigned ndim> + bool operator()(carve::mesh::Vertex<ndim> *a, + carve::mesh::Vertex<ndim> *b) const { + return order(a->v, b->v); + } + }; + + + + template<unsigned ndim> + void MeshSet<ndim>::collectVertices() { + std::unordered_map<vertex_t *, size_t> vert_idx; + + for (size_t m = 0; m < meshes.size(); ++m) { + mesh_t *mesh = meshes[m]; + + for (size_t f = 0; f < mesh->faces.size(); ++f) { + face_t *face = mesh->faces[f]; + edge_t *edge = face->edge; + do { + vert_idx[edge->vert] = 0; + edge = edge->next; + } while (edge != face->edge); + } + } + + std::vector<vertex_t> new_vertex_storage; + new_vertex_storage.reserve(vert_idx.size()); + for (typename std::unordered_map<vertex_t *, size_t>::iterator + i = vert_idx.begin(); i != vert_idx.end(); ++i) { + (*i).second = new_vertex_storage.size(); + new_vertex_storage.push_back(*(*i).first); + } + + for (size_t m = 0; m < meshes.size(); ++m) { + mesh_t *mesh = meshes[m]; + for (size_t f = 0; f < mesh->faces.size(); ++f) { + face_t *face = mesh->faces[f]; + edge_t *edge = face->edge; + do { + size_t i = vert_idx[edge->vert]; + edge->vert = &new_vertex_storage[i]; + edge = edge->next; + } while (edge != face->edge); + } + } + + std::swap(vertex_storage, new_vertex_storage); + } + + + + template<unsigned ndim> + void MeshSet<ndim>::canonicalize() { + std::vector<vertex_t *> vptr; + std::vector<vertex_t *> vmap; + std::vector<vertex_t> vout; + const size_t N = vertex_storage.size(); + + vptr.reserve(N); + vout.reserve(N); + vmap.resize(N); + + for (size_t i = 0; i != N; ++i) { + vptr.push_back(&vertex_storage[i]); + } + std::sort(vptr.begin(), vptr.end(), VPtrSort<std::less<typename vertex_t::vector_t> >()); + + for (size_t i = 0; i != N; ++i) { + vout.push_back(*vptr[i]); + vmap[vptr[i] - &vertex_storage[0]] = &vout[i]; + } + + for (face_iter i = faceBegin(); i != faceEnd(); ++i) { + for (typename face_t::edge_iter_t j = (*i)->begin(); j != (*i)->end(); ++j) { + (*j).vert = vmap[(*j).vert - &vertex_storage[0]]; + } + (*i)->canonicalize(); + } + + vertex_storage.swap(vout); + } + + } +} diff --git a/extern/carve/include/carve/mesh_ops.hpp b/extern/carve/include/carve/mesh_ops.hpp new file mode 100644 index 00000000000..02b1bde4e45 --- /dev/null +++ b/extern/carve/include/carve/mesh_ops.hpp @@ -0,0 +1,975 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/mesh.hpp> + +#include <iostream> +#include <fstream> + +namespace carve { + namespace mesh { + namespace detail { + // make a triangle out of three edges. + template<unsigned ndim> + void link(Edge<ndim> *e1, Edge<ndim> *e2, Edge<ndim> *e3, Face<ndim> *f = NULL) { + e1->next = e2; e2->next = e3; e3->next = e1; + e3->prev = e2; e2->prev = e1; e1->prev = e3; + e1->face = e2->face = e3->face = f; + if (f) { + f->edge = e1; + f->recalc(); + } + } + + + + template<unsigned ndim, typename proj_t> + double loopArea(carve::mesh::Edge<ndim> *edge, proj_t proj) { + double A = 0.0; + carve::mesh::Edge<3> *e = edge; + do { + carve::geom2d::P2 p1 = proj(e->vert->v); + carve::geom2d::P2 p2 = proj(e->next->vert->v); + A += (p2.y + p1.y) * (p2.x - p1.x); + e = e->next; + } while (e != edge); + return A / 2.0; + } + + + + template<unsigned ndim, typename proj_t> + struct TriangulationData { + typedef Edge<ndim> edge_t; + + struct VertexInfo { + double score; + carve::geom2d::P2 p; + bool convex; + bool failed; + VertexInfo *next, *prev; + edge_t *edge; + + VertexInfo(edge_t *_edge, + const carve::geom2d::P2 &_p) : + score(0.0), p(_p), convex(false), failed(false), next(NULL), prev(NULL), edge(_edge) { + } + + bool isCandidate() const { + return convex && !failed; + } + + void fail() { + failed = true; + } + + static bool isLeft(const VertexInfo *a, const VertexInfo *b, const geom2d::P2 &p) { + if (a < b) { + return carve::geom2d::orient2d(a->p, b->p, p) > 0.0; + } else { + return carve::geom2d::orient2d(b->p, a->p, p) < 0.0; + } + } + + // is the ear prev->edge->next convex? + bool testForConvexVertex() const { + return isLeft(next, prev, p); + } + + static double triScore(const geom2d::P2 &a, const geom2d::P2 &b, const geom2d::P2 &c) { + // score is in the range: [0, 1] + // equilateral triangles score 1 + // sliver triangles score 0 + double dab = (a - b).length(); + double dbc = (b - c).length(); + double dca = (c - a).length(); + + if (dab < 1e-10 || dbc < 1e-10 || dca < 1e-10) return 0.0; + + return std::max(std::min((dab + dbc) / dca, std::min((dab + dca) / dbc, (dbc + dca) / dab)) - 1.0, 0.0); + } + + // calculate a score for the ear edge. + double calcScore() const { + double this_tri = triScore(prev->p, p, next->p); + double next_tri = triScore(prev->p, next->p, next->next->p); + double prev_tri = triScore(prev->prev->p, prev->p, next->p); + + return this_tri + std::max(next_tri, prev_tri) * .2; + } + + void recompute() { + convex = testForConvexVertex(); + failed = false; + if (convex) { + score = calcScore(); + } else { + score = -1e-5; + } + } + + static bool inTriangle(const VertexInfo *a, + const VertexInfo *b, + const VertexInfo *c, + const geom2d::P2 &e) { + return !isLeft(b, a, e) && !isLeft(c, b, e) && !isLeft(a, c, e); + } + + + bool isClipable() const { + for (const VertexInfo *v_test = next->next; v_test != prev; v_test = v_test->next) { + if (v_test->convex) { + continue; + } + + if (v_test->p == prev->p || v_test->p == next->p) { + continue; + } + + if (v_test->p == p) { + if (v_test->next->p == prev->p && v_test->prev->p == next->p) { + return false; + } + + if (v_test->next->p == prev->p || v_test->prev->p == next->p) { + continue; + } + } + + if (inTriangle(prev, this, next, v_test->p)) { + return false; + } + } + return true; + } + }; + + struct order_by_score { + bool operator()(const VertexInfo *a, const VertexInfo *b) const { + return a->score < b->score; + } + }; + + typedef std::pair<VertexInfo *, VertexInfo *> diag_t; + + proj_t proj; + + geom2d::P2 P(const VertexInfo *vi) const { + return vi->p; + } + + geom2d::P2 P(const edge_t *edge) const { + return proj(edge->vert->v); + } + + bool isLeft(const edge_t *a, const edge_t *b, const geom2d::P2 &p) const { + if (a < b) { + return carve::geom2d::orient2d(P(a), P(b), p) > 0.0; + } else { + return carve::geom2d::orient2d(P(b), P(a), p) < 0.0; + } + } + + bool testForConvexVertex(const edge_t *vert) const { + return isLeft(vert->next, vert->prev, P(vert)); + } + + bool inCone(const VertexInfo *vert, const geom2d::P2 &p) const { + return geom2d::internalToAngle(P(vert->next), P(vert), P(vert->prev), p); + } + + int windingNumber(VertexInfo *vert, const carve::geom2d::P2 &point) const { + int wn = 0; + + VertexInfo *v = vert; + geom2d::P2 v_p = P(vert); + do { + geom2d::P2 n_p = P(v->next); + + if (v_p.y <= point.y) { + if (n_p.y > point.y && carve::geom2d::orient2d(v_p, n_p, point) > 0.0) { + ++wn; + } + } else { + if (n_p.y <= point.y && carve::geom2d::orient2d(v_p, n_p, point) < 0.0) { + --wn; + } + } + v = v->next; + v_p = n_p; + } while (v != vert); + + return wn; + } + + bool diagonalIsCandidate(diag_t diag) const { + VertexInfo *v1 = diag.first; + VertexInfo *v2 = diag.second; + return (inCone(v1, P(v2)) && inCone(v2, P(v1))); + } + + bool testDiagonal(diag_t diag) const { + // test whether v1-v2 is a valid diagonal. + VertexInfo *v1 = diag.first; + VertexInfo *v2 = diag.second; + geom2d::P2 v1p = P(v1); + geom2d::P2 v2p = P(v2); + + bool intersected = false; + + for (VertexInfo *t = v1->next; !intersected && t != v1->prev; t = t->next) { + VertexInfo *u = t->next; + if (t == v2 || u == v2) continue; + + geom2d::P2 tp = P(t); + geom2d::P2 up = P(u); + + double l_a1 = carve::geom2d::orient2d(v1p, v2p, tp); + double l_a2 = carve::geom2d::orient2d(v1p, v2p, up); + + double l_b1 = carve::geom2d::orient2d(tp, up, v1p); + double l_b2 = carve::geom2d::orient2d(tp, up, v2p); + + if (l_a1 > l_a2) std::swap(l_a1, l_a2); + if (l_b1 > l_b2) std::swap(l_b1, l_b2); + + if (l_a1 == 0.0 && l_a2 == 0.0 && + l_b1 == 0.0 && l_b2 == 0.0) { + // colinear + if (std::max(tp.x, up.x) >= std::min(v1p.x, v2p.x) && std::min(tp.x, up.x) <= std::max(v1p.x, v2p.x)) { + // colinear and intersecting + intersected = true; + } + continue; + } + + if (l_a2 <= 0.0 || l_a1 >= 0.0 || l_b2 <= 0.0 || l_b1 >= 0.0) { + // no intersection + continue; + } + + intersected = true; + } + + if (!intersected) { + // test whether midpoint winding == 1 + + carve::geom2d::P2 mid = (v1p + v2p) / 2; + if (windingNumber(v1, mid) == 1) { + // this diagonal is ok + return true; + } + } + return false; + } + + // Find the vertex half way around the loop (rounds upwards). + VertexInfo *findMidpoint(VertexInfo *vert) const { + VertexInfo *v = vert; + VertexInfo *r = vert; + while (1) { + r = r->next; + v = v->next; if (v == vert) return r; + v = v->next; if (v == vert) return r; + } + } + + // Test all diagonals with a separation of a-b by walking both + // pointers around the loop. In the case where a-b divides the + // loop exactly in half, this will test each diagonal twice, + // but avoiding this case is not worth the extra effort + // required. + diag_t scanDiagonals(VertexInfo *a, VertexInfo *b) const { + VertexInfo *v1 = a; + VertexInfo *v2 = b; + + do { + diag_t d(v1, v2); + if (diagonalIsCandidate(d) && testDiagonal(d)) { + return d; + } + v1 = v1->next; + v2 = v2->next; + } while (v1 != a); + + return diag_t(NULL, NULL); + } + + diag_t scanAllDiagonals(VertexInfo *a) const { + // Rationale: We want to find a diagonal that splits the + // loop into two as evenly as possible, to reduce the number + // of times that diagonal splitting is required. Start by + // scanning all diagonals separated by loop_len / 2, then + // decrease the separation until we find something. + + // loops of length 2 or 3 have no possible diagonal. + if (a->next == a || a->next->next == a) return diag_t(NULL, NULL); + + VertexInfo *b = findMidpoint(a); + while (b != a->next) { + diag_t d = scanDiagonals(a, b); + if (d != diag_t(NULL, NULL)) return d; + b = b->prev; + } + + return diag_t(NULL, NULL); + } + + diag_t findDiagonal(VertexInfo *vert) const { + return scanAllDiagonals(vert); + } + + diag_t findHighScoringDiagonal(VertexInfo *vert) const { + typedef std::pair<double, diag_t> heap_entry_t; + VertexInfo *v1, *v2; + std::vector<heap_entry_t> heap; + size_t loop_len = 0; + + v1 = vert; + do { + ++loop_len; + v1 = v1->next; + } while (v1 != vert); + + v1 = vert; + do { + v2 = v1->next->next; + size_t dist = 2; + do { + if (diagonalIsCandidate(diag_t(v1, v2))) { + double score = std::min(dist, loop_len - dist); + // double score = (v1->edge->vert->v - v2->edge->vert->v).length2(); + heap.push_back(heap_entry_t(score, diag_t(v1, v2))); + } + v2 = v2->next; + ++dist; + } while (v2 != vert && v2 != v1->prev); + v1 = v1->next; + } while (v1->next->next != vert); + + std::make_heap(heap.begin(), heap.end()); + + while (heap.size()) { + std::pop_heap(heap.begin(), heap.end()); + heap_entry_t h = heap.back(); + heap.pop_back(); + + if (testDiagonal(h.second)) return h.second; + } + + // couldn't find a diagonal that was ok. + return diag_t(NULL, NULL); + } + + void splitEdgeLoop(VertexInfo *v1, VertexInfo *v2) { + VertexInfo *v1_copy = new VertexInfo(new Edge<ndim>(v1->edge->vert, NULL), v1->p); + VertexInfo *v2_copy = new VertexInfo(new Edge<ndim>(v2->edge->vert, NULL), v2->p); + + v1_copy->edge->rev = v2_copy->edge; + v2_copy->edge->rev = v1_copy->edge; + + v1_copy->edge->prev = v1->edge->prev; + v1_copy->edge->next = v2->edge; + + v2_copy->edge->prev = v2->edge->prev; + v2_copy->edge->next = v1->edge; + + v1->edge->prev->next = v1_copy->edge; + v1->edge->prev = v2_copy->edge; + + v2->edge->prev->next = v2_copy->edge; + v2->edge->prev = v1_copy->edge; + + v1_copy->prev = v1->prev; + v1_copy->next = v2; + + v2_copy->prev = v2->prev; + v2_copy->next = v1; + + v1->prev->next = v1_copy; + v1->prev = v2_copy; + + v2->prev->next = v2_copy; + v2->prev = v1_copy; + } + + VertexInfo *findDegenerateEar(VertexInfo *edge) { + VertexInfo *v = edge; + + if (v->next == v || v->next->next == v) return NULL; + + do { + if (P(v) == P(v->next)) { + return v; + } else if (P(v) == P(v->next->next)) { + if (P(v->next) == P(v->next->next->next)) { + // a 'z' in the loop: z (a) b a b c -> remove a-b-a -> z (a) a b c -> remove a-a-b (next loop) -> z a b c + // z --(a)-- b + // / + // / + // a -- b -- d + return v->next; + } else { + // a 'shard' in the loop: z (a) b a c d -> remove a-b-a -> z (a) a b c d -> remove a-a-b (next loop) -> z a b c d + // z --(a)-- b + // / + // / + // a -- c -- d + // n.b. can only do this if the shard is pointing out of the polygon. i.e. b is outside z-a-c + if (!carve::geom2d::internalToAngle(P(v->next->next->next), P(v), P(v->prev), P(v->next))) { + return v->next; + } + } + } + v = v->next; + } while (v != edge); + + return NULL; + } + + // Clip off a vertex at vert, producing a triangle (with appropriate rev pointers) + template<typename out_iter_t> + VertexInfo *clipEar(VertexInfo *vert, out_iter_t out) { + CARVE_ASSERT(testForConvexVertex(vert->edge)); + + edge_t *p_edge = vert->edge->prev; + edge_t *n_edge = vert->edge->next; + + edge_t *p_copy = new edge_t(p_edge->vert, NULL); + edge_t *n_copy = new edge_t(n_edge->vert, NULL); + + n_copy->next = p_copy; + n_copy->prev = vert->edge; + + p_copy->next = vert->edge; + p_copy->prev = n_copy; + + vert->edge->next = n_copy; + vert->edge->prev = p_copy; + + p_edge->next = n_edge; + n_edge->prev = p_edge; + + if (p_edge->rev) { + p_edge->rev->rev = p_copy; + } + p_copy->rev = p_edge->rev; + + p_edge->rev = n_copy; + n_copy->rev = p_edge; + + *out++ = vert->edge; + + if (vert->edge->face) { + if (vert->edge->face->edge == vert->edge) { + vert->edge->face->edge = n_edge; + } + vert->edge->face->n_edges--; + vert->edge->face = NULL; + } + + vert->next->prev = vert->prev; + vert->prev->next = vert->next; + + VertexInfo *n = vert->next; + delete vert; + return n; + } + + template<typename out_iter_t> + size_t removeDegeneracies(VertexInfo *&begin, out_iter_t out) { + VertexInfo *v; + size_t count = 0; + + while ((v = findDegenerateEar(begin)) != NULL) { + begin = clipEar(v, out); + ++count; + } + return count; + } + + template<typename out_iter_t> + bool splitAndResume(VertexInfo *begin, out_iter_t out) { + diag_t diag; + + diag = findDiagonal(begin); + if (diag == diag_t(NULL, NULL)) { + std::cerr << "failed to find diagonal" << std::endl; + return false; + } + + // add a splitting edge between v1 and v2. + VertexInfo *v1 = diag.first; + VertexInfo *v2 = diag.second; + + splitEdgeLoop(v1, v2); + + v1->recompute(); + v1->next->recompute(); + + v2->recompute(); + v2->next->recompute(); + +#if defined(CARVE_DEBUG) + dumpPoly(v1->edge, v2->edge); +#endif + +#if defined(CARVE_DEBUG) + CARVE_ASSERT(!checkSelfIntersection(v1)); + CARVE_ASSERT(!checkSelfIntersection(v2)); +#endif + + bool r1 = doTriangulate(v1, out); + bool r2 = doTriangulate(v2, out); + + return r1 && r2; + } + + template<typename out_iter_t> + bool doTriangulate(VertexInfo *begin, out_iter_t out); + + TriangulationData(proj_t _proj) : proj(_proj) { + } + + VertexInfo *init(edge_t *begin) { + edge_t *e = begin; + VertexInfo *head = NULL, *tail = NULL, *v; + do { + VertexInfo *v = new VertexInfo(e, proj(e->vert->v)); + if (tail != NULL) { + tail->next = v; + v->prev = tail; + } else { + head = v; + } + tail = v; + + e = e->next; + } while (e != begin); + tail->next = head; + head->prev = tail; + + v = head; + do { + v->recompute(); + v = v->next; + } while (v != head); + return head; + } + + class EarQueue { + TriangulationData &data; + std::vector<VertexInfo *> queue; + + void checkheap() { +#ifdef __GNUC__ + CARVE_ASSERT(std::__is_heap(queue.begin(), queue.end(), order_by_score())); +#endif + } + + public: + EarQueue(TriangulationData &_data) : data(_data), queue() { + } + + size_t size() const { + return queue.size(); + } + + void push(VertexInfo *v) { +#if defined(CARVE_DEBUG) + checkheap(); +#endif + queue.push_back(v); + std::push_heap(queue.begin(), queue.end(), order_by_score()); + } + + VertexInfo *pop() { +#if defined(CARVE_DEBUG) + checkheap(); +#endif + std::pop_heap(queue.begin(), queue.end(), order_by_score()); + VertexInfo *v = queue.back(); + queue.pop_back(); + return v; + } + + void remove(VertexInfo *v) { +#if defined(CARVE_DEBUG) + checkheap(); +#endif + CARVE_ASSERT(std::find(queue.begin(), queue.end(), v) != queue.end()); + double score = v->score; + if (v != queue[0]) { + v->score = queue[0]->score + 1; + std::make_heap(queue.begin(), queue.end(), order_by_score()); + } + CARVE_ASSERT(v == queue[0]); + std::pop_heap(queue.begin(), queue.end(), order_by_score()); + CARVE_ASSERT(queue.back() == v); + queue.pop_back(); + v->score = score; + } + + void changeScore(VertexInfo *v, double s_from, double s_to) { +#if defined(CARVE_DEBUG) + checkheap(); +#endif + CARVE_ASSERT(std::find(queue.begin(), queue.end(), v) != queue.end()); + if (s_from != s_to) { + v->score = s_to; + std::make_heap(queue.begin(), queue.end(), order_by_score()); + } + } + + void update(VertexInfo *v) { + VertexInfo pre = *v; + v->recompute(); + VertexInfo post = *v; + + if (pre.isCandidate()) { + if (post.isCandidate()) { + changeScore(v, pre.score, post.score); + } else { + remove(v); + } + } else { + if (post.isCandidate()) { + push(v); + } + } + } + }; + + + bool checkSelfIntersection(const VertexInfo *vert) { + const VertexInfo *v1 = vert; + do { + const VertexInfo *v2 = vert->next->next; + do { + carve::geom2d::P2 a = v1->p; + carve::geom2d::P2 b = v1->next->p; + CARVE_ASSERT(a == proj(v1->edge->vert->v)); + CARVE_ASSERT(b == proj(v1->edge->next->vert->v)); + + carve::geom2d::P2 c = v2->p; + carve::geom2d::P2 d = v2->next->p; + CARVE_ASSERT(c == proj(v2->edge->vert->v)); + CARVE_ASSERT(d == proj(v2->edge->next->vert->v)); + + bool intersected = false; + if (a == c || a == d || b == c || b == d) { + } else { + intersected = true; + + double l_a1 = carve::geom2d::orient2d(a, b, c); + double l_a2 = carve::geom2d::orient2d(a, b, d); + if (l_a1 > l_a2) std::swap(l_a1, l_a2); + if (l_a2 <= 0.0 || l_a1 >= 0.0) { + intersected = false; + } + + double l_b1 = carve::geom2d::orient2d(c, d, a); + double l_b2 = carve::geom2d::orient2d(c, d, b); + if (l_b1 > l_b2) std::swap(l_b1, l_b2); + if (l_b2 <= 0.0 || l_b1 >= 0.0) { + intersected = false; + } + + if (l_a1 == 0.0 && l_a2 == 0.0 && l_b1 == 0.0 && l_b2 == 0.0) { + if (std::max(a.x, b.x) >= std::min(c.x, d.x) && std::min(a.x, b.x) <= std::max(c.x, d.x)) { + // colinear and intersecting. + } else { + // colinear but not intersecting. + intersected = false; + } + } + } + if (intersected) { + carve::geom2d::P2 p[4] = { a, b, c, d }; + carve::geom::aabb<2> A(p, p+4); + A.expand(5); + + std::cerr << "\ +<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\ +<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n\ +<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\ + x=\"" << A.min().x << "px\" y=\"" << A.min().y << "\"\n\ + width=\"" << A.extent.x * 2 << "\" height=\"" << A.extent.y * 2 << "\"\n\ + viewBox=\"" << A.min().x << " " << A.min().y << " " << A.max().x << " " << A.max().y << "\"\n\ + enable-background=\"new " << A.min().x << " " << A.min().y << " " << A.max().x << " " << A.max().y << "\"\n\ + xml:space=\"preserve\">\n\ +<line fill=\"none\" stroke=\"#000000\" x1=\"" << a.x << "\" y1=\"" << a.y << "\" x2=\"" << b.x << "\" y2=\"" << b.y << "\"/>\n\ +<line fill=\"none\" stroke=\"#000000\" x1=\"" << c.x << "\" y1=\"" << c.y << "\" x2=\"" << d.x << "\" y2=\"" << d.y << "\"/>\n\ +</svg>\n"; + return true; + } + v2 = v2->next; + } while (v2 != vert); + v1 = v1->next; + } while (v1 != vert); + return false; + } + + carve::geom::aabb<2> make2d(const edge_t *edge, std::vector<geom2d::P2> &points) { + const edge_t *e = edge; + do { + points.push_back(P(e)); + e = e->next; + } while(e != edge); + return carve::geom::aabb<2>(points.begin(), points.end()); + } + + void dumpLoop(std::ostream &out, + const std::vector<carve::geom2d::P2> &points, + const char *fill, + const char *stroke, + double stroke_width, + double offx, + double offy, + double scale + ) { + out << "<polygon fill=\"" << fill << "\" stroke=\"" << stroke << "\" stroke-width=\"" << stroke_width << "\" points=\""; + for (size_t i = 0; i < points.size(); ++i) { + if (i) out << ' '; + double x, y; + x = scale * (points[i].x - offx) + 5; + y = scale * (points[i].y - offy) + 5; + out << x << ',' << y; + } + out << "\" />" << std::endl; + } + + void dumpPoly(const edge_t *edge, const edge_t *edge2 = NULL, const char *pfx = "poly_") { + static int step = 0; + std::ostringstream filename; + filename << pfx << step++ << ".svg"; + std::cerr << "dumping to " << filename.str() << std::endl; + std::ofstream out(filename.str().c_str()); + + std::vector <geom2d::P2> points, points2; + + carve::geom::aabb<2> A = make2d(edge, points); + if (edge2) { + A.unionAABB(make2d(edge2, points2)); + } + A.expand(5); + + out << "\ +<?xml version=\"1.0\"?>\n\ +<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n\ +<svg\n\ + x=\"" << A.min().x << "px\" y=\"" << A.min().y << "\"\n\ + width=\"" << A.extent.x * 2 << "\" height=\"" << A.extent.y * 2 << "\"\n\ + viewBox=\"" << A.min().x << " " << A.min().y << " " << A.max().x << " " << A.max().y << "\"\n\ + enable-background=\"new " << A.min().x << " " << A.min().y << " " << A.max().x << " " << A.max().y << "\"\n\ + xml:space=\"preserve\">\n"; + + dumpLoop(out, points, "rgb(0,0,0)", "blue", 0.1, 0, 0, 1); + if (points2.size()) dumpLoop(out, points2, "rgb(255,0,0)", "blue", 0.1, 0, 0, 1); + + out << "</svg>" << std::endl; + } + }; + + template<unsigned ndim, typename proj_t> + template<typename out_iter_t> + bool TriangulationData<ndim, proj_t>::doTriangulate(VertexInfo *begin, out_iter_t out) { + EarQueue vq(*this); + +#if defined(CARVE_DEBUG) + dumpPoly(begin->edge, NULL, "input_"); + CARVE_ASSERT(!checkSelfIntersection(begin)); +#endif + + VertexInfo *v = begin, *n, *p; + size_t remain = 0; + do { + if (v->isCandidate()) vq.push(v); + v = v->next; + remain++; + } while (v != begin); + + while (remain > 3 && vq.size()) { + { static int __c = 0; if (++__c % 50 == 0) { break; } } + v = vq.pop(); + if (!v->isClipable()) { + v->fail(); + continue; + } + + continue_clipping: + n = clipEar(v, out); + p = n->prev; + begin = n; + if (--remain == 3) break; + // if (checkSelfIntersection(begin)) { + // dumpPoly(begin->edge, NULL, "badclip_"); + // CARVE_ASSERT(!!!"clip created self intersection"); + // } + + vq.update(n); + vq.update(p); + + if (n->score < p->score) { std::swap(n, p); } + if (n->score > 0.25 && n->isCandidate() && n->isClipable()) { + vq.remove(n); + v = n; + goto continue_clipping; + } + if (p->score > 0.25 && p->isCandidate() && p->isClipable()) { + vq.remove(p); + v = p; + goto continue_clipping; + } + } + + bool ret = false; + +#if defined(CARVE_DEBUG) + dumpPoly(begin->edge, NULL, "remainder_"); +#endif + + if (remain > 3) { + std::vector<carve::geom2d::P2> temp; + temp.reserve(remain); + VertexInfo *v = begin; + do { + temp.push_back(P(v)); + v = v->next; + } while (v != begin); + + if (carve::geom2d::signedArea(temp) == 0) { + // XXX: this test will fail in cases where the boundary is + // twisted so that a negative area balances a positive area. + std::cerr << "got to here" << std::endl; + dumpPoly(begin->edge, NULL, "interesting_case_"); + goto done; + } + } + + if (remain > 3) { + remain -= removeDegeneracies(begin, out); + } + + if (remain > 3) { + return splitAndResume(begin, out); + } + + { double a = loopArea(begin->edge, proj); CARVE_ASSERT(a <= 0.0); } + *out++ = begin->edge; + + v = begin; + do { + n = v->next; + delete v; + v = n; + } while (v != begin); + + ret = true; + + done: + return ret; + } + } + + + + template<unsigned ndim, typename proj_t, typename out_iter_t> + void triangulate(Edge<ndim> *edge, proj_t proj, out_iter_t out) { + detail::TriangulationData<ndim, proj_t> triangulator(proj); + typename detail::TriangulationData<ndim, proj_t>::VertexInfo *v = triangulator.init(edge); + triangulator.removeDegeneracies(v, out); + triangulator.doTriangulate(v, out); + } + + // given edge a-b, part of triangles a-b-c and b-a-d, make triangles c-a-d and b-c-d + template<unsigned ndim> + void flipTriEdge(Edge<ndim> *edge) { + CARVE_ASSERT(edge->rev != NULL); + CARVE_ASSERT(edge->face->nEdges() == 3); + CARVE_ASSERT(edge->rev->face->nEdges() == 3); + + CARVE_ASSERT(edge->prev != edge); + CARVE_ASSERT(edge->next != edge); + CARVE_ASSERT(edge->rev->prev != edge->rev); + CARVE_ASSERT(edge->rev->next != edge->rev); + + typedef Edge<ndim> edge_t; + typedef Face<ndim> face_t; + + edge_t *t1[3], *t2[3]; + face_t *f1, *f2; + + t1[1] = edge; t2[1] = edge->rev; + t1[0] = t1[1]->prev; t1[2] = t1[1]->next; + t2[0] = t2[1]->prev; t2[2] = t2[1]->next; + + f1 = t1[1]->face; f2 = t2[1]->face; + + // std::cerr << t1[0]->vert << "->" << t1[1]->vert << "->" << t1[2]->vert << std::endl; + // std::cerr << t2[0]->vert << "->" << t2[1]->vert << "->" << t2[2]->vert << std::endl; + + t1[1]->vert = t2[0]->vert; + t2[1]->vert = t1[0]->vert; + + // std::cerr << t1[0]->vert << "->" << t2[2]->vert << "->" << t1[1]->vert << std::endl; + // std::cerr << t2[0]->vert << "->" << t1[2]->vert << "->" << t2[1]->vert << std::endl; + + detail::link(t1[0], t2[2], t1[1], f1); + detail::link(t2[0], t1[2], t2[1], f2); + + if (t1[0]->rev) CARVE_ASSERT(t1[0]->v2() == t1[0]->rev->v1()); + if (t2[0]->rev) CARVE_ASSERT(t2[0]->v2() == t2[0]->rev->v1()); + if (t1[2]->rev) CARVE_ASSERT(t1[2]->v2() == t1[2]->rev->v1()); + if (t2[2]->rev) CARVE_ASSERT(t2[2]->v2() == t2[2]->rev->v1()); + } + + template<unsigned ndim> + void splitEdgeLoop(Edge<ndim> *v1, Edge<ndim> *v2) { + // v1 and v2 end up on different sides of the split. + Edge<ndim> *v1_copy = new Edge<ndim>(v1->vert, NULL); + Edge<ndim> *v2_copy = new Edge<ndim>(v2->vert, NULL); + + v1_copy->rev = v2_copy; + v2_copy->rev = v1_copy; + + v1_copy->prev = v1->prev; + v1_copy->next = v2; + + v2_copy->prev = v2->prev; + v2_copy->next = v1; + + v1->prev->next = v1_copy; + v1->prev = v2_copy; + + v2->prev->next = v2_copy; + v2->prev = v1_copy; + } + + template<unsigned ndim> + Edge<ndim> *clipVertex(Edge<ndim> *edge) { + Edge<ndim> *prev = edge->prev; + Edge<ndim> *next = edge->next; + splitEdgeLoop(edge->prev, edge->next); + return next; + } + } +} diff --git a/extern/carve/include/carve/mesh_simplify.hpp b/extern/carve/include/carve/mesh_simplify.hpp new file mode 100644 index 00000000000..1c5169caf58 --- /dev/null +++ b/extern/carve/include/carve/mesh_simplify.hpp @@ -0,0 +1,1574 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> +#include <carve/mesh.hpp> +#include <carve/mesh_ops.hpp> +#include <carve/geom2d.hpp> +#include <carve/heap.hpp> +#include <carve/rtree.hpp> +#include <carve/triangle_intersection.hpp> + +#include <fstream> +#include <string> +#include <utility> +#include <set> +#include <algorithm> +#include <vector> + +#include "write_ply.hpp" + + +namespace carve { + namespace mesh { + + + class MeshSimplifier { + typedef carve::mesh::MeshSet<3> meshset_t; + typedef carve::mesh::Mesh<3> mesh_t; + typedef mesh_t::vertex_t vertex_t; + typedef vertex_t::vector_t vector_t; + typedef mesh_t::edge_t edge_t; + typedef mesh_t::face_t face_t; + typedef face_t::aabb_t aabb_t; + + typedef carve::geom::RTreeNode<3, carve::mesh::Face<3> *> face_rtree_t; + + + struct EdgeInfo { + edge_t *edge; + double delta_v; + + double c[4]; + double l[2], t1[2], t2[2]; + size_t heap_idx; + + void update() { + const vertex_t *v1 = edge->vert; + const vertex_t *v2 = edge->next->vert; + const vertex_t *v3 = edge->next->next->vert; + const vertex_t *v4 = edge->rev ? edge->rev->next->next->vert : NULL; + + l[0] = (v1->v - v2->v).length(); + + t1[0] = (v3->v - v1->v).length(); + t1[1] = (v3->v - v2->v).length(); + + c[0] = std::max((t1[0] + t1[1]) / l[0] - 1.0, 0.0); + + if (v4) { + l[1] = (v3->v - v4->v).length(); + t2[0] = (v4->v - v1->v).length(); + t2[1] = (v4->v - v2->v).length(); + c[1] = std::max((t2[0] + t2[1]) / l[0] - 1.0, 0.0); + c[2] = std::max((t1[0] + t2[0]) / l[1] - 1.0, 0.0); + c[3] = std::max((t1[1] + t2[1]) / l[1] - 1.0, 0.0); + delta_v = carve::geom3d::tetrahedronVolume(v1->v, v2->v, v3->v, v4->v); + } else { + l[1] = 0.0; + t2[0] = t2[1] = 0.0; + c[1] = c[2] = c[3] = 0.0; + delta_v = 0.0; + } + } + + EdgeInfo(edge_t *e) : edge(e) { + update(); + } + + EdgeInfo() : edge(NULL) { + delta_v = 0.0; + c[0] = c[1] = c[2] = c[3] = 0.0; + l[0] = l[1] = 0.0; + t1[0] = t1[1] = 0.0; + t2[0] = t2[1] = 0.0; + } + + struct NotifyPos { + void operator()(EdgeInfo *edge, size_t pos) const { edge->heap_idx = pos; } + void operator()(EdgeInfo &edge, size_t pos) const { edge.heap_idx = pos; } + }; + }; + + + + struct FlippableBase { + double min_dp; + + FlippableBase(double _min_dp = 0.0) : min_dp(_min_dp) { + } + + bool open(const EdgeInfo *e) const { + return e->edge->rev == NULL; + } + + bool wouldCreateDegenerateEdge(const EdgeInfo *e) const { + return e->edge->prev->vert == e->edge->rev->prev->vert; + } + + bool flippable_DotProd(const EdgeInfo *e) const { + using carve::geom::dot; + using carve::geom::cross; + + if (open(e)) return false; + + edge_t *edge = e->edge; + + const vertex_t *v1 = edge->vert; + const vertex_t *v2 = edge->next->vert; + const vertex_t *v3 = edge->next->next->vert; + const vertex_t *v4 = edge->rev->next->next->vert; + + if (dot(cross(v3->v - v2->v, v1->v - v2->v).normalized(), + cross(v4->v - v1->v, v2->v - v1->v).normalized()) < min_dp) return false; + + if (dot(cross(v3->v - v4->v, v1->v - v4->v).normalized(), + cross(v4->v - v3->v, v2->v - v3->v).normalized()) < min_dp) return false; + + return true; + } + + virtual bool canFlip(const EdgeInfo *e) const { + return !open(e) && !wouldCreateDegenerateEdge(e) && score(e) > 0.0; + } + + virtual double score(const EdgeInfo *e) const { + return std::min(e->c[2], e->c[3]) - std::min(e->c[0], e->c[1]); + } + + class Priority { + Priority &operator=(const Priority &); + const FlippableBase &flip; + + public: + Priority(const FlippableBase &_flip) : flip(_flip) {} + bool operator()(const EdgeInfo *a, const EdgeInfo *b) const { return flip.score(a) > flip.score(b); } + }; + + Priority priority() const { + return Priority(*this); + } + }; + + + + struct FlippableConservative : public FlippableBase { + FlippableConservative() : FlippableBase(0.0) { + } + + bool connectsAlmostCoplanarFaces(const EdgeInfo *e) const { + // XXX: remove hard coded constants. + if (e->c[0] < 1e-10 || e->c[1] < 1e-10) return true; + return fabs(carve::geom::dot(e->edge->face->plane.N, e->edge->rev->face->plane.N) - 1.0) < 1e-10; + } + + bool connectsExactlyCoplanarFaces(const EdgeInfo *e) const { + edge_t *edge = e->edge; + return + carve::geom3d::orient3d(edge->vert->v, + edge->next->vert->v, + edge->next->next->vert->v, + edge->rev->next->next->vert->v) == 0.0 && + carve::geom3d::orient3d(edge->rev->vert->v, + edge->rev->next->vert->v, + edge->rev->next->next->vert->v, + edge->next->next->vert->v) == 0.0; + } + + virtual bool canFlip(const EdgeInfo *e) const { + return FlippableBase::canFlip(e) && connectsExactlyCoplanarFaces(e) && flippable_DotProd(e); + } + }; + + + + struct FlippableColinearPair : public FlippableBase { + + FlippableColinearPair() { + } + + + virtual double score(const EdgeInfo *e) const { + return e->l[0] - e->l[1]; + } + + virtual bool canFlip(const EdgeInfo *e) const { + if (!FlippableBase::canFlip(e)) return false; + + if (e->c[0] > 1e-3 || e->c[1] > 1e-3) return false; + + return true; + } + }; + + + + struct Flippable : public FlippableBase { + double min_colinearity; + double min_delta_v; + + Flippable(double _min_colinearity, + double _min_delta_v, + double _min_normal_angle) : + FlippableBase(cos(_min_normal_angle)), + min_colinearity(_min_colinearity), + min_delta_v(_min_delta_v) { + } + + + virtual bool canFlip(const EdgeInfo *e) const { + if (!FlippableBase::canFlip(e)) return false; + + if (fabs(e->delta_v) > min_delta_v) return false; + + // if (std::min(e->c[0], e->c[1]) > min_colinearity) return false; + + return flippable_DotProd(e); + } + }; + + + + struct EdgeMerger { + double min_edgelen; + + virtual bool canMerge(const EdgeInfo *e) const { + return e->l[0] <= min_edgelen; + } + + EdgeMerger(double _min_edgelen) : min_edgelen(_min_edgelen) { + } + + double score(const EdgeInfo *e) const { + return min_edgelen - e->l[0]; + } + + class Priority { + Priority &operator=(const Priority &); + + public: + const EdgeMerger &merger; + Priority(const EdgeMerger &_merger) : merger(_merger) { + } + bool operator()(const EdgeInfo *a, const EdgeInfo *b) const { + // collapse edges in order from shortest to longest. + return merger.score(a) < merger.score(b); + } + }; + + Priority priority() const { + return Priority(*this); + } + }; + + + + typedef std::unordered_map<edge_t *, EdgeInfo *> edge_info_map_t; + std::unordered_map<edge_t *, EdgeInfo *> edge_info; + + + + void initEdgeInfo(mesh_t *mesh) { + for (size_t i = 0; i < mesh->faces.size(); ++i) { + edge_t *e = mesh->faces[i]->edge; + do { + edge_info[e] = new EdgeInfo(e); + e = e->next; + } while (e != mesh->faces[i]->edge); + } + } + + + + void initEdgeInfo(meshset_t *meshset) { + for (size_t m = 0; m < meshset->meshes.size(); ++m) { + mesh_t *mesh = meshset->meshes[m]; + initEdgeInfo(mesh); + } + } + + + + void clearEdgeInfo() { + for (edge_info_map_t::iterator i = edge_info.begin(); i != edge_info.end(); ++i) { + delete (*i).second; + } + } + + + + void updateEdgeFlipHeap(std::vector<EdgeInfo *> &edge_heap, + edge_t *edge, + const FlippableBase &flipper) { + std::unordered_map<edge_t *, EdgeInfo *>::const_iterator i = edge_info.find(edge); + CARVE_ASSERT(i != edge_info.end()); + EdgeInfo *e = (*i).second; + + bool heap_pre = e->heap_idx != ~0U; + (*i).second->update(); + bool heap_post = edge->v1() < edge->v2() && flipper.canFlip(e); + + if (!heap_pre && heap_post) { + edge_heap.push_back(e); + carve::heap::push_heap(edge_heap.begin(), + edge_heap.end(), + flipper.priority(), + EdgeInfo::NotifyPos()); + } else if (heap_pre && !heap_post) { + CARVE_ASSERT(edge_heap[e->heap_idx] == e); + carve::heap::remove_heap(edge_heap.begin(), + edge_heap.end(), + edge_heap.begin() + e->heap_idx, + flipper.priority(), + EdgeInfo::NotifyPos()); + CARVE_ASSERT(edge_heap.back() == e); + edge_heap.pop_back(); + e->heap_idx = ~0U; + } else if (heap_pre && heap_post) { + CARVE_ASSERT(edge_heap[e->heap_idx] == e); + carve::heap::adjust_heap(edge_heap.begin(), + edge_heap.end(), + edge_heap.begin() + e->heap_idx, + flipper.priority(), + EdgeInfo::NotifyPos()); + CARVE_ASSERT(edge_heap[e->heap_idx] == e); + } + } + + + std::string vk(const vertex_t *v1, + const vertex_t *v2, + const vertex_t *v3) { + const vertex_t *v[3]; + v[0] = v1; v[1] = v2; v[2] = v3; + std::sort(v, v+3); + std::ostringstream s; + s << v[0] << ";" << v[1] << ";" << v[2]; + return s.str(); + } + + std::string vk(const face_t *f) { return vk(f->edge->vert, f->edge->next->vert, f->edge->next->next->vert); } + + int mapTriangle(const face_t *face, + const vertex_t *remap1, const vertex_t *remap2, + const vector_t &tgt, + vector_t tri[3]) { + edge_t *edge = face->edge; + int n_remaps = 0; + for (size_t i = 0; i < 3; edge = edge->next, ++i) { + if (edge->vert == remap1) { tri[i] = tgt; ++n_remaps; } + else if (edge->vert == remap2) { tri[i] = tgt; ++n_remaps; } + else { tri[i] = edge->vert->v; } + } + return n_remaps; + } + + template<typename iter1_t, typename iter2_t> + int countIntersectionPairs(iter1_t fabegin, iter1_t faend, + iter2_t fbbegin, iter2_t fbend, + const vertex_t *remap1, const vertex_t *remap2, + const vector_t &tgt) { + vector_t tri_a[3], tri_b[3]; + int remap_a, remap_b; + std::set<std::pair<const face_t *, const face_t *> > ints; + + for (iter1_t i = fabegin; i != faend; ++i) { + remap_a = mapTriangle(*i, remap1, remap2, tgt, tri_a); + if (remap_a >= 2) continue; + for (iter2_t j = fbbegin; j != fbend; ++j) { + remap_b = mapTriangle(*j, remap1, remap2, tgt, tri_b); + if (remap_b >= 2) continue; + if (carve::geom::triangle_intersection_exact(tri_a, tri_b) == carve::geom::TR_TYPE_INT) { + ints.insert(std::make_pair(std::min(*i, *j), std::max(*i, *j))); + } + } + } + + return ints.size(); + } + + int countIntersections(const vertex_t *v1, + const vertex_t *v2, + const vertex_t *v3, + const std::vector<face_t *> &faces) { + int n_int = 0; + vector_t tri_a[3], tri_b[3]; + tri_a[0] = v1->v; + tri_a[1] = v2->v; + tri_a[2] = v3->v; + + for (std::vector<face_t *>::const_iterator i = faces.begin(); i != faces.end(); ++i) { + face_t *fb = *i; + if (fb->nEdges() != 3) continue; + tri_b[0] = fb->edge->vert->v; + tri_b[1] = fb->edge->next->vert->v; + tri_b[2] = fb->edge->next->next->vert->v; + + if (carve::geom::triangle_intersection_exact(tri_a, tri_b) == carve::geom::TR_TYPE_INT) { + n_int++; + } + } + return n_int; + } + + + + int _findSelfIntersections(const face_rtree_t *a_node, + const face_rtree_t *b_node, + bool descend_a = true) { + int r = 0; + + if (!a_node->bbox.intersects(b_node->bbox)) { + return 0; + } + + if (a_node->child && (descend_a || !b_node->child)) { + for (face_rtree_t *node = a_node->child; node; node = node->sibling) { + r += _findSelfIntersections(node, b_node, false); + } + } else if (b_node->child) { + for (face_rtree_t *node = b_node->child; node; node = node->sibling) { + r += _findSelfIntersections(a_node, node, true); + } + } else { + for (size_t i = 0; i < a_node->data.size(); ++i) { + face_t *fa = a_node->data[i]; + if (fa->nVertices() != 3) continue; + + aabb_t aabb_a = fa->getAABB(); + + vector_t tri_a[3]; + tri_a[0] = fa->edge->vert->v; + tri_a[1] = fa->edge->next->vert->v; + tri_a[2] = fa->edge->next->next->vert->v; + + if (!aabb_a.intersects(b_node->bbox)) continue; + + for (size_t j = 0; j < b_node->data.size(); ++j) { + face_t *fb = b_node->data[j]; + if (fb->nVertices() != 3) continue; + + vector_t tri_b[3]; + tri_b[0] = fb->edge->vert->v; + tri_b[1] = fb->edge->next->vert->v; + tri_b[2] = fb->edge->next->next->vert->v; + + if (carve::geom::triangle_intersection_exact(tri_a, tri_b) == carve::geom::TR_TYPE_INT) { + ++r; + } + } + } + } + + return r; + } + + + + int countSelfIntersections(meshset_t *meshset) { + int n_ints = 0; + face_rtree_t *tree = face_rtree_t::construct_STR(meshset->faceBegin(), meshset->faceEnd(), 4, 4); + + for (meshset_t::face_iter f = meshset->faceBegin(); f != meshset->faceEnd(); ++f) { + face_t *fa = *f; + if (fa->nVertices() != 3) continue; + + vector_t tri_a[3]; + tri_a[0] = fa->edge->vert->v; + tri_a[1] = fa->edge->next->vert->v; + tri_a[2] = fa->edge->next->next->vert->v; + + std::vector<face_t *> near_faces; + tree->search(fa->getAABB(), std::back_inserter(near_faces)); + + for (size_t f2 = 0; f2 < near_faces.size(); ++f2) { + const face_t *fb = near_faces[f2]; + if (fb->nVertices() != 3) continue; + + if (fa >= fb) continue; + + vector_t tri_b[3]; + tri_b[0] = fb->edge->vert->v; + tri_b[1] = fb->edge->next->vert->v; + tri_b[2] = fb->edge->next->next->vert->v; + + if (carve::geom::triangle_intersection_exact(tri_a, tri_b) == carve::geom::TR_TYPE_INT) { + ++n_ints; + } + } + } + + delete tree; + + return n_ints; + } + + size_t flipEdges(meshset_t *mesh, + const FlippableBase &flipper) { + face_rtree_t *tree = face_rtree_t::construct_STR(mesh->faceBegin(), mesh->faceEnd(), 4, 4); + + size_t n_mods = 0; + + std::vector<EdgeInfo *> edge_heap; + + edge_heap.reserve(edge_info.size()); + + for (edge_info_map_t::iterator i = edge_info.begin(); + i != edge_info.end(); + ++i) { + EdgeInfo *e = (*i).second; + e->update(); + if (e->edge->v1() < e->edge->v2() && flipper.canFlip(e)) { + edge_heap.push_back(e); + } else { + e->heap_idx = ~0U; + } + } + + carve::heap::make_heap(edge_heap.begin(), + edge_heap.end(), + flipper.priority(), + EdgeInfo::NotifyPos()); + + while (edge_heap.size()) { +// std::cerr << "test" << std::endl; +// for (size_t m = 0; m < mesh->meshes.size(); ++m) { +// for (size_t f = 0; f < mesh->meshes[m]->faces.size(); ++f) { +// if (mesh->meshes[m]->faces[f]->edge) mesh->meshes[m]->faces[f]->edge->validateLoop(); +// } +// } + + carve::heap::pop_heap(edge_heap.begin(), + edge_heap.end(), + flipper.priority(), + EdgeInfo::NotifyPos()); + EdgeInfo *e = edge_heap.back(); +// std::cerr << "flip " << e << std::endl; + edge_heap.pop_back(); + e->heap_idx = ~0U; + + aabb_t aabb; + aabb = e->edge->face->getAABB(); + aabb.unionAABB(e->edge->rev->face->getAABB()); + + std::vector<face_t *> overlapping; + tree->search(aabb, std::back_inserter(overlapping)); + + // overlapping.erase(e->edge->face); + // overlapping.erase(e->edge->rev->face); + + const vertex_t *v1 = e->edge->vert; + const vertex_t *v2 = e->edge->next->vert; + const vertex_t *v3 = e->edge->next->next->vert; + const vertex_t *v4 = e->edge->rev->next->next->vert; + + int n_int1 = countIntersections(v1, v2, v3, overlapping); + int n_int2 = countIntersections(v2, v1, v4, overlapping); + int n_int3 = countIntersections(v3, v4, v2, overlapping); + int n_int4 = countIntersections(v4, v3, v1, overlapping); + + if ((n_int3 + n_int4) - (n_int1 + n_int2) > 0) { + std::cerr << "delta[ints] = " << (n_int3 + n_int4) - (n_int1 + n_int2) << std::endl; + // avoid creating a self intersection. + continue; + } + + n_mods++; + CARVE_ASSERT(flipper.canFlip(e)); + edge_info[e->edge]->update(); + edge_info[e->edge->rev]->update(); + + carve::mesh::flipTriEdge(e->edge); + + tree->updateExtents(aabb); + + updateEdgeFlipHeap(edge_heap, e->edge, flipper); + updateEdgeFlipHeap(edge_heap, e->edge->rev, flipper); + + CARVE_ASSERT(!flipper.canFlip(e)); + + updateEdgeFlipHeap(edge_heap, e->edge->next, flipper); + updateEdgeFlipHeap(edge_heap, e->edge->next->next, flipper); + updateEdgeFlipHeap(edge_heap, e->edge->rev->next, flipper); + updateEdgeFlipHeap(edge_heap, e->edge->rev->next->next, flipper); + updateEdgeFlipHeap(edge_heap, e->edge->next->rev, flipper); + updateEdgeFlipHeap(edge_heap, e->edge->next->next->rev, flipper); + updateEdgeFlipHeap(edge_heap, e->edge->rev->next->rev, flipper); + updateEdgeFlipHeap(edge_heap, e->edge->rev->next->next->rev, flipper); + } + + delete tree; + + return n_mods; + } + + + + void removeFromEdgeMergeHeap(std::vector<EdgeInfo *> &edge_heap, + EdgeInfo *edge, + const EdgeMerger &merger) { + if (edge->heap_idx != ~0U) { + CARVE_ASSERT(edge_heap[edge->heap_idx] == edge); + carve::heap::remove_heap(edge_heap.begin(), + edge_heap.end(), + edge_heap.begin() + edge->heap_idx, + merger.priority(), + EdgeInfo::NotifyPos()); + CARVE_ASSERT(edge_heap.back() == edge); + edge_heap.pop_back(); + edge->heap_idx = ~0U; + } + } + + void updateEdgeMergeHeap(std::vector<EdgeInfo *> &edge_heap, + EdgeInfo *edge, + const EdgeMerger &merger) { + bool heap_pre = edge->heap_idx != ~0U; + edge->update(); + bool heap_post = merger.canMerge(edge); + + if (!heap_pre && heap_post) { + edge_heap.push_back(edge); + carve::heap::push_heap(edge_heap.begin(), + edge_heap.end(), + merger.priority(), + EdgeInfo::NotifyPos()); + } else if (heap_pre && !heap_post) { + CARVE_ASSERT(edge_heap[edge->heap_idx] == edge); + carve::heap::remove_heap(edge_heap.begin(), + edge_heap.end(), + edge_heap.begin() + edge->heap_idx, + merger.priority(), + EdgeInfo::NotifyPos()); + CARVE_ASSERT(edge_heap.back() == edge); + edge_heap.pop_back(); + edge->heap_idx = ~0U; + } else if (heap_pre && heap_post) { + CARVE_ASSERT(edge_heap[edge->heap_idx] == edge); + carve::heap::adjust_heap(edge_heap.begin(), + edge_heap.end(), + edge_heap.begin() + edge->heap_idx, + merger.priority(), + EdgeInfo::NotifyPos()); + CARVE_ASSERT(edge_heap[edge->heap_idx] == edge); + } + } + + + + // collapse edges edges based upon the predicate implemented by EdgeMerger. + size_t collapseEdges(meshset_t *mesh, + const EdgeMerger &merger) { + face_rtree_t *tree = face_rtree_t::construct_STR(mesh->faceBegin(), mesh->faceEnd(), 4, 4); + + size_t n_mods = 0; + + std::vector<EdgeInfo *> edge_heap; + std::unordered_map<vertex_t *, std::set<EdgeInfo *> > vert_to_edges; + + edge_heap.reserve(edge_info.size()); + + for (edge_info_map_t::iterator i = edge_info.begin(); + i != edge_info.end(); + ++i) { + EdgeInfo *e = (*i).second; + + vert_to_edges[e->edge->v1()].insert(e); + vert_to_edges[e->edge->v2()].insert(e); + + if (merger.canMerge(e)) { + edge_heap.push_back(e); + } else { + e->heap_idx = ~0U; + } + } + + carve::heap::make_heap(edge_heap.begin(), + edge_heap.end(), + merger.priority(), + EdgeInfo::NotifyPos()); + + while (edge_heap.size()) { +// std::cerr << "test" << std::endl; +// for (size_t m = 0; m < mesh->meshes.size(); ++m) { +// for (size_t f = 0; f < mesh->meshes[m]->faces.size(); ++f) { +// if (mesh->meshes[m]->faces[f]->edge) mesh->meshes[m]->faces[f]->edge->validateLoop(); +// } +// } + carve::heap::pop_heap(edge_heap.begin(), + edge_heap.end(), + merger.priority(), + EdgeInfo::NotifyPos()); + EdgeInfo *e = edge_heap.back(); + edge_heap.pop_back(); + e->heap_idx = ~0U; + + edge_t *edge = e->edge; + vertex_t *v1 = edge->v1(); + vertex_t *v2 = edge->v2(); + + std::set<face_t *> affected_faces; + for (std::set<EdgeInfo *>::iterator f = vert_to_edges[v1].begin(); + f != vert_to_edges[v1].end(); + ++f) { + affected_faces.insert((*f)->edge->face); + affected_faces.insert((*f)->edge->rev->face); + } + for (std::set<EdgeInfo *>::iterator f = vert_to_edges[v2].begin(); + f != vert_to_edges[v2].end(); + ++f) { + affected_faces.insert((*f)->edge->face); + affected_faces.insert((*f)->edge->rev->face); + } + + std::vector<EdgeInfo *> edges_to_merge; + std::vector<EdgeInfo *> v1_incident; + std::vector<EdgeInfo *> v2_incident; + + std::set_intersection(vert_to_edges[v1].begin(), vert_to_edges[v1].end(), + vert_to_edges[v2].begin(), vert_to_edges[v2].end(), + std::back_inserter(edges_to_merge)); + + CARVE_ASSERT(edges_to_merge.size() > 0); + + std::set_difference(vert_to_edges[v1].begin(), vert_to_edges[v1].end(), + edges_to_merge.begin(), edges_to_merge.end(), + std::back_inserter(v1_incident)); + std::set_difference(vert_to_edges[v2].begin(), vert_to_edges[v2].end(), + edges_to_merge.begin(), edges_to_merge.end(), + std::back_inserter(v2_incident)); + + vector_t aabb_min, aabb_max; + assign_op(aabb_min, v1->v, v2->v, carve::util::min_functor()); + assign_op(aabb_max, v1->v, v2->v, carve::util::max_functor()); + + for (size_t i = 0; i < v1_incident.size(); ++i) { + assign_op(aabb_min, aabb_min, v1_incident[i]->edge->v1()->v, carve::util::min_functor()); + assign_op(aabb_max, aabb_max, v1_incident[i]->edge->v1()->v, carve::util::max_functor()); + assign_op(aabb_min, aabb_min, v1_incident[i]->edge->v2()->v, carve::util::min_functor()); + assign_op(aabb_max, aabb_max, v1_incident[i]->edge->v2()->v, carve::util::max_functor()); + } + + for (size_t i = 0; i < v2_incident.size(); ++i) { + assign_op(aabb_min, aabb_min, v2_incident[i]->edge->v1()->v, carve::util::min_functor()); + assign_op(aabb_max, aabb_max, v2_incident[i]->edge->v1()->v, carve::util::max_functor()); + assign_op(aabb_min, aabb_min, v2_incident[i]->edge->v2()->v, carve::util::min_functor()); + assign_op(aabb_max, aabb_max, v2_incident[i]->edge->v2()->v, carve::util::max_functor()); + } + + aabb_t aabb; + aabb.fit(aabb_min, aabb_max); + + std::vector<face_t *> near_faces; + tree->search(aabb, std::back_inserter(near_faces)); + + double frac = 0.5; // compute this based upon v1_incident and v2_incident? + vector_t merge = frac * v1->v + (1 - frac) * v2->v; + + int i1 = countIntersectionPairs(affected_faces.begin(), affected_faces.end(), + near_faces.begin(), near_faces.end(), + NULL, NULL, merge); + int i2 = countIntersectionPairs(affected_faces.begin(), affected_faces.end(), + near_faces.begin(), near_faces.end(), + v1, v2, merge); + if (i2 != i1) { + std::cerr << "near faces: " << near_faces.size() << " affected faces: " << affected_faces.size() << std::endl; + std::cerr << "merge delta[ints] = " << i2 - i1 << " pre: " << i1 << " post: " << i2 << std::endl; + if (i2 > i1) continue; + } + + std::cerr << "collapse " << e << std::endl; + + v2->v = merge; + ++n_mods; + + for (size_t i = 0; i < v1_incident.size(); ++i) { + if (v1_incident[i]->edge->vert == v1) { + v1_incident[i]->edge->vert = v2; + } + } + + for (size_t i = 0; i < v1_incident.size(); ++i) { + updateEdgeMergeHeap(edge_heap, v1_incident[i], merger); + } + + for (size_t i = 0; i < v2_incident.size(); ++i) { + updateEdgeMergeHeap(edge_heap, v2_incident[i], merger); + } + + vert_to_edges[v2].insert(vert_to_edges[v1].begin(), vert_to_edges[v1].end()); + vert_to_edges.erase(v1); + + for (size_t i = 0; i < edges_to_merge.size(); ++i) { + EdgeInfo *e = edges_to_merge[i]; + + removeFromEdgeMergeHeap(edge_heap, e, merger); + edge_info.erase(e->edge); + + vert_to_edges[v1].erase(e); + vert_to_edges[v2].erase(e); + + face_t *f1 = e->edge->face; + + e->edge->removeHalfEdge(); + + if (f1->n_edges == 2) { + edge_t *e1 = f1->edge; + edge_t *e2 = f1->edge->next; + if (e1->rev) e1->rev->rev = e2->rev; + if (e2->rev) e2->rev->rev = e1->rev; + EdgeInfo *e1i = edge_info[e1]; + EdgeInfo *e2i = edge_info[e2]; + CARVE_ASSERT(e1i != NULL); + CARVE_ASSERT(e2i != NULL); + vert_to_edges[e1->v1()].erase(e1i); + vert_to_edges[e1->v2()].erase(e1i); + vert_to_edges[e2->v1()].erase(e2i); + vert_to_edges[e2->v2()].erase(e2i); + removeFromEdgeMergeHeap(edge_heap, e1i, merger); + removeFromEdgeMergeHeap(edge_heap, e2i, merger); + edge_info.erase(e1); + edge_info.erase(e2); + f1->clearEdges(); + tree->remove(f1, aabb); + + delete e1i; + delete e2i; + } + delete e; + } + + tree->updateExtents(aabb); + } + + delete tree; + + return n_mods; + } + + + + size_t mergeCoplanarFaces(mesh_t *mesh, double min_normal_angle) { + std::unordered_set<edge_t *> coplanar_face_edges; + double min_dp = cos(min_normal_angle); + size_t n_merge = 0; + + for (size_t i = 0; i < mesh->closed_edges.size(); ++i) { + edge_t *e = mesh->closed_edges[i]; + face_t *f1 = e->face; + face_t *f2 = e->rev->face; + + if (carve::geom::dot(f1->plane.N, f2->plane.N) < min_dp) { + continue; + } + + coplanar_face_edges.insert(std::min(e, e->rev)); + } + + while (coplanar_face_edges.size()) { + edge_t *edge = *coplanar_face_edges.begin(); + if (edge->face == edge->rev->face) { + coplanar_face_edges.erase(edge); + continue; + } + + edge_t *removed = edge->mergeFaces(); + if (removed == NULL) { + coplanar_face_edges.erase(edge); + ++n_merge; + } else { + edge_t *e = removed; + do { + edge_t *n = e->next; + coplanar_face_edges.erase(std::min(e, e->rev)); + delete e->rev; + delete e; + e = n; + } while (e != removed); + } + } + return n_merge; + } + + + + uint8_t affected_axes(const face_t *face) { + uint8_t r = 0; + if (fabs(carve::geom::dot(face->plane.N, carve::geom::VECTOR(1,0,0))) > 0.001) r |= 1; + if (fabs(carve::geom::dot(face->plane.N, carve::geom::VECTOR(0,1,0))) > 0.001) r |= 2; + if (fabs(carve::geom::dot(face->plane.N, carve::geom::VECTOR(0,0,1))) > 0.001) r |= 4; + return r; + } + + + + double median(std::vector<double> &v) { + if (v.size() & 1) { + size_t N = v.size() / 2 + 1; + std::nth_element(v.begin(), v.begin() + N, v.end()); + return v[N]; + } else { + size_t N = v.size() / 2; + std::nth_element(v.begin(), v.begin() + N, v.end()); + return (v[N] + *std::min_element(v.begin() + N + 1, v.end())) / 2.0; + } + } + + + + double harmonicmean(const std::vector<double> &v) { + double m = 0.0; + for (size_t i = 0; i < v.size(); ++i) { + m *= v[i]; + } + return pow(m, 1.0 / v.size()); + } + + + + double mean(const std::vector<double> &v) { + double m = 0.0; + for (size_t i = 0; i < v.size(); ++i) { + m += v[i]; + } + return m / v.size(); + } + + + + template<typename iter_t> + void snapFaces(iter_t begin, iter_t end, double grid, int axis) { + std::set<vertex_t *> vertices; + for (iter_t i = begin; i != end; ++i) { + face_t *face = *i; + edge_t *edge = face->edge; + do { + vertices.insert(edge->vert); + edge = edge->next; + } while (edge != face->edge); + } + + std::vector<double> pos; + pos.reserve(vertices.size()); + for (std::set<vertex_t *>::iterator i = vertices.begin(); i != vertices.end(); ++i) { + pos.push_back((*i)->v.v[axis]); + } + + double med = median(pos); + + double snap_pos = med; + if (grid) snap_pos = round(snap_pos / grid) * grid; + + for (std::set<vertex_t *>::iterator i = vertices.begin(); i != vertices.end(); ++i) { + (*i)->v.v[axis] = snap_pos; + } + + for (iter_t i = begin; i != end; ++i) { + face_t *face = *i; + face->recalc(); + edge_t *edge = face->edge; + do { + if (edge->rev && edge->rev->face) edge->rev->face->recalc(); + edge = edge->next; + } while (edge != face->edge); + } + } + + carve::geom::plane<3> quantizePlane(const face_t *face, + int angle_xy_quantization, + int angle_z_quantization) { + if (!angle_xy_quantization && !angle_z_quantization) { + return face->plane; + } + carve::geom::vector<3> normal = face->plane.N; + + if (angle_z_quantization) { + if (normal.x || normal.y) { + double a = asin(std::min(std::max(normal.z, 0.0), 1.0)); + a = round(a * angle_z_quantization / (M_PI * 2)) * (M_PI * 2) / angle_z_quantization; + normal.z = sin(a); + double s = sqrt((1 - normal.z * normal.z) / (normal.x * normal.x + normal.y * normal.y)); + normal.x = normal.x * s; + normal.y = normal.y * s; + } + } + if (angle_xy_quantization) { + if (normal.x || normal.y) { + double a = atan2(normal.y, normal.x); + a = round(a * angle_xy_quantization / (M_PI * 2)) * (M_PI * 2) / angle_xy_quantization; + double s = sqrt(1 - normal.z * normal.z); + s = std::min(std::max(s, 0.0), 1.0); + normal.x = cos(a) * s; + normal.y = sin(a) * s; + } + } + + std::cerr << "normal = " << normal << std::endl; + + std::vector<double> d_vec; + d_vec.reserve(face->nVertices()); + edge_t *e = face->edge; + do { + d_vec.push_back(-carve::geom::dot(normal, e->vert->v)); + e = e->next; + } while (e != face->edge); + + return carve::geom::plane<3>(normal, mean(d_vec)); + } + + + + double summedError(const carve::geom::vector<3> &vert, const std::list<carve::geom::plane<3> > &planes) { + double d = 0; + for (std::list<carve::geom::plane<3> >::const_iterator i = planes.begin(); i != planes.end(); ++i) { + d += fabs(carve::geom::distance2(*i, vert)); + } + return d; + } + + + + double minimize(carve::geom::vector<3> &vert, const std::list<carve::geom::plane<3> > &planes, int axis) { + double num = 0.0; + double den = 0.0; + int a1 = (axis + 1) % 3; + int a2 = (axis + 2) % 3; + for (std::list<carve::geom::plane<3> >::const_iterator i = planes.begin(); i != planes.end(); ++i) { + const carve::geom::vector<3> &N = (*i).N; + const double d = (*i).d; + den += N.v[axis] * N.v[axis]; + num -= N.v[axis] * (N.v[a1] * vert.v[a1] + N.v[a2] * vert.v[a2] + d); + } + if (fabs(den) < 1e-5) return vert.v[axis]; + return num / den; + } + + + + size_t cleanFaceEdges(mesh_t *mesh) { + size_t n_removed = 0; + for (size_t i = 0; i < mesh->faces.size(); ++i) { + face_t *face = mesh->faces[i]; + edge_t *start = face->edge; + edge_t *edge = start; + do { + if (edge->next == edge->rev || edge->prev == edge->rev) { + edge = edge->removeEdge(); + ++n_removed; + start = edge->prev; + } else { + edge = edge->next; + } + } while (edge != start); + } + return n_removed; + } + + + + size_t cleanFaceEdges(meshset_t *mesh) { + size_t n_removed = 0; + for (size_t i = 0; i < mesh->meshes.size(); ++i) { + n_removed += cleanFaceEdges(mesh->meshes[i]); + } + return n_removed; + } + + + + void removeRemnantFaces(mesh_t *mesh) { + size_t n = 0; + for (size_t i = 0; i < mesh->faces.size(); ++i) { + if (mesh->faces[i]->nEdges() == 0) { + delete mesh->faces[i]; + } else { + mesh->faces[n++] = mesh->faces[i]; + } + } + mesh->faces.resize(n); + } + + + + void removeRemnantFaces(meshset_t *mesh) { + for (size_t i = 0; i < mesh->meshes.size(); ++i) { + removeRemnantFaces(mesh->meshes[i]); + } + } + + + + edge_t *removeFin(edge_t *e) { + // e and e->next are shared with the same reverse triangle. + edge_t *e1 = e->prev; + edge_t *e2 = e->rev->next; + CARVE_ASSERT(e1->v2() == e2->v1()); + CARVE_ASSERT(e2->v2() == e1->v1()); + + CARVE_ASSERT(e1->rev != e2 && e2->rev != e1); + + edge_t *e1r = e1->rev; + edge_t *e2r = e2->rev; + if (e1r) e1r->rev = e2r; + if (e2r) e2r->rev = e1r; + + face_t *f1 = e1->face; + face_t *f2 = e2->face; + f1->clearEdges(); + f2->clearEdges(); + + return e1r; + } + + size_t removeFin(face_t *face) { + if (face->edge == NULL || face->nEdges() != 3) return 0; + edge_t *e = face->edge; + do { + if (e->rev != NULL) { + face_t *revface = e->rev->face; + if (revface->nEdges() == 3) { + if (e->next->rev && e->next->rev->face == revface) { + if (e->next->next->rev && e->next->next->rev->face == revface) { + // isolated tripair + face->clearEdges(); + revface->clearEdges(); + return 1; + } + // fin + edge_t *spliced_edge = removeFin(e); + return 1 + removeFin(spliced_edge->face); + } + } + } + e = e->next; + } while (e != face->edge); + return 0; + } + + + + public: + // Merge adjacent coplanar faces (where coplanar is determined + // by dot-product >= cos(min_normal_angle)). + size_t mergeCoplanarFaces(meshset_t *meshset, double min_normal_angle) { + size_t n_removed = 0; + for (size_t i = 0; i < meshset->meshes.size(); ++i) { + n_removed += mergeCoplanarFaces(meshset->meshes[i], min_normal_angle); + removeRemnantFaces(meshset->meshes[i]); + cleanFaceEdges(meshset->meshes[i]); + meshset->meshes[i]->cacheEdges(); + } + return n_removed; + } + + size_t improveMesh_conservative(meshset_t *meshset) { + initEdgeInfo(meshset); + size_t modifications = flipEdges(meshset, FlippableConservative()); + clearEdgeInfo(); + return modifications; + } + + + + size_t improveMesh(meshset_t *meshset, + double min_colinearity, + double min_delta_v, + double min_normal_angle) { + initEdgeInfo(meshset); + size_t modifications = flipEdges(meshset, Flippable(min_colinearity, min_delta_v, min_normal_angle)); + clearEdgeInfo(); + return modifications; + } + + + + size_t eliminateShortEdges(meshset_t *meshset, + double min_length) { + initEdgeInfo(meshset); + size_t modifications = collapseEdges(meshset, EdgeMerger(min_length)); + removeRemnantFaces(meshset); + clearEdgeInfo(); + return modifications; + } + + + + // Snap vertices to grid, aligning almost flat axis-aligned + // faces to the axis, and flattening other faces as much as is + // possible. Passing a number less than DBL_MIN_EXPONENT (-1021) + // turns off snapping to grid (but face alignment is still + // performed). + void snap(meshset_t *meshset, + int log2_grid, + int angle_xy_quantization = 0, + int angle_z_quantization = 0) { + double grid = 0.0; + if (log2_grid >= std::numeric_limits<double>::min_exponent) grid = pow(2.0, (double)log2_grid); + + typedef std::unordered_map<face_t *, uint8_t> axis_influence_map_t; + axis_influence_map_t axis_influence; + + typedef std::unordered_map<face_t *, std::set<face_t *> > interaction_graph_t; + interaction_graph_t interacting_faces; + + for (size_t m = 0; m < meshset->meshes.size(); ++m) { + mesh_t *mesh = meshset->meshes[m]; + for (size_t f = 0; f < mesh->faces.size(); ++f) { + face_t *face = mesh->faces[f]; + axis_influence[face] = affected_axes(face); + } + } + + std::map<vertex_t *, std::list<carve::geom::plane<3> > > non_axis_vertices; + std::unordered_map<vertex_t *, uint8_t> vertex_constraints; + + for (axis_influence_map_t::iterator i = axis_influence.begin(); i != axis_influence.end(); ++i) { + face_t *face = (*i).first; + uint8_t face_axes = (*i).second; + edge_t *edge = face->edge; + if (face_axes != 1 && face_axes != 2 && face_axes != 4) { + do { + non_axis_vertices[edge->vert].push_back(quantizePlane(face, + angle_xy_quantization, + angle_z_quantization)); + edge = edge->next; + } while (edge != face->edge); + } else { + interacting_faces[face].insert(face); + do { + vertex_constraints[edge->vert] |= face_axes; + + if (edge->rev && edge->rev->face) { + face_t *face2 = edge->rev->face; + uint8_t face2_axes = axis_influence[face2]; + if (face2_axes == face_axes) { + interacting_faces[face].insert(face2); + } + } + edge = edge->next; + } while (edge != face->edge); + } + } + + while (interacting_faces.size()) { + std::set<face_t *> face_set; + uint8_t axes = 0; + + std::set<face_t *> open; + open.insert((*interacting_faces.begin()).first); + while (open.size()) { + face_t *curr = *open.begin(); + open.erase(open.begin()); + face_set.insert(curr); + axes |= axis_influence[curr]; + for (interaction_graph_t::data_type::iterator i = interacting_faces[curr].begin(), e = interacting_faces[curr].end(); i != e; ++i) { + face_t *f = *i; + if (face_set.find(f) != face_set.end()) continue; + open.insert(f); + } + } + + switch (axes) { + case 1: snapFaces(face_set.begin(), face_set.end(), grid, 0); break; + case 2: snapFaces(face_set.begin(), face_set.end(), grid, 1); break; + case 4: snapFaces(face_set.begin(), face_set.end(), grid, 2); break; + default: CARVE_FAIL("should not be reached"); + } + + for (std::set<face_t *>::iterator i = face_set.begin(); i != face_set.end(); ++i) { + interacting_faces.erase((*i)); + } + } + + for (std::map<vertex_t *, std::list<carve::geom::plane<3> > >::iterator i = non_axis_vertices.begin(); i != non_axis_vertices.end(); ++i) { + vertex_t *vert = (*i).first; + std::list<carve::geom::plane<3> > &planes = (*i).second; + uint8_t constraint = vertex_constraints[vert]; + + if (constraint == 7) continue; + + double d = summedError(vert->v, planes); + for (size_t N = 0; ; N = (N+1) % 3) { + if (constraint & (1 << N)) continue; + vert->v[N] = minimize(vert->v, planes, N); + double d_next = summedError(vert->v, planes); + if (d - d_next < 1e-20) break; + d = d_next; + } + + if (grid) { + carve::geom::vector<3> v_best = vert->v; + double d_best = 0.0; + + for (size_t axes = 0; axes < 8; ++axes) { + carve::geom::vector<3> v = vert->v; + for (size_t N = 0; N < 3; ++N) { + if (constraint & (1 << N)) continue; + if (axes & (1<<N)) { + v.v[N] = ceil(v.v[N] / grid) * grid; + } else { + v.v[N] = floor(v.v[N] / grid) * grid; + } + } + double d = summedError(v, planes); + if (axes == 0 || d < d_best) { + v_best = v; + d_best = d; + } + } + + vert->v = v_best; + } + } + } + + + + size_t simplify(meshset_t *meshset, + double min_colinearity, + double min_delta_v, + double min_normal_angle, + double min_length) { + size_t modifications = 0; + size_t n, n_flip, n_merge; + + initEdgeInfo(meshset); + + std::cerr << "initial merge" << std::endl; + modifications = collapseEdges(meshset, EdgeMerger(0.0)); + removeRemnantFaces(meshset); + + do { + n_flip = n_merge = 0; + // std::cerr << "flip colinear pairs"; + // n = flipEdges(meshset, FlippableColinearPair()); + // std::cerr << " " << n << std::endl; + // n_flip = n; + + std::cerr << "flip conservative"; + n = flipEdges(meshset, FlippableConservative()); + std::cerr << " " << n << std::endl; + n_flip += n; + + std::cerr << "flip"; + n = flipEdges(meshset, Flippable(min_colinearity, min_delta_v, min_normal_angle)); + std::cerr << " " << n << std::endl; + n_flip += n; + + std::cerr << "merge"; + n = collapseEdges(meshset, EdgeMerger(min_length)); + removeRemnantFaces(meshset); + std::cerr << " " << n << std::endl; + n_merge = n; + + modifications += n_flip + n_merge; + std::cerr << "stats:" << n_flip << " " << n_merge << std::endl; + } while (n_flip || n_merge); + + clearEdgeInfo(); + + for (size_t i = 0; i < meshset->meshes.size(); ++i) { + meshset->meshes[i]->cacheEdges(); + } + + return modifications; + } + + + + size_t removeFins(mesh_t *mesh) { + size_t n_removed = 0; + for (size_t i = 0; i < mesh->faces.size(); ++i) { + n_removed += removeFin(mesh->faces[i]); + } + if (n_removed) removeRemnantFaces(mesh); + return n_removed; + } + + + + size_t removeFins(meshset_t *meshset) { + size_t n_removed = 0; + for (size_t i = 0; i < meshset->meshes.size(); ++i) { + n_removed += removeFins(meshset->meshes[i]); + } + return n_removed; + } + + + + size_t removeLowVolumeManifolds(meshset_t *meshset, double min_abs_volume) { + size_t n_removed; + for (size_t i = 0; i < meshset->meshes.size(); ++i) { + if (fabs(meshset->meshes[i]->volume()) < min_abs_volume) { + delete meshset->meshes[i]; + meshset->meshes[i] = NULL; + ++n_removed; + } + } + meshset->meshes.erase(std::remove_if(meshset->meshes.begin(), + meshset->meshes.end(), + std::bind2nd(std::equal_to<mesh_t *>(), (mesh_t *)NULL)), + meshset->meshes.end()); + return n_removed; + } + + struct point_enumerator_t { + struct heapval_t { + double dist; + vector_t pt; + heapval_t(double _dist, vector_t _pt) : dist(_dist), pt(_pt) { + } + heapval_t() {} + bool operator==(const heapval_t &other) const { return dist == other.dist && pt == other.pt; } + bool operator<(const heapval_t &other) const { return dist > other.dist || (dist == other.dist && pt > other.pt); } + }; + + vector_t origin; + double rounding_fac; + heapval_t last; + std::vector<heapval_t> heap; + + point_enumerator_t(vector_t _origin, int _base, int _n_dp) : origin(_origin), rounding_fac(pow(_base, _n_dp)), last(-1.0, _origin), heap() { + for (size_t i = 0; i < (1 << 3); ++i) { + vector_t t = origin; + for (size_t j = 0; j < 3; ++j) { + if (i & (1U << j)) { + t[j] = ceil(t[j] * rounding_fac) / rounding_fac; + } else { + t[j] = floor(t[j] * rounding_fac) / rounding_fac; + } + } + heap.push_back(heapval_t(carve::geom::distance2(origin, t), t)); + } + std::make_heap(heap.begin(), heap.end()); + } + + vector_t next() { + heapval_t curr; + do { + CARVE_ASSERT(heap.size()); + std::pop_heap(heap.begin(), heap.end()); + curr = heap.back(); + heap.pop_back(); + } while (curr == last); + + vector_t t; + + for (int dx = -1; dx <= +1; ++dx) { + t.x = floor(curr.pt.x * rounding_fac + dx) / rounding_fac; + for (int dy = -1; dy <= +1; ++dy) { + t.y = floor(curr.pt.y * rounding_fac + dy) / rounding_fac; + for (int dz = -1; dz <= +1; ++dz) { + t.z = floor(curr.pt.z * rounding_fac + dz) / rounding_fac; + heapval_t h2(carve::geom::distance2(origin, t), t); + if (h2 < curr) { + heap.push_back(h2); + std::push_heap(heap.begin(), heap.end()); + } + } + } + } + last = curr; + return curr.pt; + } + }; + + struct quantization_info_t { + point_enumerator_t *pt; + std::set<face_t *> faces; + + quantization_info_t() : pt(NULL), faces() { + } + + ~quantization_info_t() { + if (pt) delete pt; + } + + aabb_t getAABB() const { + std::set<face_t *>::iterator i = faces.begin(); + aabb_t aabb = (*i)->getAABB(); + while (++i != faces.end()) { + aabb.unionAABB((*i)->getAABB()); + } + return aabb; + } + }; + + void selfIntersectionAwareQuantize(meshset_t *meshset, int base, int n_dp) { + typedef std::unordered_map<vertex_t *, quantization_info_t> vfsmap_t; + + vfsmap_t vertex_qinfo; + + for (size_t m = 0; m < meshset->meshes.size(); ++m) { + mesh_t *mesh = meshset->meshes[m]; + for (size_t f = 0; f < mesh->faces.size(); ++f) { + face_t *face = mesh->faces[f]; + edge_t *e = face->edge; + do { + vertex_qinfo[e->vert].faces.insert(face); + e = e->next; + } while (e != face->edge); + } + } + + face_rtree_t *tree = face_rtree_t::construct_STR(meshset->faceBegin(), meshset->faceEnd(), 4, 4); + + for (vfsmap_t::iterator i = vertex_qinfo.begin(); i != vertex_qinfo.end(); ++i) { + (*i).second.pt = new point_enumerator_t((*i).first->v, base, n_dp); + } + + while (vertex_qinfo.size()) { + std::vector<vertex_t *> quantized; + + std::cerr << "vertex_qinfo.size() == " << vertex_qinfo.size() << std::endl; + + for (vfsmap_t::iterator i = vertex_qinfo.begin(); i != vertex_qinfo.end(); ++i) { + vertex_t *vert = (*i).first; + quantization_info_t &qi = (*i).second; + vector_t q_pt = qi.pt->next(); + aabb_t aabb = qi.getAABB(); + aabb.unionAABB(aabb_t(q_pt)); + + std::vector<face_t *> overlapping; + tree->search(aabb, std::back_inserter(overlapping)); + + + int n_intersections = countIntersectionPairs(qi.faces.begin(), qi.faces.end(), + overlapping.begin(), overlapping.end(), + vert, NULL, q_pt); + + if (n_intersections == 0) { + vert->v = q_pt; + quantized.push_back((*i).first); + tree->updateExtents(aabb); + } + } + for (size_t i = 0; i < quantized.size(); ++i) { + vertex_qinfo.erase(quantized[i]); + } + + if (!quantized.size()) break; + } + } + + + }; + } +} diff --git a/extern/carve/include/carve/octree_decl.hpp b/extern/carve/include/carve/octree_decl.hpp new file mode 100644 index 00000000000..a7e3ff5c77a --- /dev/null +++ b/extern/carve/include/carve/octree_decl.hpp @@ -0,0 +1,193 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/geom3d.hpp> +#include <carve/aabb.hpp> + +#include <carve/polyhedron_base.hpp> + +namespace carve { + + namespace csg { + + const double SLACK_FACTOR=1.0009765625; + const unsigned FACE_SPLIT_THRESHOLD=50U; + const unsigned EDGE_SPLIT_THRESHOLD=50U; + const unsigned POINT_SPLIT_THRESHOLD=20U; + const unsigned MAX_SPLIT_DEPTH=32; + + class Octree { + + public: + class Node { + private: + Node(const Node &node); // undefined. + Node &operator=(const Node &node); // undefined. + + public: + Node *parent; + Node *children[8]; + bool is_leaf; + + carve::geom3d::Vector min; + carve::geom3d::Vector max; + + std::vector<const carve::poly::Geometry<3>::face_t *> faces; + std::vector<const carve::poly::Geometry<3>::edge_t *> edges; + std::vector<const carve::poly::Geometry<3>::vertex_t *> vertices; + + carve::geom3d::AABB aabb; + + Node(); + + Node(const carve::geom3d::Vector &newMin, const carve::geom3d::Vector &newMax); + Node(Node *p, double x1, double y1, double z1, double x2, double y2, double z2); + + ~Node(); + + bool mightContain(const carve::poly::Geometry<3>::face_t &face); + bool mightContain(const carve::poly::Geometry<3>::edge_t &edge); + bool mightContain(const carve::poly::Geometry<3>::vertex_t &p); + bool hasChildren(); + bool hasGeometry(); + + template <class T> + void putInside(const T &input, Node *child, T &output); + + bool split(); + }; + + + + Node *root; + + + + struct no_filter { + bool operator()(const carve::poly::Geometry<3>::edge_t *) { return true; } + bool operator()(const carve::poly::Geometry<3>::face_t *) { return true; } + }; + + + + Octree(); + + ~Octree(); + + + + void setBounds(const carve::geom3d::Vector &min, const carve::geom3d::Vector &max); + void setBounds(carve::geom3d::AABB aabb); + + + + void addEdges(const std::vector<carve::poly::Geometry<3>::edge_t > &edges); + void addFaces(const std::vector<carve::poly::Geometry<3>::face_t > &faces); + void addVertices(const std::vector<const carve::poly::Geometry<3>::vertex_t *> &vertices); + + + + static carve::geom3d::AABB makeAABB(const Node *node); + + + + void doFindEdges(const carve::geom::aabb<3> &aabb, + Node *node, + std::vector<const carve::poly::Geometry<3>::edge_t *> &out, + unsigned depth) const; + void doFindEdges(const carve::geom3d::LineSegment &l, + Node *node, + std::vector<const carve::poly::Geometry<3>::edge_t *> &out, + unsigned depth) const; + void doFindEdges(const carve::geom3d::Vector &v, + Node *node, + std::vector<const carve::poly::Geometry<3>::edge_t *> &out, + unsigned depth) const; + void doFindFaces(const carve::geom::aabb<3> &aabb, + Node *node, + std::vector<const carve::poly::Geometry<3>::face_t *> &out, + unsigned depth) const; + void doFindFaces(const carve::geom3d::LineSegment &l, + Node *node, + std::vector<const carve::poly::Geometry<3>::face_t *> &out, + unsigned depth) const; + + + + void doFindVerticesAllowDupes(const carve::geom3d::Vector &v, + Node *node, + std::vector<const carve::poly::Geometry<3>::vertex_t *> &out, + unsigned depth) const; + + void findVerticesNearAllowDupes(const carve::geom3d::Vector &v, + std::vector<const carve::poly::Geometry<3>::vertex_t *> &out) const; + + + + template<typename filter_t> + void doFindEdges(const carve::poly::Geometry<3>::face_t &f, Node *node, + std::vector<const carve::poly::Geometry<3>::edge_t *> &out, + unsigned depth, + filter_t filter) const; + + template<typename filter_t> + void findEdgesNear(const carve::poly::Geometry<3>::face_t &f, + std::vector<const carve::poly::Geometry<3>::edge_t *> &out, + filter_t filter) const; + + void findEdgesNear(const carve::poly::Geometry<3>::face_t &f, + std::vector<const carve::poly::Geometry<3>::edge_t *> &out) const { + return findEdgesNear(f, out, no_filter()); + } + + + + void findEdgesNear(const carve::geom::aabb<3> &aabb, std::vector<const carve::poly::Geometry<3>::edge_t *> &out) const; + void findEdgesNear(const carve::geom3d::LineSegment &l, std::vector<const carve::poly::Geometry<3>::edge_t *> &out) const; + void findEdgesNear(const carve::poly::Geometry<3>::edge_t &e, std::vector<const carve::poly::Geometry<3>::edge_t *> &out) const; + void findEdgesNear(const carve::geom3d::Vector &v, std::vector<const carve::poly::Geometry<3>::edge_t *> &out) const; + + + + void findFacesNear(const carve::geom::aabb<3> &aabb, std::vector<const carve::poly::Geometry<3>::face_t *> &out) const; + void findFacesNear(const carve::geom3d::LineSegment &l, std::vector<const carve::poly::Geometry<3>::face_t *> &out) const; + void findFacesNear(const carve::poly::Geometry<3>::edge_t &e, std::vector<const carve::poly::Geometry<3>::face_t *> &out) const; + + + + static void doSplit(int maxSplit, Node *node); + + + + template <typename FUNC> + void doIterate(int level, Node *node, const FUNC &f) const; + + template <typename FUNC> + void iterateNodes(const FUNC &f) const; + + + + void splitTree(); + + }; + + } +} diff --git a/extern/carve/include/carve/octree_impl.hpp b/extern/carve/include/carve/octree_impl.hpp new file mode 100644 index 00000000000..5eadb1543a0 --- /dev/null +++ b/extern/carve/include/carve/octree_impl.hpp @@ -0,0 +1,79 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +namespace carve { + namespace csg { + template<typename filter_t> + void Octree::doFindEdges(const carve::poly::Geometry<3>::face_t &f, + Node *node, + std::vector<const carve::poly::Geometry<3>::edge_t *> &out, + unsigned depth, + filter_t filter) const { + if (node == NULL) { + return; + } + + if (node->aabb.intersects(f.aabb) && node->aabb.intersects(f.plane_eqn)) { + if (node->hasChildren()) { + for (int i = 0; i < 8; ++i) { + doFindEdges(f, node->children[i], out, depth + 1, filter); + } + } else { + if (depth < MAX_SPLIT_DEPTH && node->edges.size() > EDGE_SPLIT_THRESHOLD) { + if (!node->split()) { + for (int i = 0; i < 8; ++i) { + doFindEdges(f, node->children[i], out, depth + 1, filter); + } + return; + } + } + for (std::vector<const carve::poly::Geometry<3>::edge_t*>::const_iterator it = node->edges.begin(), e = node->edges.end(); it != e; ++it) { + if ((*it)->tag_once()) { + if (filter(*it)) { + out.push_back(*it); + } + } + } + } + } + } + + template<typename filter_t> + void Octree::findEdgesNear(const carve::poly::Geometry<3>::face_t &f, std::vector<const carve::poly::Geometry<3>::edge_t *> &out, filter_t filter) const { + tagable::tag_begin(); + doFindEdges(f, root, out, 0, filter); + } + + template <typename func_t> + void Octree::doIterate(int level, Node *node, const func_t &f) const{ + f(level, node); + if (node->hasChildren()) { + for (int i = 0; i < 8; ++i) { + doIterate(level + 1, node->children[i], f); + } + } + } + + template <typename func_t> + void Octree::iterateNodes(const func_t &f) const { + doIterate(0, root, f); + } + + } +} diff --git a/extern/carve/include/carve/pointset.hpp b/extern/carve/include/carve/pointset.hpp new file mode 100644 index 00000000000..c635ce47f2f --- /dev/null +++ b/extern/carve/include/carve/pointset.hpp @@ -0,0 +1,24 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/pointset_decl.hpp> +#include <carve/pointset_impl.hpp> +#include <carve/pointset_iter.hpp> diff --git a/extern/carve/include/carve/pointset_decl.hpp b/extern/carve/include/carve/pointset_decl.hpp new file mode 100644 index 00000000000..d09f9e0e724 --- /dev/null +++ b/extern/carve/include/carve/pointset_decl.hpp @@ -0,0 +1,61 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#pragma once + +#include <iterator> +#include <list> +#include <iterator> +#include <limits> + +#include <carve/carve.hpp> +#include <carve/tag.hpp> +#include <carve/geom.hpp> +#include <carve/kd_node.hpp> +#include <carve/geom3d.hpp> +#include <carve/aabb.hpp> + +namespace carve { + namespace point { + + struct Vertex : public tagable { + carve::geom3d::Vector v; + }; + + + + struct vec_adapt_vertex_ptr { + const carve::geom3d::Vector &operator()(const Vertex * const &v) { return v->v; } + carve::geom3d::Vector &operator()(Vertex *&v) { return v->v; } + }; + + + + struct PointSet { + std::vector<Vertex> vertices; + carve::geom3d::AABB aabb; + + PointSet(const std::vector<carve::geom3d::Vector> &points); + PointSet() { + } + + void sortVertices(const carve::geom3d::Vector &axis); + + size_t vertexToIndex_fast(const Vertex *v) const; + }; + + } +} diff --git a/extern/carve/include/carve/pointset_impl.hpp b/extern/carve/include/carve/pointset_impl.hpp new file mode 100644 index 00000000000..71b5f281c0f --- /dev/null +++ b/extern/carve/include/carve/pointset_impl.hpp @@ -0,0 +1,36 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#pragma once + +#include <vector> + +#include <carve/carve.hpp> +#include <carve/tag.hpp> +#include <carve/geom.hpp> +#include <carve/kd_node.hpp> +#include <carve/geom3d.hpp> +#include <carve/aabb.hpp> + +namespace carve { + namespace point { + + inline size_t PointSet::vertexToIndex_fast(const Vertex *v) const { + return v - &vertices[0]; + } + + } +} diff --git a/extern/carve/include/carve/pointset_iter.hpp b/extern/carve/include/carve/pointset_iter.hpp new file mode 100644 index 00000000000..13cf66e4584 --- /dev/null +++ b/extern/carve/include/carve/pointset_iter.hpp @@ -0,0 +1,18 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#pragma once + diff --git a/extern/carve/include/carve/poly.hpp b/extern/carve/include/carve/poly.hpp new file mode 100644 index 00000000000..913d0600aca --- /dev/null +++ b/extern/carve/include/carve/poly.hpp @@ -0,0 +1,24 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/poly_decl.hpp> + +#include <carve/poly_impl.hpp> diff --git a/extern/carve/include/carve/poly_decl.hpp b/extern/carve/include/carve/poly_decl.hpp new file mode 100644 index 00000000000..fe550082dbb --- /dev/null +++ b/extern/carve/include/carve/poly_decl.hpp @@ -0,0 +1,25 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/vertex_decl.hpp> +#include <carve/edge_decl.hpp> +#include <carve/face_decl.hpp> +#include <carve/polyhedron_decl.hpp> diff --git a/extern/carve/include/carve/poly_impl.hpp b/extern/carve/include/carve/poly_impl.hpp new file mode 100644 index 00000000000..db5ae56e6d3 --- /dev/null +++ b/extern/carve/include/carve/poly_impl.hpp @@ -0,0 +1,25 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/vertex_impl.hpp> +#include <carve/edge_impl.hpp> +#include <carve/face_impl.hpp> +#include <carve/polyhedron_impl.hpp> diff --git a/extern/carve/include/carve/polyhedron_base.hpp b/extern/carve/include/carve/polyhedron_base.hpp new file mode 100644 index 00000000000..f55146f2986 --- /dev/null +++ b/extern/carve/include/carve/polyhedron_base.hpp @@ -0,0 +1,149 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/geom3d.hpp> + +#include <carve/vertex_decl.hpp> +#include <carve/edge_decl.hpp> +#include <carve/face_decl.hpp> + +#include <stddef.h> + +namespace carve { + namespace poly { + + + + struct Object { + }; + + + + template<typename array_t> + ptrdiff_t ptrToIndex_fast(const array_t &a, const typename array_t::value_type *v) { + return v - &a[0]; + } + + template<typename array_t> + ptrdiff_t ptrToIndex(const array_t &a, const typename array_t::value_type *v) { + if (v < &a.front() || v > &a.back()) return -1; + return v - &a[0]; + } + + + template<unsigned ndim> + struct Geometry : public Object { + struct Connectivity { + } connectivity; + }; + + + + template<> + struct Geometry<2> : public Object { + typedef Vertex<2> vertex_t; + typedef Edge<2> edge_t; + + struct Connectivity { + std::vector<std::vector<const edge_t *> > vertex_to_edge; + } connectivity; + + std::vector<vertex_t> vertices; + std::vector<edge_t> edges; + + ptrdiff_t vertexToIndex_fast(const vertex_t *v) const { return ptrToIndex_fast(vertices, v); } + ptrdiff_t vertexToIndex(const vertex_t *v) const { return ptrToIndex(vertices, v); } + + ptrdiff_t edgeToIndex_fast(const edge_t *e) const { return ptrToIndex_fast(edges, e); } + ptrdiff_t edgeToIndex(const edge_t *e) const { return ptrToIndex(edges, e); } + + + + // *** connectivity queries + + template<typename T> + int vertexToEdges(const vertex_t *v, T result) const; + }; + + + + template<> + struct Geometry<3> : public Object { + typedef Vertex<3> vertex_t; + typedef Edge<3> edge_t; + typedef Face<3> face_t; + + struct Connectivity { + std::vector<std::vector<const edge_t *> > vertex_to_edge; + std::vector<std::vector<const face_t *> > vertex_to_face; + std::vector<std::vector<const face_t *> > edge_to_face; + } connectivity; + + std::vector<vertex_t> vertices; + std::vector<edge_t> edges; + std::vector<face_t> faces; + + ptrdiff_t vertexToIndex_fast(const vertex_t *v) const { return ptrToIndex_fast(vertices, v); } + ptrdiff_t vertexToIndex(const vertex_t *v) const { return ptrToIndex(vertices, v); } + + ptrdiff_t edgeToIndex_fast(const edge_t *e) const { return ptrToIndex_fast(edges, e); } + ptrdiff_t edgeToIndex(const edge_t *e) const { return ptrToIndex(edges, e); } + + ptrdiff_t faceToIndex_fast(const face_t *f) const { return ptrToIndex_fast(faces, f); } + ptrdiff_t faceToIndex(const face_t *f) const { return ptrToIndex(faces, f); } + + template<typename order_t> + bool orderVertices(order_t order); + + bool orderVertices() { return orderVertices(std::less<vertex_t::vector_t>()); } + + + + // *** connectivity queries + + const face_t *connectedFace(const face_t *, const edge_t *) const; + + template<typename T> + int _faceNeighbourhood(const face_t *f, int depth, T *result) const; + + template<typename T> + int faceNeighbourhood(const face_t *f, int depth, T result) const; + + template<typename T> + int faceNeighbourhood(const edge_t *e, int m_id, int depth, T result) const; + + template<typename T> + int faceNeighbourhood(const vertex_t *v, int m_id, int depth, T result) const; + + template<typename T> + int vertexToEdges(const vertex_t *v, T result) const; + + template<typename T> + int edgeToFaces(const edge_t *e, T result) const; + + template<typename T> + int vertexToFaces(const vertex_t *v, T result) const; + }; + + + + } +} diff --git a/extern/carve/include/carve/polyhedron_decl.hpp b/extern/carve/include/carve/polyhedron_decl.hpp new file mode 100644 index 00000000000..fda2a304691 --- /dev/null +++ b/extern/carve/include/carve/polyhedron_decl.hpp @@ -0,0 +1,184 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/geom3d.hpp> + +#include <carve/polyhedron_base.hpp> +#include <carve/octree_decl.hpp> +#include <carve/collection_types.hpp> + +#include <assert.h> +#include <list> + + +namespace carve { + namespace mesh { + template<unsigned ndim> + class MeshSet; + } + + namespace poly { + class Polyhedron; + } + + poly::Polyhedron *polyhedronFromMesh(const mesh::MeshSet<3> *, int); + + namespace poly { + + class Polyhedron : public Geometry<3> { + private: + friend Polyhedron *carve::polyhedronFromMesh(const mesh::MeshSet<3> *, int); + + Polyhedron() { + } + + Polyhedron &operator=(const Polyhedron &); // not implemented + + // *** initialization + + bool initSpatialIndex(); + void initVertexConnectivity(); + void setFaceAndVertexOwner(); + + bool initConnectivity(); + bool markManifolds(); + bool calcManifoldEmbedding(); + + bool init(); + void faceRecalc(); + + void commonFaceInit(bool _recalc); + + public: + static void collectFaceVertices(std::vector<face_t > &faces, + std::vector<vertex_t > &vertices, + std::unordered_map<const vertex_t *, const vertex_t *> &vmap); + + static void collectFaceVertices(std::vector<face_t > &faces, + std::vector<vertex_t > &vertices); + + std::vector<bool> manifold_is_closed; + std::vector<bool> manifold_is_negative; + + carve::geom3d::AABB aabb; + carve::csg::Octree octree; + + + + // *** construction of Polyhedron objects + + Polyhedron(const Polyhedron &); + + // copy a single manifold + Polyhedron(const Polyhedron &, int m_id); + + // copy a subset of manifolds + Polyhedron(const Polyhedron &, const std::vector<bool> &selected_manifolds); + + Polyhedron(std::vector<face_t > &_faces, + std::vector<vertex_t > &_vertices, + bool _recalc = false); + + Polyhedron(std::vector<face_t > &_faces, + bool _recalc = false); + + Polyhedron(std::list<face_t > &_faces, + bool _recalc = false); + + Polyhedron(const std::vector<carve::geom3d::Vector> &vertices, + int n_faces, + const std::vector<int> &face_indices); + + ~Polyhedron(); + + + + // *** containment queries + + void testVertexAgainstClosedManifolds(const carve::geom3d::Vector &v, + std::map<int, PointClass> &result, + bool ignore_orentation) const; + + PointClass containsVertex(const carve::geom3d::Vector &v, + const face_t **hit_face = NULL, + bool even_odd = false, + int manifold_id = -1) const; + + + + // *** locality queries + + void findEdgesNear(const carve::geom::aabb<3> &aabb, std::vector<const edge_t *> &edges) const; + void findEdgesNear(const carve::geom3d::LineSegment &l, std::vector<const edge_t *> &edges) const; + void findEdgesNear(const carve::geom3d::Vector &v, std::vector<const edge_t *> &edges) const; + void findEdgesNear(const face_t &face, std::vector<const edge_t *> &edges) const; + void findEdgesNear(const edge_t &edge, std::vector<const edge_t *> &edges) const; + + void findFacesNear(const carve::geom::aabb<3> &aabb, std::vector<const face_t *> &faces) const; + void findFacesNear(const carve::geom3d::LineSegment &l, std::vector<const face_t *> &faces) const; + void findFacesNear(const edge_t &edge, std::vector<const face_t *> &faces) const; + + + + // *** manifold queries + + inline bool vertexOnManifold(const vertex_t *v, int m_id) const; + inline bool edgeOnManifold(const edge_t *e, int m_id) const; + + template<typename T> + int vertexManifolds(const vertex_t *v, T result) const; + + template<typename T> + int edgeManifolds(const edge_t *e, T result) const; + + size_t manifoldCount() const; + + bool hasOpenManifolds() const; + + + + + // *** transformation + + // flip face directions + void invertAll(); + void invert(const std::vector<bool> &selected_manifolds); + + void invert(int m_id); + void invert(); + + // matrix transform of vertices + void transform(const carve::math::Matrix &xform); + + // arbitrary function transform of vertices + template<typename T> + void transform(const T &xform); + + void print(std::ostream &) const; + + void canonicalize(); + }; + + std::ostream &operator<<(std::ostream &, const Polyhedron &); + + } + +} diff --git a/extern/carve/include/carve/polyhedron_impl.hpp b/extern/carve/include/carve/polyhedron_impl.hpp new file mode 100644 index 00000000000..06d841c7192 --- /dev/null +++ b/extern/carve/include/carve/polyhedron_impl.hpp @@ -0,0 +1,287 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/timing.hpp> + +#include <assert.h> +#include <list> + +namespace carve { + namespace poly { + + + + template<typename order_t> + struct VPtrSort { + order_t order; + + VPtrSort(const order_t &_order) : order(_order) {} + bool operator()(carve::poly::Polyhedron::vertex_t const *a, + carve::poly::Polyhedron::vertex_t const *b) const { + return order(a->v, b->v); + } + }; + + template<typename order_t> + bool Geometry<3>::orderVertices(order_t order) { + static carve::TimingName FUNC_NAME("Geometry<3>::orderVertices()"); + carve::TimingBlock block(FUNC_NAME); + + std::vector<vertex_t *> vptr; + std::vector<vertex_t *> vmap; + std::vector<vertex_t> vout; + const size_t N = vertices.size(); + + vptr.reserve(N); + vout.reserve(N); + vmap.resize(N); + + for (size_t i = 0; i != N; ++i) { + vptr.push_back(&vertices[i]); + } + std::sort(vptr.begin(), vptr.end(), VPtrSort<order_t>(order)); + + for (size_t i = 0; i != N; ++i) { + vout.push_back(*vptr[i]); + vmap[vertexToIndex_fast(vptr[i])] = &vout[i]; + } + + for (size_t i = 0; i < faces.size(); ++i) { + face_t &f = faces[i]; + for (size_t j = 0; j < f.nVertices(); ++j) { + f.vertex(j) = vmap[vertexToIndex_fast(f.vertex(j))]; + } + } + for (size_t i = 0; i < edges.size(); ++i) { + edges[i].v1 = vmap[vertexToIndex_fast(edges[i].v1)]; + edges[i].v2 = vmap[vertexToIndex_fast(edges[i].v2)]; + } + + vout.swap(vertices); + + return true; + } + + + + template<typename T> + int Geometry<3>::_faceNeighbourhood(const face_t *f, int depth, T *result) const { + if (depth < 0 || f->is_tagged()) return 0; + + f->tag(); + *(*result)++ = f; + + int r = 1; + for (size_t i = 0; i < f->edges.size(); ++i) { + const std::vector<const face_t *> &edge_faces = connectivity.edge_to_face[edgeToIndex_fast(f->edges[i])]; + const face_t *f2 = connectedFace(f, f->edges[i]); + if (f2) { + r += _faceNeighbourhood(f2, depth - 1, (*result)); + } + } + return r; + } + + + + template<typename T> + int Geometry<3>::faceNeighbourhood(const face_t *f, int depth, T result) const { + tagable::tag_begin(); + + return _faceNeighbourhood(f, depth, &result); + } + + + + template<typename T> + int Geometry<3>::faceNeighbourhood(const edge_t *e, int m_id, int depth, T result) const { + tagable::tag_begin(); + + int r = 0; + const std::vector<const face_t *> &edge_faces = connectivity.edge_to_face[edgeToIndex_fast(e)]; + for (size_t i = 0; i < edge_faces.size(); ++i) { + face_t *f = edge_faces[i]; + if (f && f->manifold_id == m_id) { r += _faceNeighbourhood(f, depth, &result); } + } + return r; + } + + + + template<typename T> + int Geometry<3>::faceNeighbourhood(const vertex_t *v, int m_id, int depth, T result) const { + tagable::tag_begin(); + + int r = 0; + const std::vector<const face_t *> &vertex_faces = connectivity.vertex_to_face[vertexToIndex_fast(v)]; + for (size_t i = 0; i < vertex_faces.size(); ++i) { + face_t *f = vertex_faces[i]; + if (f && f->manifold_id == m_id) { r += _faceNeighbourhood(f, depth, &result); } + } + return r; + } + + + + // accessing connectivity information. + template<typename T> + int Geometry<3>::vertexToEdges(const vertex_t *v, T result) const { + std::vector<const edge_t *> &e = connectivity.vertex_to_edge[vertexToIndex_fast(v)]; + std::copy(e.begin(), e.end(), result); + return e.size(); + } + + + + template<typename T> + int Geometry<3>::vertexToFaces(const vertex_t *v, T result) const { + const std::vector<const face_t *> &vertex_faces = connectivity.vertex_to_face[vertexToIndex_fast(v)]; + int c = 0; + for (size_t i = 0; i < vertex_faces.size(); ++i) { + *result++ = vertex_faces[i]; ++c; + } + return c; + } + + + + template<typename T> + int Geometry<3>::edgeToFaces(const edge_t *e, T result) const { + const std::vector<const face_t *> &edge_faces = connectivity.edge_to_face[edgeToIndex_fast(e)]; + int c = 0; + for (size_t i = 0; i < edge_faces.size(); ++i) { + if (edge_faces[i] != NULL) { *result++ = edge_faces[i]; ++c; } + } + return c; + } + + + + inline const Geometry<3>::face_t *Geometry<3>::connectedFace(const face_t *f, const edge_t *e) const { + const std::vector<const face_t *> &edge_faces = connectivity.edge_to_face[edgeToIndex_fast(e)]; + for (size_t i = 0; i < (edge_faces.size() & ~1U); i++) { + if (edge_faces[i] == f) return edge_faces[i^1]; + } + return NULL; + } + + + + inline void Polyhedron::invert(int m_id) { + std::vector<bool> selected_manifolds(manifold_is_closed.size(), false); + if (m_id >=0 && (unsigned)m_id < selected_manifolds.size()) selected_manifolds[m_id] = true; + invert(selected_manifolds); + } + + + + inline void Polyhedron::invert() { + invertAll(); + } + + + + inline bool Polyhedron::edgeOnManifold(const edge_t *e, int m_id) const { + const std::vector<const face_t *> &edge_faces = connectivity.edge_to_face[edgeToIndex_fast(e)]; + + for (size_t i = 0; i < edge_faces.size(); ++i) { + if (edge_faces[i] && edge_faces[i]->manifold_id == m_id) return true; + } + return false; + } + + inline bool Polyhedron::vertexOnManifold(const vertex_t *v, int m_id) const { + const std::vector<const face_t *> &f = connectivity.vertex_to_face[vertexToIndex_fast(v)]; + + for (size_t i = 0; i < f.size(); ++i) { + if (f[i]->manifold_id == m_id) return true; + } + return false; + } + + + + template<typename T> + int Polyhedron::edgeManifolds(const edge_t *e, T result) const { + const std::vector<const face_t *> &edge_faces = connectivity.edge_to_face[edgeToIndex_fast(e)]; + + for (size_t i = 0; i < (edge_faces.size() & ~1U); i += 2) { + const face_t *f1 = edge_faces[i]; + const face_t *f2 = edge_faces[i+1]; + assert (f1 || f2); + if (f1) + *result++ = f1->manifold_id; + else if (f2) + *result++ = f2->manifold_id; + } + return edge_faces.size() >> 1; + } + + + + template<typename T> + int Polyhedron::vertexManifolds(const vertex_t *v, T result) const { + const std::vector<const face_t *> &f = connectivity.vertex_to_face[vertexToIndex_fast(v)]; + std::set<int> em; + + for (size_t i = 0; i < f.size(); ++i) { + em.insert(f[i]->manifold_id); + } + + std::copy(em.begin(), em.end(), result); + return em.size(); + } + + + + template<typename T> + void Polyhedron::transform(const T &xform) { + for (size_t i = 0; i < vertices.size(); i++) { + vertices[i].v = xform(vertices[i].v); + } + faceRecalc(); + init(); + } + + + + inline size_t Polyhedron::manifoldCount() const { + return manifold_is_closed.size(); + } + + + + inline bool Polyhedron::hasOpenManifolds() const { + for (size_t i = 0; i < manifold_is_closed.size(); ++i) { + if (!manifold_is_closed[i]) return true; + } + return false; + } + + + + inline std::ostream &operator<<(std::ostream &o, const Polyhedron &p) { + p.print(o); + return o; + } + + + + } +} diff --git a/extern/carve/include/carve/polyline.hpp b/extern/carve/include/carve/polyline.hpp new file mode 100644 index 00000000000..a6789f8d0c5 --- /dev/null +++ b/extern/carve/include/carve/polyline.hpp @@ -0,0 +1,24 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/polyline_decl.hpp> +#include <carve/polyline_impl.hpp> +#include <carve/polyline_iter.hpp> diff --git a/extern/carve/include/carve/polyline_decl.hpp b/extern/carve/include/carve/polyline_decl.hpp new file mode 100644 index 00000000000..a29c56656ff --- /dev/null +++ b/extern/carve/include/carve/polyline_decl.hpp @@ -0,0 +1,151 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#pragma once + +#include <iterator> +#include <list> +#include <iterator> +#include <limits> + +#include <carve/carve.hpp> +#include <carve/tag.hpp> +#include <carve/geom.hpp> +#include <carve/kd_node.hpp> +#include <carve/geom3d.hpp> +#include <carve/aabb.hpp> + +namespace carve { + namespace line { + + struct PolylineEdge; + struct Polyline; + struct polyline_vertex_const_iter; + struct polyline_vertex_iter; + struct polyline_edge_const_iter; + struct polyline_edge_iter; + + + + struct Vertex : public tagable { + carve::geom3d::Vector v; + std::list<std::pair<PolylineEdge *, PolylineEdge *> > edge_pairs; + + void addEdgePair(PolylineEdge *in, PolylineEdge *out) { + edge_pairs.push_back(std::make_pair(in, out)); + } + }; + + + + struct vec_adapt_vertex_ptr { + const carve::geom3d::Vector &operator()(const Vertex * const &v) { return v->v; } + carve::geom3d::Vector &operator()(Vertex *&v) { return v->v; } + }; + + + + struct PolylineEdge : public tagable { + Polyline *parent; + unsigned edgenum; + Vertex *v1, *v2; + + PolylineEdge(Polyline *_parent, int _edgenum, Vertex *_v1, Vertex *_v2); + + carve::geom3d::AABB aabb() const; + + inline PolylineEdge *prevEdge() const; + inline PolylineEdge *nextEdge() const; + }; + + + + struct Polyline { + bool closed; + std::vector<PolylineEdge *> edges; + + Polyline(); + + size_t vertexCount() const; + + size_t edgeCount() const; + + const PolylineEdge *edge(size_t e) const; + + PolylineEdge *edge(size_t e); + + const Vertex *vertex(size_t v) const; + + Vertex *vertex(size_t v); + + bool isClosed() const; + + polyline_vertex_const_iter vbegin() const; + polyline_vertex_const_iter vend() const; + polyline_vertex_iter vbegin(); + polyline_vertex_iter vend(); + + polyline_edge_const_iter ebegin() const; + polyline_edge_const_iter eend() const; + polyline_edge_iter ebegin(); + polyline_edge_iter eend(); + + carve::geom3d::AABB aabb() const; + + template<typename iter_t> + void _init(bool c, iter_t begin, iter_t end, std::vector<Vertex> &vertices); + + template<typename iter_t> + void _init(bool closed, iter_t begin, iter_t end, std::vector<Vertex> &vertices, std::forward_iterator_tag); + + template<typename iter_t> + void _init(bool closed, iter_t begin, iter_t end, std::vector<Vertex> &vertices, std::random_access_iterator_tag); + + template<typename iter_t> + Polyline(bool closed, iter_t begin, iter_t end, std::vector<Vertex> &vertices); + + ~Polyline() { + for (size_t i = 0; i < edges.size(); ++i) { + delete edges[i]; + } + } + }; + + + + struct PolylineSet { + typedef std::list<Polyline *> line_list; + typedef line_list::iterator line_iter; + typedef line_list::const_iterator const_line_iter; + + std::vector<Vertex> vertices; + line_list lines; + carve::geom3d::AABB aabb; + + PolylineSet(const std::vector<carve::geom3d::Vector> &points); + PolylineSet() { + } + + template<typename iter_t> + void addPolyline(bool closed, iter_t begin, iter_t end); + + void sortVertices(const carve::geom3d::Vector &axis); + + size_t vertexToIndex_fast(const Vertex *v) const; + }; + + } +} diff --git a/extern/carve/include/carve/polyline_impl.hpp b/extern/carve/include/carve/polyline_impl.hpp new file mode 100644 index 00000000000..3c17980a9af --- /dev/null +++ b/extern/carve/include/carve/polyline_impl.hpp @@ -0,0 +1,160 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#pragma once + +namespace carve { + namespace line { + + inline PolylineEdge::PolylineEdge(Polyline *_parent, int _edgenum, Vertex *_v1, Vertex *_v2) : + tagable(), parent(_parent), edgenum(_edgenum), v1(_v1), v2(_v2) { + } + + inline carve::geom3d::AABB PolylineEdge::aabb() const { + carve::geom3d::AABB a; + a.fit(v1->v, v2->v); + return a; + } + + inline PolylineEdge *PolylineEdge::prevEdge() const { + if (edgenum) { + return parent->edge(edgenum - 1); + } else { + if (parent->closed) { + return parent->edge(parent->edgeCount() - 1); + } else { + return NULL; + } + } + } + + inline PolylineEdge *PolylineEdge::nextEdge() const { + if (edgenum + 1 < parent->edgeCount()) { + return parent->edge(edgenum + 1); + } else { + if (parent->closed) { + return parent->edge(0); + } else { + return NULL; + } + } + } + + + + inline Polyline::Polyline() : edges() { + } + + inline size_t Polyline::vertexCount() const { + return edgeCount() + (closed ? 0 : 1); + } + + inline size_t Polyline::edgeCount() const { + return edges.size(); + } + + inline const PolylineEdge *Polyline::edge(size_t e) const { + return edges[e % edges.size()]; + } + + inline PolylineEdge *Polyline::edge(size_t e) { + return edges[e % edges.size()]; + } + + inline const Vertex *Polyline::vertex(size_t v) const { + if (closed) { + v %= edgeCount(); + } else if (v >= edgeCount()) { + return v == edgeCount() ? edges.back()->v2 : NULL; + } + return edges[v]->v1; + } + + inline Vertex *Polyline::vertex(size_t v) { + if (closed) { + v %= edgeCount(); + } else if (v >= edgeCount()) { + return v == edgeCount() ? edges.back()->v2 : NULL; + } + return edges[v]->v1; + } + + inline bool Polyline::isClosed() const { + return closed; + } + + template<typename iter_t> + void Polyline::_init(bool c, iter_t begin, iter_t end, std::vector<Vertex> &vertices) { + closed = c; + + PolylineEdge *e; + if (begin == end) return; + size_t v1 = (int)*begin++; + if (begin == end) return; + + while (begin != end) { + size_t v2 = (int)*begin++; + e = new PolylineEdge(this, edges.size(), &vertices[v1], &vertices[v2]); + edges.push_back(e); + v1 = v2; + } + + if (closed) { + e = new PolylineEdge(this, edges.size(), edges.back()->v2, edges.front()->v1); + edges.push_back(e); + + edges.front()->v1->addEdgePair(edges.back(), edges.front()); + for (size_t i = 1; i < edges.size(); ++i) { + edges[i]->v1->addEdgePair(edges[i-1], edges[i]); + } + } else { + edges.front()->v1->addEdgePair(NULL, edges.front()); + for (size_t i = 1; i < edges.size(); ++i) { + edges[i]->v1->addEdgePair(edges[i-1], edges[i]); + } + edges.back()->v2->addEdgePair(edges.back(), NULL); + } + } + + template<typename iter_t> + void Polyline::_init(bool closed, iter_t begin, iter_t end, std::vector<Vertex> &vertices, std::forward_iterator_tag) { + _init(closed, begin, end, vertices); + } + + template<typename iter_t> + void Polyline::_init(bool closed, iter_t begin, iter_t end, std::vector<Vertex> &vertices, std::random_access_iterator_tag) { + edges.reserve(end - begin - (closed ? 0 : 1)); + _init(closed, begin, end, vertices); + } + + template<typename iter_t> + Polyline::Polyline(bool closed, iter_t begin, iter_t end, std::vector<Vertex> &vertices) { + _init(closed, begin, end, vertices, typename std::iterator_traits<iter_t>::iterator_category()); + } + + + + template<typename iter_t> + void PolylineSet::addPolyline(bool closed, iter_t begin, iter_t end) { + Polyline *p = new Polyline(closed, begin, end, vertices); + lines.push_back(p); + } + + inline size_t PolylineSet::vertexToIndex_fast(const Vertex *v) const { + return v - &vertices[0]; + } + } +} diff --git a/extern/carve/include/carve/polyline_iter.hpp b/extern/carve/include/carve/polyline_iter.hpp new file mode 100644 index 00000000000..5092f9abecd --- /dev/null +++ b/extern/carve/include/carve/polyline_iter.hpp @@ -0,0 +1,198 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + +#pragma once + +#include <iterator> +#include <list> +#include <iterator> +#include <limits> + +#include <carve/polyline_decl.hpp> + +namespace carve { + namespace line { + + struct polyline_vertex_iter : public std::iterator<std::random_access_iterator_tag, Vertex *> { + Polyline *base; + size_t idx; + + polyline_vertex_iter(Polyline *_base) : base(_base), idx(0) { + } + + polyline_vertex_iter(Polyline *_base, size_t _idx) : base(_base), idx(_idx) { + } + + polyline_vertex_iter operator++(int) { return polyline_vertex_iter(base, idx++); } + polyline_vertex_iter &operator++() { ++idx; return *this; } + polyline_vertex_iter &operator+=(int v) { idx += v; return *this; } + + polyline_vertex_iter operator--(int) { return polyline_vertex_iter(base, idx--); } + polyline_vertex_iter &operator--() { --idx; return *this; } + polyline_vertex_iter &operator-=(int v) { idx -= v; return *this; } + + Vertex *operator*() const { + return base->vertex(idx); + } + }; + + + + static inline ptrdiff_t operator-(const polyline_vertex_iter &a, const polyline_vertex_iter &b) { return a.idx - b.idx; } + + static inline bool operator==(const polyline_vertex_iter&a, const polyline_vertex_iter &b) { return a.idx == b.idx; } + static inline bool operator!=(const polyline_vertex_iter&a, const polyline_vertex_iter &b) { return a.idx != b.idx; } + static inline bool operator<(const polyline_vertex_iter&a, const polyline_vertex_iter &b) { return a.idx < b.idx; } + static inline bool operator>(const polyline_vertex_iter&a, const polyline_vertex_iter &b) { return a.idx > b.idx; } + static inline bool operator<=(const polyline_vertex_iter&a, const polyline_vertex_iter &b) { return a.idx <= b.idx; } + static inline bool operator>=(const polyline_vertex_iter&a, const polyline_vertex_iter &b) { return a.idx >= b.idx; } + + + + struct polyline_vertex_const_iter : public std::iterator<std::random_access_iterator_tag, Vertex *> { + const Polyline *base; + size_t idx; + + polyline_vertex_const_iter(const Polyline *_base) : base(_base), idx(0) { + } + + polyline_vertex_const_iter(const Polyline *_base, size_t _idx) : base(_base), idx(_idx) { + } + + polyline_vertex_const_iter operator++(int) { return polyline_vertex_const_iter(base, idx++); } + polyline_vertex_const_iter &operator++() { ++idx; return *this; } + polyline_vertex_const_iter &operator+=(int v) { idx += v; return *this; } + + polyline_vertex_const_iter operator--(int) { return polyline_vertex_const_iter(base, idx--); } + polyline_vertex_const_iter &operator--() { --idx; return *this; } + polyline_vertex_const_iter &operator-=(int v) { idx -= v; return *this; } + + const Vertex *operator*() const { + return base->vertex(idx); + } + }; + + + + static inline ptrdiff_t operator-(const polyline_vertex_const_iter &a, const polyline_vertex_const_iter &b) { return a.idx - b.idx; } + + static inline bool operator==(const polyline_vertex_const_iter&a, const polyline_vertex_const_iter &b) { return a.idx == b.idx; } + static inline bool operator!=(const polyline_vertex_const_iter&a, const polyline_vertex_const_iter &b) { return a.idx != b.idx; } + static inline bool operator<(const polyline_vertex_const_iter&a, const polyline_vertex_const_iter &b) { return a.idx < b.idx; } + static inline bool operator>(const polyline_vertex_const_iter&a, const polyline_vertex_const_iter &b) { return a.idx > b.idx; } + static inline bool operator<=(const polyline_vertex_const_iter&a, const polyline_vertex_const_iter &b) { return a.idx <= b.idx; } + static inline bool operator>=(const polyline_vertex_const_iter&a, const polyline_vertex_const_iter &b) { return a.idx >= b.idx; } + + inline polyline_vertex_const_iter Polyline::vbegin() const { + return polyline_vertex_const_iter(this, 0); + } + inline polyline_vertex_const_iter Polyline::vend() const { + return polyline_vertex_const_iter(this, vertexCount()); + } + inline polyline_vertex_iter Polyline::vbegin() { + return polyline_vertex_iter(this, 0); + } + inline polyline_vertex_iter Polyline::vend() { + return polyline_vertex_iter(this, vertexCount()); + } + + + + struct polyline_edge_iter : public std::iterator<std::random_access_iterator_tag, PolylineEdge *> { + Polyline *base; + size_t idx; + + polyline_edge_iter(Polyline *_base) : base(_base), idx(0) { + } + + polyline_edge_iter(Polyline *_base, size_t _idx) : base(_base), idx(_idx) { + } + + polyline_edge_iter operator++(int) { return polyline_edge_iter(base, idx++); } + polyline_edge_iter &operator++() { ++idx; return *this; } + polyline_edge_iter &operator+=(int v) { idx += v; return *this; } + + polyline_edge_iter operator--(int) { return polyline_edge_iter(base, idx--); } + polyline_edge_iter &operator--() { --idx; return *this; } + polyline_edge_iter &operator-=(int v) { idx -= v; return *this; } + + PolylineEdge *operator*() const { + return base->edge(idx); + } + }; + + + + static inline int operator-(const polyline_edge_iter&a, const polyline_edge_iter &b) { return a.idx - b.idx; } + + static inline bool operator==(const polyline_edge_iter&a, const polyline_edge_iter &b) { return a.idx == b.idx; } + static inline bool operator!=(const polyline_edge_iter&a, const polyline_edge_iter &b) { return a.idx != b.idx; } + static inline bool operator<(const polyline_edge_iter&a, const polyline_edge_iter &b) { return a.idx < b.idx; } + static inline bool operator>(const polyline_edge_iter&a, const polyline_edge_iter &b) { return a.idx > b.idx; } + static inline bool operator<=(const polyline_edge_iter&a, const polyline_edge_iter &b) { return a.idx <= b.idx; } + static inline bool operator>=(const polyline_edge_iter&a, const polyline_edge_iter &b) { return a.idx >= b.idx; } + + + + struct polyline_edge_const_iter : public std::iterator<std::random_access_iterator_tag, PolylineEdge *> { + const Polyline *base; + size_t idx; + + polyline_edge_const_iter(const Polyline *_base) : base(_base), idx(0) { + } + + polyline_edge_const_iter(const Polyline *_base, size_t _idx) : base(_base), idx(_idx) { + } + + polyline_edge_const_iter operator++(int) { return polyline_edge_const_iter(base, idx++); } + polyline_edge_const_iter &operator++() { ++idx; return *this; } + polyline_edge_const_iter &operator+=(int v) { idx += v; return *this; } + + polyline_edge_const_iter operator--(int) { return polyline_edge_const_iter(base, idx--); } + polyline_edge_const_iter &operator--() { --idx; return *this; } + polyline_edge_const_iter &operator-=(int v) { idx -= v; return *this; } + + const PolylineEdge *operator*() const { + return base->edge(idx); + } + }; + + + + static inline int operator-(const polyline_edge_const_iter&a, const polyline_edge_const_iter &b) { return a.idx - b.idx; } + + static inline bool operator==(const polyline_edge_const_iter&a, const polyline_edge_const_iter &b) { return a.idx == b.idx; } + static inline bool operator!=(const polyline_edge_const_iter&a, const polyline_edge_const_iter &b) { return a.idx != b.idx; } + static inline bool operator<(const polyline_edge_const_iter&a, const polyline_edge_const_iter &b) { return a.idx < b.idx; } + static inline bool operator>(const polyline_edge_const_iter&a, const polyline_edge_const_iter &b) { return a.idx > b.idx; } + static inline bool operator<=(const polyline_edge_const_iter&a, const polyline_edge_const_iter &b) { return a.idx <= b.idx; } + static inline bool operator>=(const polyline_edge_const_iter&a, const polyline_edge_const_iter &b) { return a.idx >= b.idx; } + + inline polyline_edge_const_iter Polyline::ebegin() const { + return polyline_edge_const_iter(this, 0); + } + inline polyline_edge_const_iter Polyline::eend() const { + return polyline_edge_const_iter(this, edgeCount()); + } + inline polyline_edge_iter Polyline::ebegin() { + return polyline_edge_iter(this, 0); + } + inline polyline_edge_iter Polyline::eend() { + return polyline_edge_iter(this, edgeCount()); + } + + } +} diff --git a/extern/carve/include/carve/rescale.hpp b/extern/carve/include/carve/rescale.hpp new file mode 100644 index 00000000000..6478298cfe6 --- /dev/null +++ b/extern/carve/include/carve/rescale.hpp @@ -0,0 +1,100 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/vector.hpp> +#include <carve/aabb.hpp> +#include <carve/matrix.hpp> + +#include <limits> + +namespace carve { + namespace rescale { + + template<typename T> + T calc_scale(T max) { + const int radix = std::numeric_limits<T>::radix; + + T div = T(1); + T m = fabs(max); + while (div < m) div *= radix; + m *= radix; + while (div > m) div /= radix; + return div; + } + + template<typename T> + T calc_delta(T min, T max) { + const int radix = std::numeric_limits<T>::radix; + + if (min >= T(0) || max <= T(0)) { + bool neg = false; + if (max <= T(0)) { + min = -min; + max = -max; + std::swap(min, max); + neg = true; + } + T t = T(1); + while (t > max) t /= radix; + while (t <= max/radix) t *= radix; + volatile T temp = t + min; + temp -= t; + if (neg) temp = -temp; + return temp; + } else { + return T(0); + } + } + + struct rescale { + double dx, dy, dz, scale; + + void init(double minx, double miny, double minz, double maxx, double maxy, double maxz) { + dx = calc_delta(minx, maxx); minx -= dx; maxx -= dx; + dy = calc_delta(miny, maxy); miny -= dy; maxy -= dy; + dz = calc_delta(minz, maxz); minz -= dz; maxz -= dz; + scale = calc_scale(std::max(std::max(fabs(minz), fabs(maxz)), + std::max(std::max(fabs(minx), fabs(maxx)), + std::max(fabs(miny), fabs(maxy))))); + } + + rescale(double minx, double miny, double minz, double maxx, double maxy, double maxz) { + init(minx, miny, minz, maxx, maxy, maxz); + } + rescale(const carve::geom3d::Vector &min, const carve::geom3d::Vector &max) { + init(min.x, min.y, min.z, max.x, max.y, max.z); + } + }; + + struct fwd { + rescale r; + fwd(const rescale &_r) : r(_r) { } + carve::geom3d::Vector operator()(const carve::geom3d::Vector &v) const { return carve::geom::VECTOR((v.x - r.dx) / r.scale, (v.y - r.dy) / r.scale, (v.z - r.dz) / r.scale); } + }; + + struct rev { + rescale r; + rev(const rescale &_r) : r(_r) { } + carve::geom3d::Vector operator()(const carve::geom3d::Vector &v) const { return carve::geom::VECTOR((v.x * r.scale) + r.dx, (v.y * r.scale) + r.dy, (v.z * r.scale) + r.dz); } + }; + + } +} diff --git a/extern/carve/include/carve/rtree.hpp b/extern/carve/include/carve/rtree.hpp new file mode 100644 index 00000000000..77f93c14e08 --- /dev/null +++ b/extern/carve/include/carve/rtree.hpp @@ -0,0 +1,501 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/geom.hpp> +#include <carve/aabb.hpp> + +#include <iostream> + +#include <cmath> +#include <limits> + +#if defined(HAVE_STDINT_H) +# include <stdint.h> +#endif + +namespace carve { + namespace geom { + + template<unsigned ndim, + typename data_t, + typename aabb_calc_t = carve::geom::get_aabb<ndim, data_t> > + struct RTreeNode { + typedef aabb<ndim> aabb_t; + typedef vector<ndim> vector_t; + typedef RTreeNode<ndim, data_t, aabb_calc_t> node_t; + + aabb_t bbox; + node_t *child; + node_t *sibling; + std::vector<data_t> data; + + aabb_t getAABB() const { return bbox; } + + + + struct data_aabb_t { + aabb_t bbox; + data_t data; + + data_aabb_t() { } + data_aabb_t(const data_t &_data) : bbox(aabb_calc_t()(_data)), data(_data) { + } + + aabb_t getAABB() const { return bbox; } + + struct cmp { + size_t dim; + cmp(size_t _dim) : dim(_dim) { } + bool operator()(const data_aabb_t &a, const data_aabb_t &b) { + return a.bbox.pos.v[dim] < b.bbox.pos.v[dim]; + } + }; + }; + + // Fill an rtree node with a set of (data, aabb) pairs. + template<typename iter_t> + void _fill(iter_t begin, iter_t end, data_aabb_t) { + data.reserve(std::distance(begin, end)); + for (iter_t i = begin; i != end; ++i) { + data.push_back((*i).data); + } + bbox.fit(begin, end); + } + + // Fill an rtree node with a set of data. + template<typename iter_t> + void _fill(iter_t begin, iter_t end, data_t) { + data.reserve(std::distance(begin, end)); + std::copy(begin, end, std::back_inserter(data)); + bbox.fit(begin, end, aabb_calc_t()); + } + + // Fill an rtree node with a set of child nodes. + template<typename iter_t> + void _fill(iter_t begin, iter_t end, node_t *) { + iter_t i = begin; + node_t *curr = child = *i; + while (++i != end) { + curr->sibling = *i; + curr = curr->sibling; + } + bbox.fit(begin, end); + } + + // Search the rtree for objects that intersect obj (generally an aabb). + // The aabb class must provide a method intersects(obj_t). + template<typename obj_t, typename out_iter_t> + void search(const obj_t &obj, out_iter_t out) const { + if (!bbox.intersects(obj)) return; + if (child) { + for (node_t *node = child; node; node = node->sibling) { + node->search(obj, out); + } + } else { + std::copy(data.begin(), data.end(), out); + } + } + + // update the bounding box extents of nodes that intersect obj (generally an aabb). + // The aabb class must provide a method intersects(obj_t). + template<typename obj_t> + void updateExtents(const obj_t &obj) { + if (!bbox.intersects(obj)) return; + + if (child) { + node_t *node = child; + node->updateExtents(obj); + bbox = node->bbox; + for (node = node->sibling; node; node = node->sibling) { + node->updateExtents(obj); + bbox.unionAABB(node->bbox); + } + } else { + bbox.fit(data.begin(), data.end()); + } + } + + // update the bounding box extents of nodes that intersect obj (generally an aabb). + // The aabb class must provide a method intersects(obj_t). + bool remove(const data_t &val, const aabb_t &val_aabb) { + if (!bbox.intersects(val_aabb)) return false; + + if (child) { + node_t *node = child; + node->remove(val, val_aabb); + bbox = node->bbox; + bool removed = false; + for (node = node->sibling; node; node = node->sibling) { + if (!removed) removed = node->remove(val, val_aabb); + bbox.unionAABB(node->bbox); + } + return removed; + } else { + typename std::vector<data_t>::iterator i = std::remove(data.begin(), data.end(), val); + if (i == data.end()) { + return false; + } + data.erase(i, data.end()); + bbox.fit(data.begin(), data.end()); + return true; + } + } + + template<typename iter_t> + RTreeNode(iter_t begin, iter_t end) : bbox(), child(NULL), sibling(NULL), data() { + _fill(begin, end, typename std::iterator_traits<iter_t>::value_type()); + } + + + + // functor for ordering nodes by increasing aabb midpoint, along a specified axis. + struct aabb_cmp_mid { + size_t dim; + aabb_cmp_mid(size_t _dim) : dim(_dim) { } + + bool operator()(const node_t *a, const node_t *b) { + return a->bbox.mid(dim) < b->bbox.mid(dim); + } + bool operator()(const data_aabb_t &a, const data_aabb_t &b) { + return a.bbox.mid(dim) < b.bbox.mid(dim); + } + }; + + // functor for ordering nodes by increasing aabb minimum, along a specified axis. + struct aabb_cmp_min { + size_t dim; + aabb_cmp_min(size_t _dim) : dim(_dim) { } + + bool operator()(const node_t *a, const node_t *b) { + return a->bbox.min(dim) < b->bbox.min(dim); + } + bool operator()(const data_aabb_t &a, const data_aabb_t &b) { + return a.bbox.min(dim) < b.bbox.min(dim); + } + }; + + // functor for ordering nodes by increasing aabb maximum, along a specified axis. + struct aabb_cmp_max { + size_t dim; + aabb_cmp_max(size_t _dim) : dim(_dim) { } + + bool operator()(const node_t *a, const node_t *b) { + return a->bbox.max(dim) < b->bbox.max(dim); + } + bool operator()(const data_aabb_t &a, const data_aabb_t &b) { + return a.bbox.max(dim) < b.bbox.max(dim); + } + }; + + // facade for projecting node bounding box onto an axis. + struct aabb_extent { + size_t dim; + aabb_extent(size_t _dim) : dim(_dim) { } + + double min(const node_t *a) { return a->bbox.pos.v[dim] - a->bbox.extent.v[dim]; } + double max(const node_t *a) { return a->bbox.pos.v[dim] + a->bbox.extent.v[dim]; } + double len(const node_t *a) { return 2.0 * a->bbox.extent.v[dim]; } + double min(const data_aabb_t &a) { return a.bbox.pos.v[dim] - a.bbox.extent.v[dim]; } + double max(const data_aabb_t &a) { return a.bbox.pos.v[dim] + a.bbox.extent.v[dim]; } + double len(const data_aabb_t &a) { return 2.0 * a.bbox.extent.v[dim]; } + }; + + template<typename iter_t> + static void makeNodes(const iter_t begin, + const iter_t end, + size_t dim_num, + uint32_t dim_mask, + size_t child_size, + std::vector<node_t *> &out) { + const size_t N = std::distance(begin, end); + + size_t dim = ndim; + double r_best = N+1; + + // find the sparsest remaining dimension to partition by. + for (size_t i = 0; i < ndim; ++i) { + if (dim_mask & (1U << i)) continue; + aabb_extent extent(i); + double dmin, dmax, dsum; + + dmin = extent.min(*begin); + dmax = extent.max(*begin); + dsum = 0.0; + for (iter_t j = begin; j != end; ++j) { + dmin = std::min(dmin, extent.min(*j)); + dmax = std::max(dmax, extent.max(*j)); + dsum += extent.len(*j); + } + double r = dsum ? dsum / (dmax - dmin) : 0.0; + if (r_best > r) { + dim = i; + r_best = r; + } + } + + CARVE_ASSERT(dim < ndim); + + // dim = dim_num; + + const size_t P = (N + child_size - 1) / child_size; + const size_t n_parts = (size_t)std::ceil(std::pow((double)P, 1.0 / (ndim - dim_num))); + + std::sort(begin, end, aabb_cmp_mid(dim)); + + if (dim_num == ndim - 1 || n_parts == 1) { + for (size_t i = 0, s = 0, e = 0; i < P; ++i, s = e) { + e = N * (i+1) / P; + CARVE_ASSERT(e - s <= child_size); + out.push_back(new node_t(begin + s, begin + e)); + } + } else { + for (size_t i = 0, s = 0, e = 0; i < n_parts; ++i, s = e) { + e = N * (i+1) / n_parts; + makeNodes(begin + s, begin + e, dim_num + 1, dim_mask | (1U << dim), child_size, out); + } + } + } + + static node_t *construct_STR(std::vector<data_aabb_t> &data, size_t leaf_size, size_t internal_size) { + std::vector<node_t *> out; + makeNodes(data.begin(), data.end(), 0, 0, leaf_size, out); + + while (out.size() > 1) { + std::vector<node_t *> next; + makeNodes(out.begin(), out.end(), 0, 0, internal_size, next); + std::swap(out, next); + } + + CARVE_ASSERT(out.size() == 1); + return out[0]; + } + + template<typename iter_t> + static node_t *construct_STR(const iter_t &begin, + const iter_t &end, + size_t leaf_size, + size_t internal_size) { + std::vector<data_aabb_t> data; + data.reserve(std::distance(begin, end)); + for (iter_t i = begin; i != end; ++i) { + data.push_back(*i); + } + return construct_STR(data, leaf_size, internal_size); + } + + + template<typename iter_t> + static node_t *construct_STR(const iter_t &begin1, + const iter_t &end1, + const iter_t &begin2, + const iter_t &end2, + size_t leaf_size, + size_t internal_size) { + std::vector<data_aabb_t> data; + data.reserve(std::distance(begin1, end1) + std::distance(begin2, end2)); + for (iter_t i = begin1; i != end1; ++i) { + data.push_back(*i); + } + for (iter_t i = begin2; i != end2; ++i) { + data.push_back(*i); + } + return construct_STR(data, leaf_size, internal_size); + } + + + struct partition_info { + double score; + size_t partition_pos; + + partition_info() : score(std::numeric_limits<double>::max()), partition_pos(0) { + } + partition_info(double _score, size_t _partition_pos) : + score(_score), + partition_pos(_partition_pos) { + } + }; + + static partition_info findPartition(typename std::vector<data_aabb_t>::iterator base, + std::vector<size_t>::iterator begin, + std::vector<size_t>::iterator end, + size_t part_size) { + partition_info best(std::numeric_limits<double>::max(), 0); + const size_t N = std::distance(begin, end); + + std::vector<double> rhs_vol(N, 0.0); + + aabb_t rhs = base[begin[N-1]].aabb; + rhs_vol[N-1] = rhs.volume(); + for (size_t i = N - 1; i > 0; ) { + rhs.unionAABB(base[begin[--i]].aabb); + rhs_vol[i] = rhs.volume(); + } + + aabb_t lhs = base[begin[0]].aabb; + for (size_t i = 1; i < N; ++i) { + lhs.unionAABB(base[begin[i]].aabb); + if (i % part_size == 0 || (N - i) % part_size == 0) { + partition_info curr(lhs.volume() + rhs_vol[i], i); + if (best.score > curr.score) best = curr; + } + } + return best; + } + + static void partition(typename std::vector<data_aabb_t>::iterator base, + std::vector<size_t>::iterator begin, + std::vector<size_t>::iterator end, + size_t part_size, + std::vector<size_t> &part_num, + size_t &part_next) { + const size_t N = std::distance(begin, end); + + partition_info best; + partition_info curr; + size_t part_curr = part_num[*begin]; + + std::vector<size_t> tmp(begin, end); + + for (size_t dim = 0; dim < ndim; ++dim) { + std::sort(tmp.begin(), tmp.end(), make_index_sort(base, aabb_cmp_min(dim))); + curr = findPartition(base, tmp.begin(), tmp.end(), part_size); + if (best.score > curr.score) { + best = curr; + std::copy(tmp.begin(), tmp.end(), begin); + } + + std::sort(tmp.begin(), tmp.end(), make_index_sort(base, aabb_cmp_mid(dim))); + curr = findPartition(base, tmp.begin(), tmp.end(), part_size); + if (best.score > curr.score) { + best = curr; + std::copy(tmp.begin(), tmp.end(), begin); + } + + std::sort(tmp.begin(), tmp.end(), make_index_sort(base, aabb_cmp_max(dim))); + curr = findPartition(base, tmp.begin(), tmp.end(), part_size); + if (best.score > curr.score) { + best = curr; + std::copy(tmp.begin(), tmp.end(), begin); + } + } + + for (size_t j = 0; j < best.partition_pos; ++j) part_num[begin[j]] = part_curr; + for (size_t j = best.partition_pos; j < N; ++j) part_num[begin[j]] = part_next; + ++part_next; + + if (best.partition_pos > part_size) { + partition(base, begin, begin + best.partition_pos, part_size, part_num, part_next); + } + if (N - best.partition_pos > part_size) { + partition(base, begin + best.partition_pos, end, part_size, part_num, part_next); + } + } + + static size_t makePartitions(typename std::vector<data_aabb_t>::iterator begin, + typename std::vector<data_aabb_t>::iterator end, + size_t part_size, + std::vector<size_t> &part_num) { + const size_t N = std::distance(begin, end); + std::vector<size_t> idx; + idx.reserve(N); + for (size_t i = 0; i < N; ++i) { idx.push_back(i); } + size_t part_next = 1; + + partition(begin, idx.begin(), idx.end(), part_size, part_num, part_next); + return part_next; + } + + static node_t *construct_TGS(typename std::vector<data_aabb_t>::iterator begin, + typename std::vector<data_aabb_t>::iterator end, + size_t leaf_size, + size_t internal_size) { + size_t N = std::distance(begin, end); + + if (N <= leaf_size) { + return new node_t(begin, end); + } else { + size_t P = (N + internal_size - 1) / internal_size; + std::vector<size_t> part_num(N, 0); + P = makePartitions(begin, end, P, part_num); + + size_t S = 0, E = 0; + std::vector<node_t *> children; + for (size_t i = 0; i < P; ++i) { + size_t j = S, k = N; + while (true) { + while (true) { + if (j == k) goto done; + else if (part_num[j] == i) ++j; + else break; + } + --k; + while (true) { + if (j == k) goto done; + else if (part_num[k] != i) --k; + else break; + } + std::swap(*(begin+j), *(begin+k)); + std::swap(part_num[j], part_num[k]); + ++j; + } + done: + E = j; + children.push_back(construct_TGS(begin + S, begin + E, leaf_size, internal_size)); + S = E; + } + return new node_t(children.begin(), children.end()); + } + } + + template<typename iter_t> + static node_t *construct_TGS(const iter_t &begin, + const iter_t &end, + size_t leaf_size, + size_t internal_size) { + std::vector<data_aabb_t> data; + data.reserve(std::distance(begin, end)); + for (iter_t i = begin; i != end; ++i) { + data.push_back(*i); + } + return construct_TGS(data.begin(), data.end(), leaf_size, internal_size); + } + + template<typename iter_t> + static node_t *construct_TGS(const iter_t &begin1, + const iter_t &end1, + const iter_t &begin2, + const iter_t &end2, + size_t leaf_size, + size_t internal_size) { + std::vector<data_aabb_t> data; + data.reserve(std::distance(begin1, end1) + std::distance(begin2, end2)); + for (iter_t i = begin1; i != end1; ++i) { + data.push_back(*i); + } + for (iter_t i = begin2; i != end2; ++i) { + data.push_back(*i); + } + return construct_TGS(data.begin(), data.end(), leaf_size, internal_size); + } + }; + + } +} diff --git a/extern/carve/include/carve/spacetree.hpp b/extern/carve/include/carve/spacetree.hpp new file mode 100644 index 00000000000..f2ea2bb83a7 --- /dev/null +++ b/extern/carve/include/carve/spacetree.hpp @@ -0,0 +1,264 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/geom.hpp> +#include <carve/aabb.hpp> +#include <carve/vertex_decl.hpp> +#include <carve/edge_decl.hpp> +#include <carve/face_decl.hpp> + +namespace carve { + + namespace space { + + static inline bool intersection_test(const carve::geom::aabb<3> &aabb, const carve::poly::Face<3> *face) { + if (face->nVertices() == 3) { + return aabb.intersects(carve::geom::tri<3>(face->vertex(0)->v, face->vertex(1)->v, face->vertex(2)->v)); + } else { + // partial, conservative SAT. + return aabb.intersects(face->aabb) && aabb.intersects(face->plane_eqn); + } + } + + static inline bool intersection_test(const carve::geom::aabb<3> &aabb, const carve::poly::Edge<3> *edge) { + return aabb.intersectsLineSegment(edge->v1->v, edge->v2->v); + } + + static inline bool intersection_test(const carve::geom::aabb<3> &aabb, const carve::poly::Vertex<3> *vertex) { + return aabb.intersects(vertex->v); + } + + + + struct nodedata_FaceEdge { + std::vector<const carve::poly::Face<3> *> faces; + std::vector<const carve::poly::Edge<3> *> edges; + + void add(const carve::poly::Face<3> *face) { + faces.push_back(face); + } + + void add(const carve::poly::Edge<3> *edge) { + edges.push_back(edge); + } + + template<typename iter_t> + void _fetch(iter_t &iter, const carve::poly::Edge<3> *) { + std::copy(edges.begin(), edges.end(), iter); + } + + template<typename iter_t> + void _fetch(iter_t &iter, const carve::poly::Face<3> *) { + std::copy(faces.begin(), faces.end(), iter); + } + + template<typename node_t> + void propagate(node_t *node) { + } + + template<typename iter_t> + void fetch(iter_t &iter) { + return _fetch(iter, std::iterator_traits<iter_t>::value_type); + } + }; + + + + const static double SLACK_FACTOR = 1.0009765625; + const static unsigned MAX_SPLIT_DEPTH = 32; + + + + template<unsigned n_dim, typename nodedata_t> + class SpatialSubdivTree { + + typedef carve::geom::aabb<n_dim> aabb_t; + typedef carve::geom::vector<n_dim> vector_t; + + public: + + class Node { + enum { + n_children = 1 << n_dim + }; + + public: + Node *parent; + Node *children; + + vector_t min; + vector_t max; + + aabb_t aabb; + + nodedata_t data; + + private: + Node(const Node &node); // undefined. + Node &operator=(const Node &node); // undefined. + + Node() { + } + + inline aabb_t makeAABB() const { + vector_t centre = 0.5 * (min + max); + vector_t size = SLACK_FACTOR * 0.5 * (max - min); + return aabb_t(centre, size); + } + + void setup(Node *_parent, const vector_t &_min, const vector_t &_max) { + parent = _parent; + min = _min; + max = _max; + aabb = makeAABB(); + } + + void alloc_children() { + vector_t mid = 0.5 * (min + max); + children = new Node[n_children]; + for (size_t i = 0; i < (n_children); ++i) { + vector_t new_min, new_max; + for (size_t c = 0; c < n_dim; ++c) { + if (i & (1 << c)) { + new_min.v[c] = min.v[c]; + new_max.v[c] = mid.v[c]; + } else { + new_min.v[c] = mid.v[c]; + new_max.v[c] = max.v[c]; + } + } + children[i].setup(this, new_min, new_max); + } + } + + void dealloc_children() { + delete [] children; + } + + public: + + inline bool isLeaf() const { return children == NULL; } + + Node(Node *_parent, const vector_t &_min, const vector_t &_max) : parent(_parent), children(NULL), min(_min), max(_max) { + aabb = makeAABB(); + } + + ~Node() { + dealloc_children(); + } + + bool split() { + if (isLeaf()) { + alloc_children(); + data.propagate(this); + } + return isLeaf(); + } + + template<typename obj_t> + void insert(const obj_t &object) { + if (!isLeaf()) { + for (size_t i = 0; i < n_children; ++i) { + if (intersection_test(children[i].aabb, object)) { + children[i].insert(object); + } + } + } else { + data.add(object); + } + } + + template<typename obj_t> + void insertVector(typename std::vector<obj_t>::iterator beg, typename std::vector<obj_t>::iterator end) { + if (isLeaf()) { + while (beg != end) { + data.add(*beg); + } + } else { + for (size_t i = 0; i < n_children; ++i) { + typename std::vector<obj_t>::iterator mid = std::partition(beg, end, std::bind1st(intersection_test, children[i].aabb)); + children[i].insertVector(beg, mid); + } + } + } + + template<typename iter_t> + void insertMany(iter_t begin, iter_t end) { + if (isLeaf()) { + } + } + + template<typename obj_t, typename iter_t, typename filter_t> + void findObjectsNear(const obj_t &object, iter_t &output, filter_t filter) { + if (!isLeaf()) { + for (size_t i = 0; i < n_children; ++i) { + if (intersection_test(children[i].aabb, object)) { + children[i].findObjectsNear(object, output, filter); + } + } + return; + } + data.fetch(output); + } + + // bool hasGeometry(); + + // template <class T> + // void putInside(const T &input, Node *child, T &output); + + }; + + + + Node *root; + + SpatialSubdivTree(const vector_t &_min, const vector_t &_max) : root(new Node(NULL, _min, _max)) { + } + + ~SpatialSubdivTree() { + delete root; + } + + struct no_filter { + template<typename obj_t> + bool operator()(const obj_t &obj) const { + return true; + } + }; + + struct tag_filter { + template<typename obj_t> + bool operator()(const obj_t &obj) const { + return obj.tag_once(); + } + }; + + // in order to be used as an input, aabb_t::intersect(const obj_t &) must exist. + template<typename obj_t, typename iter_t, typename filter_t> + void findObjectsNear(const obj_t &object, iter_t output, filter_t filter) { + if (!intersection_test(root->aabb, object)) return; + root->findObjectsNear(root, object, output, filter); + } + + }; + + } +} diff --git a/extern/carve/include/carve/tag.hpp b/extern/carve/include/carve/tag.hpp new file mode 100644 index 00000000000..57f9ba21460 --- /dev/null +++ b/extern/carve/include/carve/tag.hpp @@ -0,0 +1,44 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +namespace carve { + + class tagable { + private: + static int s_count; + + protected: + mutable int __tag; + + public: + tagable(const tagable &) : __tag(s_count - 1) { } + tagable &operator=(const tagable &) { return *this; } + + tagable() : __tag(s_count - 1) { } + + void tag() const { __tag = s_count; } + void untag() const { __tag = s_count - 1; } + bool is_tagged() const { return __tag == s_count; } + bool tag_once() const { if (__tag == s_count) return false; __tag = s_count; return true; } + + static void tag_begin() { s_count++; } + }; +} diff --git a/extern/carve/include/carve/timing.hpp b/extern/carve/include/carve/timing.hpp new file mode 100644 index 00000000000..f5051f72d63 --- /dev/null +++ b/extern/carve/include/carve/timing.hpp @@ -0,0 +1,96 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#ifndef CARVE_USE_TIMINGS +#define CARVE_USE_TIMINGS 0 +#endif + +namespace carve { + +#if CARVE_USE_TIMINGS + + class TimingName { + public: + TimingName(const char *name); + int id; + }; + + class TimingBlock { + public: + /** + * Starts timing at the end of this constructor, using the given ID. To + * associate an ID with a textual name, use Timing::registerID. + */ + TimingBlock(int id); + TimingBlock(const TimingName &name); + ~TimingBlock(); + }; + + class Timing { + public: + + /** + * Starts timing against a particular ID. + */ + static void start(int id); + + static void start(const TimingName &id) { + start(id.id); + } + + /** + * Stops the most recent timing block. + */ + static double stop(); + + /** + * This will print out the current state of recorded time blocks. It will + * display the tree of timings, as well as the summaries down the bottom. + */ + static void printTimings(); + + /** + * Associates a particular ID with a text string. This is used when + * printing out the timings. + */ + static void registerID(int id, const char *name); + + }; + +#else + + struct TimingName { + TimingName(const char *) {} + }; + struct TimingBlock { + TimingBlock(int /* id */) {} + TimingBlock(const TimingName & /* name */) {} + }; + struct Timing { + static void start(int /* id */) {} + static void start(const TimingName & /* id */) {} + static double stop() { return 0; } + static void printTimings() {} + static void registerID(int /* id */, const char * /* name */) {} + }; + +#endif +} diff --git a/extern/carve/include/carve/tree.hpp b/extern/carve/include/carve/tree.hpp new file mode 100644 index 00000000000..ba1bcc6e3c1 --- /dev/null +++ b/extern/carve/include/carve/tree.hpp @@ -0,0 +1,324 @@ + +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/matrix.hpp> +#include <carve/timing.hpp> +#include <carve/rescale.hpp> + +namespace carve { + namespace csg { + + class CSG_TreeNode { + CSG_TreeNode(const CSG_TreeNode &); + CSG_TreeNode &operator=(const CSG_TreeNode &); + + protected: + + public: + CSG_TreeNode() { + } + + virtual ~CSG_TreeNode() { + } + + virtual carve::mesh::MeshSet<3> *eval(bool &is_temp, CSG &csg) =0; + + virtual carve::mesh::MeshSet<3> *eval(CSG &csg) { + bool temp; + carve::mesh::MeshSet<3> *r = eval(temp, csg); + if (!temp) r = r->clone(); + return r; + } + }; + + + + class CSG_TransformNode : public CSG_TreeNode { + carve::math::Matrix transform; + CSG_TreeNode *child; + + public: + CSG_TransformNode(const carve::math::Matrix &_transform, CSG_TreeNode *_child) : transform(_transform), child(_child) { + } + virtual ~CSG_TransformNode() { + delete child; + } + + virtual carve::mesh::MeshSet<3> *eval(bool &is_temp, CSG &csg) { + carve::mesh::MeshSet<3> *result = child->eval(is_temp, csg); + if (!is_temp) { + result = result->clone(); + is_temp = true; + } + result->transform(carve::math::matrix_transformation(transform)); + return result; + } + }; + + + + + class CSG_InvertNode : public CSG_TreeNode { + std::vector<bool> selected_meshes; + CSG_TreeNode *child; + + public: + CSG_InvertNode(CSG_TreeNode *_child) : selected_meshes(), child(_child) { + } + CSG_InvertNode(int g_id, CSG_TreeNode *_child) : selected_meshes(), child(_child) { + selected_meshes.resize(g_id + 1, false); + selected_meshes[g_id] = true; + } + virtual ~CSG_InvertNode() { + delete child; + } + + template<typename T> + CSG_InvertNode(T start, T end, CSG_TreeNode *_child) : selected_meshes(), child(_child) { + while (start != end) { + int g_id = (int)(*start); + if (selected_meshes.size() < g_id + 1) selected_meshes.resize(g_id + 1, false); + selected_meshes[g_id] = true; + ++start; + } + } + + virtual carve::mesh::MeshSet<3> *eval(bool &is_temp, CSG &csg) { + bool c_temp; + carve::mesh::MeshSet<3> *c = child->eval(c_temp, csg); + if (!c_temp) c = c->clone(); + if (!selected_meshes.size()) { + c->invert(); + } else { + for (size_t i = 0; i < c->meshes.size() && i < selected_meshes.size(); ++i) { + if (selected_meshes[i]) { + c->meshes[i]->invert(); + } + } + } + is_temp = true; + return c; + } + }; + + + + + class CSG_SelectNode : public CSG_TreeNode { + std::vector<bool> selected_meshes; + CSG_TreeNode *child; + + public: + CSG_SelectNode(int m_id, CSG_TreeNode *_child) : selected_meshes(), child(_child) { + selected_meshes.resize(m_id + 1, false); + selected_meshes[m_id] = true; + } + + template<typename T> + CSG_SelectNode(T start, T end, CSG_TreeNode *_child) : selected_meshes(), child(_child) { + while (start != end) { + int m_id = (int)(*start); + if ((int)selected_meshes.size() < m_id + 1) selected_meshes.resize(m_id + 1, false); + selected_meshes[m_id] = true; + ++start; + } + } + + virtual ~CSG_SelectNode() { + delete child; + } + + virtual carve::mesh::MeshSet<3> *eval(bool &is_temp, CSG &csg) { + bool c_temp; + carve::mesh::MeshSet<3> *c = child->eval(c_temp, csg); + if (!c_temp) c = c->clone(); + size_t i = 0; + size_t j = 0; + for (size_t i = 0; i < c->meshes.size(); ++i) { + if (i >= selected_meshes.size() || !selected_meshes[i]) { + delete c->meshes[i]; + c->meshes[i] = NULL; + } else { + c->meshes[j++] = c->meshes[i]; + } + } + c->meshes.erase(c->meshes.begin() + j, c->meshes.end()); + c->collectVertices(); + is_temp = true; + return c; + } + }; + + + + + class CSG_PolyNode : public CSG_TreeNode { + carve::mesh::MeshSet<3> *poly; + bool del; + + public: + CSG_PolyNode(carve::mesh::MeshSet<3> *_poly, bool _del) : poly(_poly), del(_del) { + } + virtual ~CSG_PolyNode() { + static carve::TimingName FUNC_NAME("delete polyhedron"); + carve::TimingBlock block(FUNC_NAME); + + if (del) { + delete poly; + } + } + + virtual carve::mesh::MeshSet<3> *eval(bool &is_temp, CSG &csg) { + is_temp = false; + return poly; + } + }; + + + + class CSG_OPNode : public CSG_TreeNode { + CSG_TreeNode *left, *right; + CSG::OP op; + bool rescale; + CSG::CLASSIFY_TYPE classify_type; + + public: + CSG_OPNode(CSG_TreeNode *_left, + CSG_TreeNode *_right, + CSG::OP _op, + bool _rescale, + CSG::CLASSIFY_TYPE _classify_type = CSG::CLASSIFY_NORMAL) : left(_left), right(_right), op(_op), rescale(_rescale), classify_type(_classify_type) { + } + + virtual ~CSG_OPNode() { + delete left; + delete right; + } + + void minmax(double &min_x, double &min_y, double &min_z, + double &max_x, double &max_y, double &max_z, + const std::vector<carve::geom3d::Vector> &points) { + for (unsigned i = 1; i < points.size(); ++i) { + min_x = std::min(min_x, points[i].x); + max_x = std::max(max_x, points[i].x); + min_y = std::min(min_y, points[i].y); + max_y = std::max(max_y, points[i].y); + min_z = std::min(min_z, points[i].z); + max_z = std::max(max_z, points[i].z); + } + } + + virtual carve::mesh::MeshSet<3> *evalScaled(bool &is_temp, CSG &csg) { + carve::mesh::MeshSet<3> *l, *r; + bool l_temp, r_temp; + + l = left->eval(l_temp, csg); + r = right->eval(r_temp, csg); + + if (!l_temp) { l = l->clone(); } + if (!r_temp) { r = r->clone(); } + + carve::geom3d::Vector min, max; + carve::geom3d::Vector min_l, max_l; + carve::geom3d::Vector min_r, max_r; + + carve::geom::bounds<3>(l->vertex_storage.begin(), + l->vertex_storage.end(), + carve::mesh::Face<3>::vector_mapping(), + min_l, + max_l); + carve::geom::bounds<3>(r->vertex_storage.begin(), + r->vertex_storage.end(), + carve::mesh::Face<3>::vector_mapping(), + min_r, + max_r); + + carve::geom::assign_op(min, min_l, min_r, carve::util::min_functor()); + carve::geom::assign_op(max, max_l, max_r, carve::util::max_functor()); + + carve::rescale::rescale scaler(min.x, min.y, min.z, max.x, max.y, max.z); + + carve::rescale::fwd fwd_r(scaler); + carve::rescale::rev rev_r(scaler); + + l->transform(fwd_r); + r->transform(fwd_r); + + carve::mesh::MeshSet<3> *result = NULL; + { + static carve::TimingName FUNC_NAME("csg.compute()"); + carve::TimingBlock block(FUNC_NAME); + result = csg.compute(l, r, op, NULL, classify_type); + } + + { + static carve::TimingName FUNC_NAME("delete polyhedron"); + carve::TimingBlock block(FUNC_NAME); + + delete l; + delete r; + } + + result->transform(rev_r); + + is_temp = true; + return result; + } + + virtual carve::mesh::MeshSet<3> *evalUnscaled(bool &is_temp, CSG &csg) { + carve::mesh::MeshSet<3> *l, *r; + bool l_temp, r_temp; + + l = left->eval(l_temp, csg); + r = right->eval(r_temp, csg); + + carve::mesh::MeshSet<3> *result = NULL; + { + static carve::TimingName FUNC_NAME("csg.compute()"); + carve::TimingBlock block(FUNC_NAME); + result = csg.compute(l, r, op, NULL, classify_type); + } + + { + static carve::TimingName FUNC_NAME("delete polyhedron"); + carve::TimingBlock block(FUNC_NAME); + + if (l_temp) delete l; + if (r_temp) delete r; + } + + is_temp = true; + return result; + } + + + virtual carve::mesh::MeshSet<3> *eval(bool &is_temp, CSG &csg) { + if (rescale) { + return evalScaled(is_temp, csg); + } else { + return evalUnscaled(is_temp, csg); + } + } + }; + + } +} diff --git a/extern/carve/include/carve/triangulator.hpp b/extern/carve/include/carve/triangulator.hpp new file mode 100644 index 00000000000..aa007f98077 --- /dev/null +++ b/extern/carve/include/carve/triangulator.hpp @@ -0,0 +1,175 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <list> +#include <vector> +#include <algorithm> + +#include <carve/carve.hpp> + +#include <carve/geom2d.hpp> + +namespace carve { + namespace triangulate { + + /** + * \brief Merge a set of holes into a polygon. (templated) + * + * Take a polygon loop and a collection of hole loops, and patch + * the hole loops into the polygon loop, returning a vector of + * vertices from the polygon and holes, which describes a new + * polygon boundary with no holes. The new polygon boundary is + * constructed via the addition of edges * joining the polygon + * loop to the holes. + * + * This may be applied to arbitrary vertex data (generally + * carve::geom3d::Vertex pointers), but a projection function must + * be supplied to convert vertices to coordinates in 2-space, in + * which the work is performed. + * + * @tparam project_t A functor which converts vertices to a 2d + * projection. + * @tparam vert_t The vertex type. + * @param project The projection functor. + * @param f_loop The polygon loop into which holes are to be + * incorporated. + * @param h_loops The set of hole loops to be incorporated. + * + * @return A vector of vertex pointers. + */ + template<typename project_t, typename vert_t> + static std::vector<vert_t> + incorporateHolesIntoPolygon(const project_t &project, + const std::vector<vert_t> &f_loop, + const std::vector<std::vector<vert_t> > &h_loops); + + void + incorporateHolesIntoPolygon(const std::vector<std::vector<carve::geom2d::P2> > &poly, + std::vector<std::pair<size_t, size_t> > &result, + size_t poly_loop, + const std::vector<size_t> &hole_loops); + + /** + * \brief Merge a set of holes into a polygon. (2d) + * + * Take a polygon loop and a collection of hole loops, and patch + * the hole loops into the polygon loop, returning a vector of + * containing the vertices from the polygon and holes which + * describes a new polygon boundary with no holes, through the + * addition of edges joining the polygon loop to the holes. + * + * @param poly A vector containing the face loop (the first + * element of poly) and the hole loops (second and + * subsequent elements of poly). + * + * @return A vector of pairs of <loop_number, index> that + * reference poly and define the result polygon loop. + */ + std::vector<std::pair<size_t, size_t> > incorporateHolesIntoPolygon(const std::vector<std::vector<carve::geom2d::P2> > &poly); + + std::vector<std::vector<std::pair<size_t, size_t> > > mergePolygonsAndHoles(const std::vector<std::vector<carve::geom2d::P2> > &poly); + + + struct tri_idx { + union { + unsigned v[3]; + struct { unsigned a, b, c; }; + }; + + tri_idx() : a(0), b(0), c(0) { + } + tri_idx(unsigned _a, unsigned _b, unsigned _c) : a(_a), b(_b), c(_c) { + } + }; + + /** + * \brief Triangulate a 2-dimensional polygon. + * + * Given a 2-dimensional polygon described as a vector of 2-d + * points, with no holes and no self-crossings, produce a + * triangulation using an ear-clipping algorithm. + * + * @param [in] poly A vector containing the input polygon. + * @param [out] result A vector of triangles, represented as + * indicies into poly. + */ + + + void triangulate(const std::vector<carve::geom2d::P2> &poly, std::vector<tri_idx> &result); + + /** + * \brief Triangulate a polygon (templated). + * + * @tparam project_t A functor which converts vertices to a 2d + * projection. + * @tparam vert_t The vertex type. + * @param [in] project The projection functor. + * @param [in] poly A vector containing the input polygon, + * represented as vert_t pointers. + * @param [out] result A vector of triangles, represented as + * indicies into poly. + */ + template<typename project_t, typename vert_t> + void triangulate(const project_t &project, + const std::vector<vert_t> &poly, + std::vector<tri_idx> &result); + + /** + * \brief Improve a candidate triangulation of poly by minimising + * the length of internal edges. (templated) + * + * @tparam project_t A functor which converts vertices to a 2d + * projection. + * @tparam vert_t The vertex type. + * @param [in] project The projection functor. + * @param [in] poly A vector containing the input polygon, + * represented as vert_t pointers. + * @param [inout] result A vector of triangles, represented as + * indicies into poly. On input, this vector + * must contain a candidate triangulation of + * poly. Calling improve() modifies the + * contents of the vector, returning an + * improved triangulation. + */ + template<typename project_t, typename vert_t> + void improve(const project_t &project, + const std::vector<vert_t> &poly, + std::vector<tri_idx> &result); + + /** + * \brief Improve a candidate triangulation of poly by minimising + * the length of internal edges. + * + * @param [in] poly A vector containing the input polygon. + + * @param [inout] result A vector of triangles, represented as + * indicies into poly. On input, this vector + * must contain a candidate triangulation of + * poly. Calling improve() modifies the + * contents of the vector, returning an + * improved triangulation. + */ + static inline void improve(const std::vector<carve::geom2d::P2> &poly, std::vector<tri_idx> &result) { + improve(carve::geom2d::p2_adapt_ident(), poly, result); + } + + } +} + +#include <carve/triangulator_impl.hpp> diff --git a/extern/carve/include/carve/triangulator_impl.hpp b/extern/carve/include/carve/triangulator_impl.hpp new file mode 100644 index 00000000000..476438fd248 --- /dev/null +++ b/extern/carve/include/carve/triangulator_impl.hpp @@ -0,0 +1,851 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/geom2d.hpp> + +#if defined(CARVE_DEBUG) +# include <iostream> +#endif + +namespace carve { + namespace triangulate { + namespace detail { + + + + static inline bool axisOrdering(const carve::geom2d::P2 &a, + const carve::geom2d::P2 &b, + int axis) { + return a.v[axis] < b.v[axis] || (a.v[axis] == b.v[axis] && a.v[1-axis] < b.v[1-axis]); + } + + + + /** + * \class order_h_loops + * \brief Provides an ordering of hole loops based upon a single + * projected axis. + * + * @tparam project_t A functor which converts vertices to a 2d + * projection. + * @tparam hole_t A collection of vertices. + */ + template<typename project_t, typename vert_t> + class order_h_loops { + const project_t &project; + int axis; + public: + + /** + * + * @param _project The projection functor. + * @param _axis The axis of the 2d projection upon which hole + * loops are ordered. + */ + order_h_loops(const project_t &_project, int _axis) : project(_project), axis(_axis) { } + + bool operator()(const vert_t &a, + const vert_t &b) const { + return axisOrdering(project(a), project(b), axis); + } + + bool operator()( + const std::pair<const typename std::vector<vert_t> *, typename std::vector<vert_t>::const_iterator> &a, + const std::pair<const typename std::vector<vert_t> *, typename std::vector<vert_t>::const_iterator> &b) { + return axisOrdering(project(*(a.second)), project(*(b.second)), axis); + } + }; + + + + /** + * \class heap_ordering + * \brief Provides an ordering of vertex indicies in a polygon + * loop according to proximity to a vertex. + * + * @tparam project_t A functor which converts vertices to a 2d + * projection. + * @tparam vert_t A vertex type. + */ + template<typename project_t, typename vert_t> + class heap_ordering { + const project_t &project; + const std::vector<vert_t> &loop; + const carve::geom2d::P2 p; + int axis; + + public: + /** + * + * @param _project A functor which converts vertices to a 2d + * projection. + * @param _loop The polygon loop which indices address. + * @param _vert The vertex from which distance is measured. + * + */ + heap_ordering(const project_t &_project, + const std::vector<vert_t> &_loop, + vert_t _vert, + int _axis) : + project(_project), + loop(_loop), + p(_project(_vert)), + axis(_axis) { + } + + bool operator()(size_t a, size_t b) const { + carve::geom2d::P2 pa = project(loop[a]); + carve::geom2d::P2 pb = project(loop[b]); + double da = carve::geom::distance2(p, pa); + double db = carve::geom::distance2(p, pb); + if (da > db) return true; + if (da < db) return false; + return axisOrdering(pa, pb, axis); + } + }; + + + + /** + * \brief Given a polygon loop and a hole loop, and attachment + * points, insert the hole loop vertices into the polygon loop. + * + * @param[in,out] f_loop The polygon loop to incorporate the + * hole into. + * @param f_loop_attach[in] The index of the vertex of the + * polygon loop that the hole is to be + * attached to. + * @param hole_attach[in] A pair consisting of a pointer to a + * hole container and an iterator into + * that container reflecting the point of + * attachment of the hole. + */ + template<typename vert_t> + void patchHoleIntoPolygon(std::vector<vert_t> &f_loop, + unsigned f_loop_attach, + const std::pair<const std::vector<vert_t> *, + typename std::vector<vert_t>::const_iterator> &hole_attach) { + // join the vertex curr of the polygon loop to the hole at + // h_loop_connect + f_loop.insert(f_loop.begin() + f_loop_attach + 1, hole_attach.first->size() + 2, NULL); + typename std::vector<vert_t>::iterator f = f_loop.begin() + f_loop_attach; + + typename std::vector<vert_t>::const_iterator h = hole_attach.second; + + while (h != hole_attach.first->end()) { + *++f = *h++; + } + + h = hole_attach.first->begin(); + typename std::vector<vert_t>::const_iterator he = hole_attach.second; ++he; + while (h != he) { + *++f = *h++; + } + + *++f = f_loop[f_loop_attach]; + } + + + + struct vertex_info; + + + + /** + * \brief Determine whether c is to the left of a->b. + */ + static inline bool isLeft(const vertex_info *a, + const vertex_info *b, + const vertex_info *c); + + + + /** + * \brief Determine whether d is contained in the triangle abc. + */ + static inline bool pointInTriangle(const vertex_info *a, + const vertex_info *b, + const vertex_info *c, + const vertex_info *d); + + + + /** + * \class vertex_info + * \brief Maintains a linked list of untriangulated vertices + * during a triangulation operation. + */ + + struct vertex_info { + vertex_info *prev; + vertex_info *next; + carve::geom2d::P2 p; + size_t idx; + double score; + bool convex; + bool failed; + + vertex_info(const carve::geom2d::P2 &_p, size_t _idx) : + prev(NULL), next(NULL), + p(_p), idx(_idx), + score(0.0), convex(false) { + } + + static double triScore(const vertex_info *p, const vertex_info *v, const vertex_info *n); + + double calcScore() const; + + void recompute() { + score = calcScore(); + convex = isLeft(prev, this, next); + failed = false; + } + + bool isCandidate() const { + return convex && !failed; + } + + void remove() { + next->prev = prev; + prev->next = next; + } + + bool isClipable() const; + }; + + + + static inline bool isLeft(const vertex_info *a, + const vertex_info *b, + const vertex_info *c) { + if (a->idx < b->idx && b->idx < c->idx) { + return carve::geom2d::orient2d(a->p, b->p, c->p) > 0.0; + } else if (a->idx < c->idx && c->idx < b->idx) { + return carve::geom2d::orient2d(a->p, c->p, b->p) < 0.0; + } else if (b->idx < a->idx && a->idx < c->idx) { + return carve::geom2d::orient2d(b->p, a->p, c->p) < 0.0; + } else if (b->idx < c->idx && c->idx < a->idx) { + return carve::geom2d::orient2d(b->p, c->p, a->p) > 0.0; + } else if (c->idx < a->idx && a->idx < b->idx) { + return carve::geom2d::orient2d(c->p, a->p, b->p) > 0.0; + } else { + return carve::geom2d::orient2d(c->p, b->p, a->p) < 0.0; + } + } + + + + static inline bool pointInTriangle(const vertex_info *a, + const vertex_info *b, + const vertex_info *c, + const vertex_info *d) { + return !isLeft(a, c, d) && !isLeft(b, a, d) && !isLeft(c, b, d); + } + + + + size_t removeDegeneracies(vertex_info *&begin, std::vector<carve::triangulate::tri_idx> &result); + + bool splitAndResume(vertex_info *begin, std::vector<carve::triangulate::tri_idx> &result); + + bool doTriangulate(vertex_info *begin, std::vector<carve::triangulate::tri_idx> &result); + + + + typedef std::pair<unsigned, unsigned> vert_edge_t; + + + + struct hash_vert_edge_t { + size_t operator()(const vert_edge_t &e) const { + size_t r = (size_t)e.first; + size_t s = (size_t)e.second; + return r ^ ((s >> 16) | (s << 16)); + } + }; + + + + static inline vert_edge_t ordered_vert_edge_t(unsigned a, unsigned b) { + return (a < b) ? vert_edge_t(a, b) : vert_edge_t(b, a); + } + + + + struct tri_pair_t { + carve::triangulate::tri_idx *a, *b; + double score; + size_t idx; + + tri_pair_t() : a(NULL), b(NULL), score(0.0) { + } + + static inline unsigned N(unsigned i) { return (i+1)%3; } + static inline unsigned P(unsigned i) { return (i+2)%3; } + + void findSharedEdge(unsigned &ai, unsigned &bi) const { + if (a->v[1] == b->v[0]) { if (a->v[0] == b->v[1]) { ai = 0; bi = 0; } else { ai = 1; bi = 2; } return; } + if (a->v[1] == b->v[1]) { if (a->v[0] == b->v[2]) { ai = 0; bi = 1; } else { ai = 1; bi = 0; } return; } + if (a->v[1] == b->v[2]) { if (a->v[0] == b->v[0]) { ai = 0; bi = 2; } else { ai = 1; bi = 1; } return; } + if (a->v[2] == b->v[0]) { ai = 2; bi = 2; return; } + if (a->v[2] == b->v[1]) { ai = 2; bi = 0; return; } + if (a->v[2] == b->v[2]) { ai = 2; bi = 1; return; } + CARVE_FAIL("should not be reached"); + } + + void flip(vert_edge_t &old_edge, + vert_edge_t &new_edge, + vert_edge_t perim[4]); + + template<typename project_t, typename vert_t, typename distance_calc_t> + double calc(const project_t &project, + const std::vector<vert_t> &poly, + distance_calc_t dist) { + unsigned ai, bi; + unsigned cross_ai, cross_bi; + unsigned ea, eb; + + findSharedEdge(ai, bi); + +#if defined(CARVE_DEBUG) + if (carve::geom2d::signedArea(project(poly[a->v[0]]), project(poly[a->v[1]]), project(poly[a->v[2]])) > 0.0 || + carve::geom2d::signedArea(project(poly[b->v[0]]), project(poly[b->v[1]]), project(poly[b->v[2]])) > 0.0) { + std::cerr << "warning: triangle pair " << this << " contains triangles with incorrect orientation" << std::endl; + } +#endif + + cross_ai = P(ai); + cross_bi = P(bi); + + ea = a->v[cross_ai]; + eb = b->v[cross_bi]; + + double side_1 = carve::geom2d::orient2d(project(poly[ea]), project(poly[eb]), project(poly[a->v[ai]])); + double side_2 = carve::geom2d::orient2d(project(poly[ea]), project(poly[eb]), project(poly[a->v[N(ai)]])); + + bool can_flip = (side_1 < 0.0 && side_2 > 0.0) || (side_1 > 0.0 && side_2 < 0.0); + + if (!can_flip) { + score = -1; + } else { + score = + dist(poly[a->v[ai]], poly[b->v[bi]]) - + dist(poly[a->v[cross_ai]], poly[b->v[cross_bi]]); + } + return score; + } + + template<typename project_t, typename vert_t, typename distance_calc_t> + double edgeLen(const project_t &project, + const std::vector<vert_t> &poly, + distance_calc_t dist) const { + unsigned ai, bi; + findSharedEdge(ai, bi); + return dist(poly[a->v[ai]], poly[b->v[bi]]); + } + }; + + + + struct max_score { + bool operator()(const tri_pair_t *a, const tri_pair_t *b) const { return a->score < b->score; } + }; + + + + struct tri_pairs_t { + typedef std::unordered_map<vert_edge_t, tri_pair_t *, hash_vert_edge_t> storage_t; + storage_t storage; + + tri_pairs_t() : storage() { + }; + + ~tri_pairs_t() { + for (storage_t::iterator i = storage.begin(); i != storage.end(); ++i) { + if ((*i).second) delete (*i).second; + } + } + + void insert(unsigned a, unsigned b, carve::triangulate::tri_idx *t); + + template<typename project_t, typename vert_t, typename distance_calc_t> + void updateEdge(tri_pair_t *tp, + const project_t &project, + const std::vector<vert_t> &poly, + distance_calc_t dist, + std::vector<tri_pair_t *> &edges, + size_t &n) { + double old_score = tp->score; + double new_score = tp->calc(project, poly, dist); +#if defined(CARVE_DEBUG) + std::cerr << "tp:" << tp << " old_score: " << old_score << " new_score: " << new_score << std::endl; +#endif + if (new_score > 0.0 && old_score <= 0.0) { + tp->idx = n; + edges[n++] = tp; + } else if (new_score <= 0.0 && old_score > 0.0) { + std::swap(edges[tp->idx], edges[--n]); + edges[tp->idx]->idx = tp->idx; + } + } + + tri_pair_t *get(vert_edge_t &e) { + storage_t::iterator i; + i = storage.find(e); + if (i == storage.end()) return NULL; + return (*i).second; + } + + template<typename project_t, typename vert_t, typename distance_calc_t> + void flip(const project_t &project, + const std::vector<vert_t> &poly, + distance_calc_t dist, + std::vector<tri_pair_t *> &edges, + size_t &n) { + vert_edge_t old_e, new_e; + vert_edge_t perim[4]; + +#if defined(CARVE_DEBUG) + std::cerr << "improvable edges: " << n << std::endl; +#endif + + tri_pair_t *tp = *std::max_element(edges.begin(), edges.begin() + n, max_score()); + +#if defined(CARVE_DEBUG) + std::cerr << "improving tri-pair: " << tp << " with score: " << tp->score << std::endl; +#endif + + tp->flip(old_e, new_e, perim); + +#if defined(CARVE_DEBUG) + std::cerr << "old_e: " << old_e.first << "," << old_e.second << " -> new_e: " << new_e.first << "," << new_e.second << std::endl; +#endif + + CARVE_ASSERT(storage.find(old_e) != storage.end()); + storage.erase(old_e); + storage[new_e] = tp; + + std::swap(edges[tp->idx], edges[--n]); + edges[tp->idx]->idx = tp->idx; + + tri_pair_t *tp2; + + tp2 = get(perim[0]); + if (tp2 != NULL) { + updateEdge(tp2, project, poly, dist, edges, n); + } + + tp2 = get(perim[1]); + if (tp2 != NULL) { + CARVE_ASSERT(tp2->a == tp->b || tp2->b == tp->b); + if (tp2->a == tp->b) { tp2->a = tp->a; } else { tp2->b = tp->a; } + updateEdge(tp2, project, poly, dist, edges, n); + } + + tp2 = get(perim[2]); + if (tp2 != NULL) { + updateEdge(tp2, project, poly, dist, edges, n); + } + + tp2 = get(perim[3]); + if (tp2 != NULL) { + CARVE_ASSERT(tp2->a == tp->a || tp2->b == tp->a); + if (tp2->a == tp->a) { tp2->a = tp->b; } else { tp2->b = tp->b; } + updateEdge(tp2, project, poly, dist, edges, n); + } + } + + template<typename project_t, typename vert_t, typename distance_calc_t> + size_t getInternalEdges(const project_t &project, + const std::vector<vert_t> &poly, + distance_calc_t dist, + std::vector<tri_pair_t *> &edges) { + size_t count = 0; + + for (storage_t::iterator i = storage.begin(); i != storage.end();) { + tri_pair_t *tp = (*i).second; + if (tp->a && tp->b) { + tp->calc(project, poly, dist); + count++; +#if defined(CARVE_DEBUG) + std::cerr << "internal edge: " << (*i).first.first << "," << (*i).first.second << " -> " << tp << " " << tp->score << std::endl; +#endif + ++i; + } else { + delete (*i).second; + storage.erase(i++); + } + } + + edges.resize(count); + + size_t fwd = 0; + size_t rev = count; + for (storage_t::iterator i = storage.begin(); i != storage.end(); ++i) { + tri_pair_t *tp = (*i).second; + if (tp && tp->a && tp->b) { + if (tp->score > 0.0) { + edges[fwd++] = tp; + } else { + edges[--rev] = tp; + } + } + } + + CARVE_ASSERT(fwd == rev); + + return fwd; + } + }; + + + + template<typename project_t, typename vert_t> + static bool + testCandidateAttachment(const project_t &project, + std::vector<vert_t> ¤t_f_loop, + size_t curr, + carve::geom2d::P2 hole_min) { + const size_t SZ = current_f_loop.size(); + + size_t prev, next; + + if (curr == 0) { + prev = SZ - 1; next = 1; + } else if (curr == SZ - 1) { + prev = curr - 1; next = 0; + } else { + prev = curr - 1; next = curr + 1; + } + + if (!carve::geom2d::internalToAngle(project(current_f_loop[next]), + project(current_f_loop[curr]), + project(current_f_loop[prev]), + hole_min)) { + return false; + } + + if (hole_min == project(current_f_loop[curr])) { + return true; + } + + carve::geom2d::LineSegment2 test(hole_min, project(current_f_loop[curr])); + + size_t v1 = current_f_loop.size() - 1; + size_t v2 = 0; + double v1_side = carve::geom2d::orient2d(test.v1, test.v2, project(current_f_loop[v1])); + double v2_side = 0; + + while (v2 != current_f_loop.size()) { + v2_side = carve::geom2d::orient2d(test.v1, test.v2, project(current_f_loop[v2])); + + if (v1_side != v2_side) { + // XXX: need to test vertices, not indices, because they may + // be duplicated. + if (project(current_f_loop[v1]) != project(current_f_loop[curr]) && + project(current_f_loop[v2]) != project(current_f_loop[curr])) { + carve::geom2d::LineSegment2 test2(project(current_f_loop[v1]), project(current_f_loop[v2])); + if (carve::geom2d::lineSegmentIntersection_simple(test, test2)) { + // intersection; failed. + return false; + } + } + } + + v1 = v2; + v1_side = v2_side; + ++v2; + } + return true; + } + + + + } + + + + template<typename project_t, typename vert_t> + static std::vector<vert_t> + incorporateHolesIntoPolygon(const project_t &project, + const std::vector<vert_t> &f_loop, + const std::vector<std::vector<vert_t> > &h_loops) { + typedef std::vector<vert_t> hole_t; + typedef typename std::vector<vert_t>::const_iterator vert_iter; + typedef typename std::vector<std::vector<vert_t> >::const_iterator hole_iter; + + size_t N = f_loop.size(); + + // work out how much space to reserve for the patched in holes. + for (hole_iter i = h_loops.begin(); i != h_loops.end(); ++i) { + N += 2 + (*i).size(); + } + + // this is the vector that we will build the result in. + std::vector<vert_t> current_f_loop; + current_f_loop.reserve(N); + + std::vector<size_t> f_loop_heap; + f_loop_heap.reserve(N); + + for (unsigned i = 0; i < f_loop.size(); ++i) { + current_f_loop.push_back(f_loop[i]); + } + + std::vector<std::pair<const std::vector<vert_t> *, vert_iter> > h_loop_min_vertex; + + h_loop_min_vertex.reserve(h_loops.size()); + + // find the major axis for the holes - this is the axis that we + // will sort on for finding vertices on the polygon to join + // holes up to. + // + // it might also be nice to also look for whether it is better + // to sort ascending or descending. + // + // another trick that could be used is to modify the projection + // by 90 degree rotations or flipping about an axis. just as + // long as we keep the carve::geom3d::Vector pointers for the + // real data in sync, everything should be ok. then we wouldn't + // need to accomodate axes or sort order in the main loop. + + // find the bounding box of all the holes. + bool first = true; + double min_x, min_y, max_x, max_y; + for (hole_iter i = h_loops.begin(); i != h_loops.end(); ++i) { + const hole_t &hole(*i); + for (vert_iter j = hole.begin(); j != hole.end(); ++j) { + carve::geom2d::P2 curr = project(*j); + if (first) { + min_x = max_x = curr.x; + min_y = max_y = curr.y; + first = false; + } else { + min_x = std::min(min_x, curr.x); + min_y = std::min(min_y, curr.y); + max_x = std::max(max_x, curr.x); + max_y = std::max(max_y, curr.y); + } + } + } + + // choose the axis for which the bbox is largest. + int axis = (max_x - min_x) > (max_y - min_y) ? 0 : 1; + + // for each hole, find the minimum vertex in the chosen axis. + for (hole_iter i = h_loops.begin(); i != h_loops.end(); ++i) { + const hole_t &hole = *i; + vert_iter best_i = std::min_element(hole.begin(), hole.end(), detail::order_h_loops<project_t, vert_t>(project, axis)); + h_loop_min_vertex.push_back(std::make_pair(&hole, best_i)); + } + + // sort the holes by the minimum vertex. + std::sort(h_loop_min_vertex.begin(), h_loop_min_vertex.end(), detail::order_h_loops<project_t, vert_t>(project, axis)); + + // now, for each hole, find a vertex in the current polygon loop that it can be joined to. + for (unsigned i = 0; i < h_loop_min_vertex.size(); ++i) { + const size_t N_f_loop = current_f_loop.size(); + + // the index of the vertex in the hole to connect. + vert_iter h_loop_connect = h_loop_min_vertex[i].second; + + carve::geom2d::P2 hole_min = project(*h_loop_connect); + + f_loop_heap.clear(); + // we order polygon loop vertices that may be able to be connected + // to the hole vertex by their distance to the hole vertex + detail::heap_ordering<project_t, vert_t> _heap_ordering(project, current_f_loop, *h_loop_connect, axis); + + for (size_t j = 0; j < N_f_loop; ++j) { + // it is guaranteed that there exists a polygon vertex with + // coord < the min hole coord chosen, which can be joined to + // the min hole coord without crossing the polygon + // boundary. also, because we merge holes in ascending + // order, it is also true that this join can never cross + // another hole (and that doesn't need to be tested for). + if (project(current_f_loop[j]).v[axis] <= hole_min.v[axis]) { + f_loop_heap.push_back(j); + std::push_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering); + } + } + + // we are going to test each potential (according to the + // previous test) polygon vertex as a candidate join. we order + // by closeness to the hole vertex, so that the join we make + // is as small as possible. to test, we need to check the + // joining line segment does not cross any other line segment + // in the current polygon loop (excluding those that have the + // vertex that we are attempting to join with as an endpoint). + size_t attachment_point = current_f_loop.size(); + + while (f_loop_heap.size()) { + std::pop_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering); + size_t curr = f_loop_heap.back(); + f_loop_heap.pop_back(); + // test the candidate join from current_f_loop[curr] to hole_min + + if (!detail::testCandidateAttachment(project, current_f_loop, curr, hole_min)) { + continue; + } + + attachment_point = curr; + break; + } + + if (attachment_point == current_f_loop.size()) { + CARVE_FAIL("didn't manage to link up hole!"); + } + + detail::patchHoleIntoPolygon(current_f_loop, attachment_point, h_loop_min_vertex[i]); + } + + return current_f_loop; + } + + + + template<typename project_t, typename vert_t> + void triangulate(const project_t &project, + const std::vector<vert_t> &poly, + std::vector<tri_idx> &result) { + std::vector<detail::vertex_info *> vinfo; + const size_t N = poly.size(); + + result.clear(); + if (N < 3) { + return; + } + + result.reserve(poly.size() - 2); + + if (N == 3) { + result.push_back(tri_idx(0, 1, 2)); + return; + } + + vinfo.resize(N); + + vinfo[0] = new detail::vertex_info(project(poly[0]), 0); + for (size_t i = 1; i < N-1; ++i) { + vinfo[i] = new detail::vertex_info(project(poly[i]), i); + vinfo[i]->prev = vinfo[i-1]; + vinfo[i-1]->next = vinfo[i]; + } + vinfo[N-1] = new detail::vertex_info(project(poly[N-1]), N-1); + vinfo[N-1]->prev = vinfo[N-2]; + vinfo[N-1]->next = vinfo[0]; + vinfo[0]->prev = vinfo[N-1]; + vinfo[N-2]->next = vinfo[N-1]; + + for (size_t i = 0; i < N; ++i) { + vinfo[i]->recompute(); + } + + detail::vertex_info *begin = vinfo[0]; + + removeDegeneracies(begin, result); + doTriangulate(begin, result); + } + + + + template<typename project_t, typename vert_t, typename distance_calc_t> + void improve(const project_t &project, + const std::vector<vert_t> &poly, + distance_calc_t dist, + std::vector<tri_idx> &result) { + detail::tri_pairs_t tri_pairs; + +#if defined(CARVE_DEBUG) + bool warn = false; + for (size_t i = 0; i < result.size(); ++i) { + tri_idx &t = result[i]; + if (carve::geom2d::signedArea(project(poly[t.a]), project(poly[t.b]), project(poly[t.c])) > 0) { + warn = true; + } + } + if (warn) { + std::cerr << "carve::triangulate::improve(): Some triangles are incorrectly oriented. Results may be incorrect." << std::endl; + } +#endif + + for (size_t i = 0; i < result.size(); ++i) { + tri_idx &t = result[i]; + tri_pairs.insert(t.a, t.b, &t); + tri_pairs.insert(t.b, t.c, &t); + tri_pairs.insert(t.c, t.a, &t); + } + + std::vector<detail::tri_pair_t *> edges; + size_t n = tri_pairs.getInternalEdges(project, poly, dist, edges); + for (size_t i = 0; i < n; ++i) { + edges[i]->idx = i; + } + + // procedure: + // while a tri pair with a positive score exists: + // p = pair with highest positive score + // flip p, rewriting its two referenced triangles. + // negate p's score + // for each q in the up-to-four adjoining tri pairs: + // update q's tri ptr, if changed, and its score. + +#if defined(CARVE_DEBUG) + double initial_score = 0; + for (size_t i = 0; i < edges.size(); ++i) { + initial_score += edges[i]->edgeLen(project, poly, dist); + } + std::cerr << "initial score: " << initial_score << std::endl; +#endif + + while (n) { + tri_pairs.flip(project, poly, dist, edges, n); + } + +#if defined(CARVE_DEBUG) + double final_score = 0; + for (size_t i = 0; i < edges.size(); ++i) { + final_score += edges[i]->edgeLen(project, poly, dist); + } + std::cerr << "final score: " << final_score << std::endl; +#endif + +#if defined(CARVE_DEBUG) + if (!warn) { + for (size_t i = 0; i < result.size(); ++i) { + tri_idx &t = result[i]; + CARVE_ASSERT (carve::geom2d::signedArea(project(poly[t.a]), project(poly[t.b]), project(poly[t.c])) <= 0.0); + } + } +#endif + } + + + + template<typename project_t, typename vert_t> + void improve(const project_t &project, + const std::vector<vert_t> &poly, + std::vector<tri_idx> &result) { + improve(project, poly, carve::geom::distance_functor(), result); + } + + + + } +} diff --git a/extern/carve/include/carve/util.hpp b/extern/carve/include/carve/util.hpp new file mode 100644 index 00000000000..dc33f5b64ce --- /dev/null +++ b/extern/carve/include/carve/util.hpp @@ -0,0 +1,31 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +namespace carve { + namespace util { + struct min_functor { + template<typename T> + const T &operator()(const T &a, const T &b) const { return std::min(a, b); } + }; + struct max_functor { + template<typename T> + const T &operator()(const T &a, const T &b) const { return std::max(a, b); } + }; + } +} diff --git a/extern/carve/include/carve/vcpp_config.h b/extern/carve/include/carve/vcpp_config.h new file mode 100644 index 00000000000..5ebd4006159 --- /dev/null +++ b/extern/carve/include/carve/vcpp_config.h @@ -0,0 +1,17 @@ +/* include/carve/config.h. Generated from config.h.in by configure. */ +#pragma once + +#include <math.h> + +/* Define if using boost collections. Preferred, because the visual C++ unordered collections are slow and memory hungry. */ +#define HAVE_BOOST_UNORDERED_COLLECTIONS + +#if defined(_MSC_VER) +# pragma warning(disable:4201) +#endif + +#include <math.h> + +static inline double round(double value) { + return (value >= 0) ? floor(value + 0.5) : ceil(value - 0.5); +} diff --git a/extern/carve/include/carve/vector.hpp b/extern/carve/include/carve/vector.hpp new file mode 100644 index 00000000000..6753ddb85d4 --- /dev/null +++ b/extern/carve/include/carve/vector.hpp @@ -0,0 +1,163 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/math_constants.hpp> +#include <carve/geom.hpp> +#include <carve/geom3d.hpp> + +#include <sstream> +#include <algorithm> + +#include <math.h> + +namespace carve { + namespace geom3d { + + struct hash_vector_ptr { + size_t operator()(const Vector * const &v) const { + return (size_t)v; + } + size_t operator()(const std::pair<const Vector *, const Vector *> &v) const { + size_t r = (size_t)v.first; + size_t s = (size_t)v.second; + return r ^ ((s >> 16) | (s << 16)); + } + }; + + + + struct vec_adapt_ident { + const Vector &operator()(const Vector &v) const { return v; } + Vector &operator()(Vector &v) const { return v; } + }; + + + + struct vec_adapt_ptr { + const Vector &operator()(const Vector * const &v) const { return *v; } + Vector &operator()(Vector *&v) const { return *v; } + }; + + + + struct vec_adapt_pair_first { + template<typename pair_t> const Vector &operator()(const pair_t &v) const { return v.first; } + template<typename pair_t> Vector &operator()(pair_t &v) const { return v.first; } + }; + + + + struct vec_adapt_pair_second { + template<typename pair_t> const Vector &operator()(const pair_t &v) const { return v.second; } + template<typename pair_t> Vector &operator()(pair_t &v) const { return v.second; } + }; + + + + template<typename adapt_t> + struct vec_cmp_lt_x { + adapt_t adapt; + vec_cmp_lt_x(adapt_t _adapt = adapt_t()) : adapt(_adapt) {} + template<typename input_t> bool operator()(const input_t &a, const input_t &b) const { return adapt(a).x < adapt(b).x; } + }; + template<typename adapt_t> vec_cmp_lt_x<adapt_t> vec_lt_x(adapt_t &adapt) { return vec_cmp_lt_x<adapt_t>(adapt); } + + + + template<typename adapt_t> + struct vec_cmp_lt_y { + adapt_t adapt; + vec_cmp_lt_y(adapt_t _adapt = adapt_t()) : adapt(_adapt) {} + template<typename input_t> bool operator()(const input_t &a, const input_t &b) const { return adapt(a).y < adapt(b).y; } + }; + template<typename adapt_t> vec_cmp_lt_y<adapt_t> vec_lt_y(adapt_t &adapt) { return vec_cmp_lt_y<adapt_t>(adapt); } + + + + template<typename adapt_t> + struct vec_cmp_lt_z { + adapt_t adapt; + vec_cmp_lt_z(adapt_t _adapt = adapt_t()) : adapt(_adapt) {} + template<typename input_t> bool operator()(const input_t &a, const input_t &b) const { return adapt(a).z < adapt(b).z; } + }; + template<typename adapt_t> vec_cmp_lt_z<adapt_t> vec_lt_z(adapt_t &adapt) { return vec_cmp_lt_z<adapt_t>(adapt); } + + + + template<typename adapt_t> + struct vec_cmp_gt_x { + adapt_t adapt; + vec_cmp_gt_x(adapt_t _adapt = adapt_t()) : adapt(_adapt) {} + template<typename input_t> bool operator()(const input_t &a, const input_t &b) const { return adapt(a).x > adapt(b).x; } + }; + template<typename adapt_t> vec_cmp_gt_x<adapt_t> vec_gt_x(adapt_t &adapt) { return vec_cmp_gt_x<adapt_t>(adapt); } + + + + template<typename adapt_t> + struct vec_cmp_gt_y { + adapt_t adapt; + vec_cmp_gt_y(adapt_t _adapt = adapt_t()) : adapt(_adapt) {} + template<typename input_t> bool operator()(const input_t &a, const input_t &b) const { return adapt(a).y > adapt(b).y; } + }; + template<typename adapt_t> vec_cmp_gt_y<adapt_t> vec_gt_y(adapt_t &adapt) { return vec_cmp_gt_y<adapt_t>(adapt); } + + + + template<typename adapt_t> + struct vec_cmp_gt_z { + adapt_t adapt; + vec_cmp_gt_z(adapt_t _adapt = adapt_t()) : adapt(_adapt) {} + template<typename input_t> bool operator()(const input_t &a, const input_t &b) const { return adapt(a).z > adapt(b).z; } + }; + template<typename adapt_t> vec_cmp_gt_z<adapt_t> vec_gt_z(adapt_t &adapt) { return vec_cmp_gt_z<adapt_t>(adapt); } + + + + template<typename iter_t, typename adapt_t> + void sortInDirectionOfRay(const Vector &ray_dir, iter_t begin, iter_t end, adapt_t adapt) { + switch (carve::geom::largestAxis(ray_dir)) { + case 0: + if (ray_dir.x > 0) { + std::sort(begin, end, vec_lt_x(adapt)); + } else { + std::sort(begin, end, vec_gt_x(adapt)); + } + break; + case 1: + if (ray_dir.y > 0) { + std::sort(begin, end, vec_lt_y(adapt)); + } else { + std::sort(begin, end, vec_gt_y(adapt)); + } + break; + case 2: + if (ray_dir.z > 0) { + std::sort(begin, end, vec_lt_z(adapt)); + } else { + std::sort(begin, end, vec_gt_z(adapt)); + } + break; + } + } + + } +} diff --git a/extern/carve/include/carve/vertex_decl.hpp b/extern/carve/include/carve/vertex_decl.hpp new file mode 100644 index 00000000000..62c76473020 --- /dev/null +++ b/extern/carve/include/carve/vertex_decl.hpp @@ -0,0 +1,111 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/geom2d.hpp> +#include <carve/vector.hpp> +#include <carve/matrix.hpp> +#include <carve/geom3d.hpp> +#include <carve/aabb.hpp> +#include <carve/tag.hpp> + +#include <vector> +#include <list> +#include <map> + +namespace carve { + namespace poly { + + + + struct Object; + + + + template<unsigned ndim> + class Vertex : public tagable { + public: + typedef carve::geom::vector<ndim> vector_t; + typedef Object obj_t; + + vector_t v; + obj_t *owner; + + Vertex() : tagable(), v() { + } + + ~Vertex() { + } + + Vertex(const vector_t &_v) : tagable(), v(_v) { + } + }; + + + + struct hash_vertex_ptr { + template<unsigned ndim> + size_t operator()(const Vertex<ndim> * const &v) const { + return (size_t)v; + } + + template<unsigned ndim> + size_t operator()(const std::pair<const Vertex<ndim> *, const Vertex<ndim> *> &v) const { + size_t r = (size_t)v.first; + size_t s = (size_t)v.second; + return r ^ ((s >> 16) | (s << 16)); + } + + }; + + + + template<unsigned ndim> + double distance(const Vertex<ndim> *v1, const Vertex<ndim> *v2) { + return distance(v1->v, v2->v); + } + + template<unsigned ndim> + double distance(const Vertex<ndim> &v1, const Vertex<ndim> &v2) { + return distance(v1.v, v2.v); + } + + struct vec_adapt_vertex_ref { + template<unsigned ndim> + const typename Vertex<ndim>::vector_t &operator()(const Vertex<ndim> &v) const { return v.v; } + + template<unsigned ndim> + typename Vertex<ndim>::vector_t &operator()(Vertex<ndim> &v) const { return v.v; } + }; + + + + struct vec_adapt_vertex_ptr { + template<unsigned ndim> + const typename Vertex<ndim>::vector_t &operator()(const Vertex<ndim> *v) const { return v->v; } + + template<unsigned ndim> + typename Vertex<ndim>::vector_t &operator()(Vertex<ndim> *v) const { return v->v; } + }; + + + + } +} diff --git a/extern/carve/include/carve/vertex_impl.hpp b/extern/carve/include/carve/vertex_impl.hpp new file mode 100644 index 00000000000..7e1803ae89c --- /dev/null +++ b/extern/carve/include/carve/vertex_impl.hpp @@ -0,0 +1,24 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +namespace carve { + namespace poly { + + } +} diff --git a/extern/carve/include/carve/win32.h b/extern/carve/include/carve/win32.h new file mode 100755 index 00000000000..b73c9535f52 --- /dev/null +++ b/extern/carve/include/carve/win32.h @@ -0,0 +1,53 @@ +// Copyright 2006 Tobias Sargeant (toby@permuted.net) +// All rights reserved. +#pragma once + +#pragma warning (disable : 4996) +#pragma warning (disable : 4786) + +#include <string.h> +#include <stdlib.h> + +inline int strcasecmp(const char *a, const char *b) { + return _stricmp(a,b); +} + +inline void srandom(unsigned long input) { + srand(input); +} + +inline long random() { + return rand(); +} + +#if defined(_MSC_VER) +# include <carve/cbrt.h> + +#if _MSC_VER < 1300 +// intptr_t is an integer type that is big enough to hold a pointer +// It is not defined in VC6 so include a definition here for the older compiler +typedef long intptr_t; +typedef unsigned long uintptr_t; +#endif + +# if _MSC_VER < 1600 +// stdint.h is not available before VS2010 +#if defined(_WIN32) && !defined(__MINGW32__) +/* The __intXX are built-in types of the visual complier! So we don't + need to include anything else here. + This typedefs should be in sync with types from MEM_sys_types.h */ + +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; + +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +#endif +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +# else +# include <stdint.h> +# endif +#endif diff --git a/extern/carve/lib/aabb.cpp b/extern/carve/lib/aabb.cpp new file mode 100644 index 00000000000..188929a8cfa --- /dev/null +++ b/extern/carve/lib/aabb.cpp @@ -0,0 +1,29 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/aabb.hpp> +#include <carve/geom3d.hpp> + +namespace carve { + namespace geom3d { + } +} + diff --git a/extern/carve/lib/carve.cpp b/extern/carve/lib/carve.cpp new file mode 100644 index 00000000000..9af2d0408fb --- /dev/null +++ b/extern/carve/lib/carve.cpp @@ -0,0 +1,29 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/carve.hpp> + +#define DEF_EPSILON 1.4901161193847656e-08 + +namespace carve { + double EPSILON = DEF_EPSILON; + double EPSILON2 = DEF_EPSILON * DEF_EPSILON; +} diff --git a/extern/carve/lib/convex_hull.cpp b/extern/carve/lib/convex_hull.cpp new file mode 100644 index 00000000000..616d8cbe561 --- /dev/null +++ b/extern/carve/lib/convex_hull.cpp @@ -0,0 +1,100 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/csg.hpp> +#include <carve/convex_hull.hpp> + +#include <algorithm> + +namespace { + + bool grahamScan(const std::vector<carve::geom2d::P2> &points, + int vpp, int vp, + const std::vector<int> &ordered, + int start, + std::vector<int> &result, int _i = 0) { + carve::geom2d::P2 v1 = points[vp] - points[vpp]; + if (start == (int)ordered.size()) return true; + + for (int i = start; i < (int)ordered.size(); ++i) { + int v = ordered[i]; + carve::geom2d::P2 v2 = points[v] - points[vp]; + + double cp = v1.x * v2.y - v2.x * v1.y; + if (cp < 0) return false; + + int j = i + 1; + while (j < (int)ordered.size() && points[ordered[j]] == points[v]) j++; + + result.push_back(v); + if (grahamScan(points, vp, v, ordered, j, result, _i + 1)) return true; + result.pop_back(); + } + + return false; + } + +} + +namespace carve { + namespace geom { + + std::vector<int> convexHull(const std::vector<carve::geom2d::P2> &points) { + double max_x = points[0].x; + unsigned max_v = 0; + + for (unsigned i = 1; i < points.size(); ++i) { + if (points[i].x > max_x) { + max_x = points[i].x; + max_v = i; + } + } + + std::vector<std::pair<double, double> > angle_dist; + std::vector<int> ordered; + angle_dist.reserve(points.size()); + ordered.reserve(points.size() - 1); + for (unsigned i = 0; i < points.size(); ++i) { + if (i == max_v) continue; + angle_dist[i] = std::make_pair(carve::math::ANG(carve::geom2d::atan2(points[i] - points[max_v])), distance2(points[i], points[max_v])); + ordered.push_back(i); + } + + std::sort(ordered.begin(), + ordered.end(), + make_index_sort(angle_dist.begin())); + + std::vector<int> result; + result.push_back(max_v); + result.push_back(ordered[0]); + + if (!grahamScan(points, max_v, ordered[0], ordered, 1, result)) { + result.clear(); + throw carve::exception("convex hull failed!"); + } + + return result; + } + + } +} + + diff --git a/extern/carve/lib/csg.cpp b/extern/carve/lib/csg.cpp new file mode 100644 index 00000000000..3d3dfb8bf75 --- /dev/null +++ b/extern/carve/lib/csg.cpp @@ -0,0 +1,93 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/csg.hpp> +#include "csg_detail.hpp" + + +const char *carve::csg::ENUM(carve::csg::FaceClass f) { + if (f == FACE_ON_ORIENT_OUT) return "FACE_ON_ORIENT_OUT"; + if (f == FACE_OUT) return "FACE_OUT"; + if (f == FACE_IN) return "FACE_IN"; + if (f == FACE_ON_ORIENT_IN) return "FACE_ON_ORIENT_IN"; + return "???"; +} + + + +const char *carve::csg::ENUM(carve::PointClass p) { + if (p == POINT_UNK) return "POINT_UNK"; + if (p == POINT_OUT) return "POINT_OUT"; + if (p == POINT_ON) return "POINT_ON"; + if (p == POINT_IN) return "POINT_IN"; + if (p == POINT_VERTEX) return "POINT_VERTEX"; + if (p == POINT_EDGE) return "POINT_EDGE"; + return "???"; +} + + + +void carve::csg::detail::LoopEdges::addFaceLoop(FaceLoop *fl) { + carve::mesh::MeshSet<3>::vertex_t *v1, *v2; + v1 = fl->vertices[fl->vertices.size() - 1]; + for (unsigned j = 0; j < fl->vertices.size(); ++j) { + v2 = fl->vertices[j]; + (*this)[std::make_pair(v1, v2)].push_back(fl); + v1 = v2; + } +} + + + +void carve::csg::detail::LoopEdges::sortFaceLoopLists() { + for (super::iterator i = begin(), e = end(); i != e; ++i) { + (*i).second.sort(); + } +} + + + +void carve::csg::detail::LoopEdges::removeFaceLoop(FaceLoop *fl) { + carve::mesh::MeshSet<3>::vertex_t *v1, *v2; + v1 = fl->vertices[fl->vertices.size() - 1]; + for (unsigned j = 0; j < fl->vertices.size(); ++j) { + v2 = fl->vertices[j]; + iterator l(find(std::make_pair(v1, v2))); + if (l != end()) { + (*l).second.remove(fl); + if (!(*l).second.size()) { + erase(l); + } + } + v1 = v2; + } +} + + + +carve::csg::FaceClass carve::csg::FaceLoopGroup::classificationAgainst(const carve::mesh::MeshSet<3>::mesh_t *mesh) const { + for (std::list<ClassificationInfo>::const_iterator i = classification.begin(); i != classification.end(); ++i) { + if ((*i).intersected_mesh == mesh) { + return (*i).classification; + } + } + return FACE_UNCLASSIFIED; +} diff --git a/extern/carve/lib/csg_collector.cpp b/extern/carve/lib/csg_collector.cpp new file mode 100644 index 00000000000..6e86b128b51 --- /dev/null +++ b/extern/carve/lib/csg_collector.cpp @@ -0,0 +1,371 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/csg.hpp> +#include <iostream> +#include "intersect_debug.hpp" + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) +void writePLY(const std::string &out_file, const carve::mesh::MeshSet<3> *poly, bool ascii); +#endif + + +namespace carve { + namespace csg { + namespace { + + class BaseCollector : public CSG::Collector { + BaseCollector(); + BaseCollector(const BaseCollector &); + BaseCollector &operator=(const BaseCollector &); + + protected: + struct face_data_t { + carve::mesh::MeshSet<3>::face_t *face; + const carve::mesh::MeshSet<3>::face_t *orig_face; + bool flipped; + face_data_t(carve::mesh::MeshSet<3>::face_t *_face, + const carve::mesh::MeshSet<3>::face_t *_orig_face, + bool _flipped) : face(_face), orig_face(_orig_face), flipped(_flipped) { + }; + }; + + std::list<face_data_t> faces; + + const carve::mesh::MeshSet<3> *src_a; + const carve::mesh::MeshSet<3> *src_b; + + BaseCollector(const carve::mesh::MeshSet<3> *_src_a, + const carve::mesh::MeshSet<3> *_src_b) : CSG::Collector(), src_a(_src_a), src_b(_src_b) { + } + + virtual ~BaseCollector() { + } + + void FWD(const carve::mesh::MeshSet<3>::face_t *orig_face, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices, + carve::geom3d::Vector /* normal */, + bool /* poly_a */, + FaceClass face_class, + CSG::Hooks &hooks) { + std::vector<carve::mesh::MeshSet<3>::face_t *> new_faces; + new_faces.reserve(1); + new_faces.push_back(orig_face->create(vertices.begin(), vertices.end(), false)); + hooks.processOutputFace(new_faces, orig_face, false); + for (size_t i = 0; i < new_faces.size(); ++i) { + faces.push_back(face_data_t(new_faces[i], orig_face, false)); + } + +#if defined(CARVE_DEBUG) && defined(DEBUG_PRINT_RESULT_FACES) + std::cerr << "+" << ENUM(face_class) << " "; + for (unsigned i = 0; i < vertices.size(); ++i) std::cerr << " " << vertices[i] << ":" << *vertices[i]; + std::cerr << std::endl; +#endif + } + + void REV(const carve::mesh::MeshSet<3>::face_t *orig_face, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices, + carve::geom3d::Vector /* normal */, + bool /* poly_a */, + FaceClass face_class, + CSG::Hooks &hooks) { + // normal = -normal; + std::vector<carve::mesh::MeshSet<3>::face_t *> new_faces; + new_faces.reserve(1); + new_faces.push_back(orig_face->create(vertices.begin(), vertices.end(), true)); + hooks.processOutputFace(new_faces, orig_face, true); + for (size_t i = 0; i < new_faces.size(); ++i) { + faces.push_back(face_data_t(new_faces[i], orig_face, true)); + } + +#if defined(CARVE_DEBUG) && defined(DEBUG_PRINT_RESULT_FACES) + std::cerr << "-" << ENUM(face_class) << " "; + for (unsigned i = 0; i < vertices.size(); ++i) std::cerr << " " << vertices[i] << ":" << *vertices[i]; + std::cerr << std::endl; +#endif + } + + virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices, + carve::geom3d::Vector normal, + bool poly_a, + FaceClass face_class, + CSG::Hooks &hooks) =0; + + virtual void collect(FaceLoopGroup *grp, CSG::Hooks &hooks) { + std::list<ClassificationInfo> &cinfo = (grp->classification); + + if (cinfo.size() == 0) { + std::cerr << "WARNING! group " << grp << " has no classification info!" << std::endl; + return; + } + + FaceClass fc = FACE_UNCLASSIFIED; + + unsigned fc_closed_bits = 0; + unsigned fc_open_bits = 0; + unsigned fc_bits = 0; + + for (std::list<ClassificationInfo>::const_iterator i = grp->classification.begin(), e = grp->classification.end(); i != e; ++i) { + + if ((*i).intersected_mesh == NULL) { + // classifier only returns global info + fc_closed_bits = class_to_class_bit((*i).classification); + break; + } + + if ((*i).classification == FACE_UNCLASSIFIED) continue; + if ((*i).intersectedMeshIsClosed()) { + fc_closed_bits |= class_to_class_bit((*i).classification); + } else { + fc_open_bits |= class_to_class_bit((*i).classification); + } + } + + if (fc_closed_bits) { + fc_bits = fc_closed_bits; + } else { + fc_bits = fc_open_bits; + } + + fc = class_bit_to_class(fc_bits); + + // handle the complex cases where a group is classified differently with respect to two or more closed manifolds. + if (fc == FACE_UNCLASSIFIED) { + unsigned inout_bits = fc_bits & FACE_NOT_ON_BIT; + unsigned on_bits = fc_bits & FACE_ON_BIT; + + // both in and out. indicates an invalid manifold embedding. + if (inout_bits == (FACE_IN_BIT | FACE_OUT_BIT)) goto out; + + // on, both orientations. could be caused by two manifolds touching at a face. + if (on_bits == (FACE_ON_ORIENT_IN_BIT | FACE_ON_ORIENT_OUT_BIT)) goto out; + + // in or out, but also on (with orientation). the on classification takes precedence. + fc = class_bit_to_class(on_bits); + } + + out: + + if (fc == FACE_UNCLASSIFIED) { + std::cerr << "group " << grp << " is unclassified!" << std::endl; + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + static int uc_count = 0; + + std::vector<carve::mesh::MeshSet<3>::face_t *> faces; + + for (FaceLoop *f = grp->face_loops.head; f; f = f->next) { + carve::mesh::MeshSet<3>::face_t *temp = f->orig_face->create(f->vertices.begin(), f->vertices.end(), false); + faces.push_back(temp); + } + + carve::mesh::MeshSet<3> *p = new carve::mesh::MeshSet<3>(faces); + + std::ostringstream filename; + filename << "classifier_fail_" << ++uc_count << ".ply"; + std::string out(filename.str().c_str()); + ::writePLY(out, p, false); + + delete p; +#endif + + return; + } + + bool is_poly_a = grp->src == src_a; + + for (FaceLoop *f = grp->face_loops.head; f; f = f->next) { + collect(f->orig_face, f->vertices, f->orig_face->plane.N, is_poly_a, fc, hooks); + } + } + + virtual carve::mesh::MeshSet<3> *done(CSG::Hooks &hooks) { + std::vector<carve::mesh::MeshSet<3>::face_t *> f; + f.reserve(faces.size()); + for (std::list<face_data_t>::iterator i = faces.begin(); i != faces.end(); ++i) { + f.push_back((*i).face); + } + + carve::mesh::MeshSet<3> *p = new carve::mesh::MeshSet<3>(f); + + if (hooks.hasHook(carve::csg::CSG::Hooks::RESULT_FACE_HOOK)) { + for (std::list<face_data_t>::iterator i = faces.begin(); i != faces.end(); ++i) { + hooks.resultFace((*i).face, (*i).orig_face, (*i).flipped); + } + } + + return p; + } + }; + + + + class AllCollector : public BaseCollector { + public: + AllCollector(const carve::mesh::MeshSet<3> *_src_a, + const carve::mesh::MeshSet<3> *_src_b) : BaseCollector(_src_a, _src_b) { + } + virtual ~AllCollector() { + } + virtual void collect(FaceLoopGroup *grp, CSG::Hooks &hooks) { + for (FaceLoop *f = grp->face_loops.head; f; f = f->next) { + FWD(f->orig_face, f->vertices, f->orig_face->plane.N, f->orig_face->mesh->meshset == src_a, FACE_OUT, hooks); + } + } + virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices, + carve::geom3d::Vector normal, + bool poly_a, + FaceClass face_class, + CSG::Hooks &hooks) { + FWD(orig_face, vertices, normal, poly_a, face_class, hooks); + } + }; + + + + class UnionCollector : public BaseCollector { + public: + UnionCollector(const carve::mesh::MeshSet<3> *_src_a, + const carve::mesh::MeshSet<3> *_src_b) : BaseCollector(_src_a, _src_b) { + } + virtual ~UnionCollector() { + } + virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices, + carve::geom3d::Vector normal, + bool poly_a, + FaceClass face_class, + CSG::Hooks &hooks) { + if (face_class == FACE_OUT || (poly_a && face_class == FACE_ON_ORIENT_OUT)) { + FWD(orig_face, vertices, normal, poly_a, face_class, hooks); + } + } + }; + + + + class IntersectionCollector : public BaseCollector { + public: + IntersectionCollector(const carve::mesh::MeshSet<3> *_src_a, + const carve::mesh::MeshSet<3> *_src_b) : BaseCollector(_src_a, _src_b) { + } + virtual ~IntersectionCollector() { + } + virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices, + carve::geom3d::Vector normal, + bool poly_a, + FaceClass face_class, + CSG::Hooks &hooks) { + if (face_class == FACE_IN || (poly_a && face_class == FACE_ON_ORIENT_OUT)) { + FWD(orig_face, vertices, normal, poly_a, face_class, hooks); + } + } + }; + + + + class SymmetricDifferenceCollector : public BaseCollector { + public: + SymmetricDifferenceCollector(const carve::mesh::MeshSet<3> *_src_a, + const carve::mesh::MeshSet<3> *_src_b) : BaseCollector(_src_a, _src_b) { + } + virtual ~SymmetricDifferenceCollector() { + } + virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices, + carve::geom3d::Vector normal, + bool poly_a, + FaceClass face_class, + CSG::Hooks &hooks) { + if (face_class == FACE_OUT) { + FWD(orig_face, vertices, normal, poly_a, face_class, hooks); + } else if (face_class == FACE_IN) { + REV(orig_face, vertices, normal, poly_a, face_class, hooks); + } + } + }; + + + + class AMinusBCollector : public BaseCollector { + public: + AMinusBCollector(const carve::mesh::MeshSet<3> *_src_a, + const carve::mesh::MeshSet<3> *_src_b) : BaseCollector(_src_a, _src_b) { + } + virtual ~AMinusBCollector() { + } + virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices, + carve::geom3d::Vector normal, + bool poly_a, + FaceClass face_class, + CSG::Hooks &hooks) { + if ((face_class == FACE_OUT || face_class == FACE_ON_ORIENT_IN) && poly_a) { + FWD(orig_face, vertices, normal, poly_a, face_class, hooks); + } else if (face_class == FACE_IN && !poly_a) { + REV(orig_face, vertices, normal, poly_a, face_class, hooks); + } + } + }; + + + + class BMinusACollector : public BaseCollector { + public: + BMinusACollector(const carve::mesh::MeshSet<3> *_src_a, + const carve::mesh::MeshSet<3> *_src_b) : BaseCollector(_src_a, _src_b) { + } + virtual ~BMinusACollector() { + } + virtual void collect(const carve::mesh::MeshSet<3>::face_t *orig_face, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &vertices, + carve::geom3d::Vector normal, + bool poly_a, + FaceClass face_class, + CSG::Hooks &hooks) { + if ((face_class == FACE_OUT || face_class == FACE_ON_ORIENT_IN) && !poly_a) { + FWD(orig_face, vertices, normal, poly_a, face_class, hooks); + } else if (face_class == FACE_IN && poly_a) { + REV(orig_face, vertices, normal, poly_a, face_class, hooks); + } + } + }; + + } + + CSG::Collector *makeCollector(CSG::OP op, + const carve::mesh::MeshSet<3> *poly_a, + const carve::mesh::MeshSet<3> *poly_b) { + switch (op) { + case CSG::UNION: return new UnionCollector(poly_a, poly_b); + case CSG::INTERSECTION: return new IntersectionCollector(poly_a, poly_b); + case CSG::A_MINUS_B: return new AMinusBCollector(poly_a, poly_b); + case CSG::B_MINUS_A: return new BMinusACollector(poly_a, poly_b); + case CSG::SYMMETRIC_DIFFERENCE: return new SymmetricDifferenceCollector(poly_a, poly_b); + case CSG::ALL: return new AllCollector(poly_a, poly_b); + } + return NULL; + } + } +} diff --git a/extern/carve/lib/csg_collector.hpp b/extern/carve/lib/csg_collector.hpp new file mode 100644 index 00000000000..c68d3f3aa42 --- /dev/null +++ b/extern/carve/lib/csg_collector.hpp @@ -0,0 +1,24 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +namespace carve { + namespace csg { + CSG::Collector *makeCollector(CSG::OP op, + const carve::mesh::MeshSet<3> *poly_a, + const carve::mesh::MeshSet<3> *poly_b); + } +} diff --git a/extern/carve/lib/csg_data.hpp b/extern/carve/lib/csg_data.hpp new file mode 100644 index 00000000000..085d05ce8d5 --- /dev/null +++ b/extern/carve/lib/csg_data.hpp @@ -0,0 +1,52 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/csg.hpp> + +#include "csg_detail.hpp" + +struct carve::csg::detail::Data { +// * @param[out] vmap A mapping from vertex pointer to intersection point. +// * @param[out] emap A mapping from edge pointer to intersection points. +// * @param[out] fmap A mapping from face pointer to intersection points. +// * @param[out] fmap_rev A mapping from intersection points to face pointers. + // map from intersected vertex to intersection point. + VVMap vmap; + + // map from intersected edge to intersection points. + EVSMap emap; + + // map from intersected face to intersection points. + FVSMap fmap; + + // map from intersection point to intersected faces. + VFSMap fmap_rev; + + // created by divideEdges(). + // holds, for each edge, a + EVVMap divided_edges; + + // created by faceSplitEdges. + FV2SMap face_split_edges; + + // mapping from vertex to edge for potentially intersected + // faces. Saves building the vertex to edge map for all faces of + // both meshes. + VEVecMap vert_to_edges; +}; diff --git a/extern/carve/lib/csg_detail.hpp b/extern/carve/lib/csg_detail.hpp new file mode 100644 index 00000000000..4b8fca3d2d2 --- /dev/null +++ b/extern/carve/lib/csg_detail.hpp @@ -0,0 +1,71 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include <carve/carve.hpp> + +#include <carve/polyhedron_base.hpp> + +namespace carve { + namespace csg { + namespace detail { + + typedef std::unordered_set<carve::mesh::MeshSet<3>::vertex_t *> VSet; + typedef std::unordered_set<carve::mesh::MeshSet<3>::face_t *> FSet; + + typedef std::set<carve::mesh::MeshSet<3>::vertex_t *> VSetSmall; + typedef std::set<csg::V2> V2SetSmall; + typedef std::set<carve::mesh::MeshSet<3>::face_t *> FSetSmall; + + typedef std::unordered_map<carve::mesh::MeshSet<3>::vertex_t *, VSetSmall> VVSMap; + typedef std::unordered_map<carve::mesh::MeshSet<3>::edge_t *, VSetSmall> EVSMap; + typedef std::unordered_map<carve::mesh::MeshSet<3>::face_t *, VSetSmall> FVSMap; + + typedef std::unordered_map<carve::mesh::MeshSet<3>::vertex_t *, FSetSmall> VFSMap; + typedef std::unordered_map<carve::mesh::MeshSet<3>::face_t *, V2SetSmall> FV2SMap; + + typedef std::unordered_map< + carve::mesh::MeshSet<3>::edge_t *, + std::vector<carve::mesh::MeshSet<3>::vertex_t *> > EVVMap; + + typedef std::unordered_map<carve::mesh::MeshSet<3>::vertex_t *, + std::vector<carve::mesh::MeshSet<3>::edge_t *> > VEVecMap; + + + class LoopEdges : public std::unordered_map<V2, std::list<FaceLoop *> > { + typedef std::unordered_map<V2, std::list<FaceLoop *> > super; + + public: + void addFaceLoop(FaceLoop *fl); + void sortFaceLoopLists(); + void removeFaceLoop(FaceLoop *fl); + }; + + } + } +} + + + +static inline std::ostream &operator<<(std::ostream &o, const carve::csg::detail::FSet &s) { + const char *sep=""; + for (carve::csg::detail::FSet::const_iterator i = s.begin(); i != s.end(); ++i) { + o << sep << *i; sep=","; + } + return o; +} diff --git a/extern/carve/lib/edge.cpp b/extern/carve/lib/edge.cpp new file mode 100644 index 00000000000..4414e6496f3 --- /dev/null +++ b/extern/carve/lib/edge.cpp @@ -0,0 +1,23 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/poly.hpp> + diff --git a/extern/carve/lib/face.cpp b/extern/carve/lib/face.cpp new file mode 100644 index 00000000000..c0718923cbb --- /dev/null +++ b/extern/carve/lib/face.cpp @@ -0,0 +1,278 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/poly.hpp> + +double CALC_X(const carve::geom::plane<3> &p, double y, double z) { return -(p.d + p.N.y * y + p.N.z * z) / p.N.x; } +double CALC_Y(const carve::geom::plane<3> &p, double x, double z) { return -(p.d + p.N.x * x + p.N.z * z) / p.N.y; } +double CALC_Z(const carve::geom::plane<3> &p, double x, double y) { return -(p.d + p.N.x * x + p.N.y * y) / p.N.z; } + +namespace carve { + namespace poly { + + carve::geom2d::P2 _project_1(const carve::geom3d::Vector &v) { + return carve::geom::VECTOR(v.z, v.y); + } + + carve::geom2d::P2 _project_2(const carve::geom3d::Vector &v) { + return carve::geom::VECTOR(v.x, v.z); + } + + carve::geom2d::P2 _project_3(const carve::geom3d::Vector &v) { + return carve::geom::VECTOR(v.y, v.x); + } + + carve::geom2d::P2 _project_4(const carve::geom3d::Vector &v) { + return carve::geom::VECTOR(v.y, v.z); + } + + carve::geom2d::P2 _project_5(const carve::geom3d::Vector &v) { + return carve::geom::VECTOR(v.z, v.x); + } + + carve::geom2d::P2 _project_6(const carve::geom3d::Vector &v) { + return carve::geom::VECTOR(v.x, v.y); + } + + + carve::geom3d::Vector _unproject_1(const carve::geom2d::P2 &p, const carve::geom3d::Plane &plane_eqn) { + return carve::geom::VECTOR(CALC_X(plane_eqn, p.y, p.x), p.y, p.x); + } + + carve::geom3d::Vector _unproject_2(const carve::geom2d::P2 &p, const carve::geom3d::Plane &plane_eqn) { + return carve::geom::VECTOR(p.x, CALC_Y(plane_eqn, p.x, p.y), p.y); + } + + carve::geom3d::Vector _unproject_3(const carve::geom2d::P2 &p, const carve::geom3d::Plane &plane_eqn) { + return carve::geom::VECTOR(p.y, p.x, CALC_Z(plane_eqn, p.y, p.x)); + } + + carve::geom3d::Vector _unproject_4(const carve::geom2d::P2 &p, const carve::geom3d::Plane &plane_eqn) { + return carve::geom::VECTOR(CALC_X(plane_eqn, p.x, p.y), p.x, p.y); + } + + carve::geom3d::Vector _unproject_5(const carve::geom2d::P2 &p, const carve::geom3d::Plane &plane_eqn) { + return carve::geom::VECTOR(p.y, CALC_Y(plane_eqn, p.y, p.x), p.x); + } + + carve::geom3d::Vector _unproject_6(const carve::geom2d::P2 &p, const carve::geom3d::Plane &plane_eqn) { + return carve::geom::VECTOR(p.x, p.y, CALC_Z(plane_eqn, p.x, p.y)); + } + + static carve::geom2d::P2 (*project_tab[2][3])(const carve::geom3d::Vector &) = { + { &_project_1, &_project_2, &_project_3 }, + { &_project_4, &_project_5, &_project_6 } + }; + + static carve::geom3d::Vector (*unproject_tab[2][3])(const carve::geom2d::P2 &, const carve::geom3d::Plane &) = { + { &_unproject_1, &_unproject_2, &_unproject_3 }, + { &_unproject_4, &_unproject_5, &_unproject_6 } + }; + + // only implemented for 3d. + template<unsigned ndim> + typename Face<ndim>::project_t Face<ndim>::getProjector(bool positive_facing, int axis) { + return NULL; + } + + template<> + Face<3>::project_t Face<3>::getProjector(bool positive_facing, int axis) { + return project_tab[positive_facing ? 1 : 0][axis]; + } + + template<unsigned ndim> + typename Face<ndim>::unproject_t Face<ndim>::getUnprojector(bool positive_facing, int axis) { + return NULL; + } + + template<> + Face<3>::unproject_t Face<3>::getUnprojector(bool positive_facing, int axis) { + return unproject_tab[positive_facing ? 1 : 0][axis]; + } + + + + template<unsigned ndim> + Face<ndim>::Face(const std::vector<const vertex_t *> &_vertices, + bool delay_recalc) : tagable() { + vertices = _vertices; + edges.resize(nVertices(), NULL); + if (!delay_recalc && !recalc()) { } + } + + template<unsigned ndim> + Face<ndim>::Face(const vertex_t *a, + const vertex_t *b, + const vertex_t *c, + bool delay_recalc) : tagable() { + vertices.reserve(3); + vertices.push_back(a); + vertices.push_back(b); + vertices.push_back(c); + edges.resize(3, NULL); + if (!delay_recalc && !recalc()) { } + } + + template<unsigned ndim> + Face<ndim>::Face(const vertex_t *a, + const vertex_t *b, + const vertex_t *c, + const vertex_t *d, + bool delay_recalc) : tagable() { + vertices.reserve(4); + vertices.push_back(a); + vertices.push_back(b); + vertices.push_back(c); + vertices.push_back(d); + edges.resize(4, NULL); + if (!delay_recalc && !recalc()) { } + } + + template<unsigned ndim> + void Face<ndim>::invert() { + size_t n_verts = vertices.size(); + std::reverse(vertices.begin(), vertices.end()); + + if (project != NULL) { + plane_eqn.negate(); + + int da = carve::geom::largestAxis(plane_eqn.N); + + project = getProjector(plane_eqn.N.v[da] > 0, da); + unproject = getUnprojector(plane_eqn.N.v[da] > 0, da); + } + + std::reverse(edges.begin(), edges.end() - 1); + for (size_t i = 0; i < n_verts; i++) { + const vertex_t *v1 = vertices[i]; + const vertex_t *v2 = vertices[(i+1) % n_verts]; + CARVE_ASSERT((edges[i]->v1 == v1 && edges[i]->v2 == v2) || (edges[i]->v1 == v2 && edges[i]->v2 == v1)); + } + } + + template<unsigned ndim> + bool Face<ndim>::recalc() { + aabb.fit(vertices.begin(), vertices.end(), vec_adapt_vertex_ptr()); + + if (!carve::geom3d::fitPlane(vertices.begin(), vertices.end(), vec_adapt_vertex_ptr(), plane_eqn)) { + return false; + } + + int da = carve::geom::largestAxis(plane_eqn.N); + project = getProjector(false, da); + + double A = carve::geom2d::signedArea(vertices, projector()); + if ((A < 0.0) ^ (plane_eqn.N.v[da] < 0.0)) { + plane_eqn.negate(); + } + + project = getProjector(plane_eqn.N.v[da] > 0, da); + unproject = getUnprojector(plane_eqn.N.v[da] > 0, da); + + return true; + } + + template<unsigned ndim> + Face<ndim> *Face<ndim>::init(const Face *base, const std::vector<const vertex_t *> &_vertices, bool flipped) { + return init(base, _vertices.begin(), _vertices.end(), flipped); + } + + template<unsigned ndim> + bool Face<ndim>::containsPoint(const vector_t &p) const { + if (!carve::math::ZERO(carve::geom::distance(plane_eqn, p))) return false; + // return pointInPolySimple(vertices, projector(), (this->*project)(p)); + return carve::geom2d::pointInPoly(vertices, projector(), face::project(this, p)).iclass != POINT_OUT; + } + + template<unsigned ndim> + bool Face<ndim>::containsPointInProjection(const vector_t &p) const { + return carve::geom2d::pointInPoly(vertices, projector(), face::project(this, p)).iclass != POINT_OUT; + } + + template<unsigned ndim> + bool Face<ndim>::simpleLineSegmentIntersection(const carve::geom::linesegment<ndim> &line, + vector_t &intersection) const { + if (!line.OK()) return false; + + carve::geom3d::Vector p; + IntersectionClass intersects = carve::geom3d::lineSegmentPlaneIntersection(plane_eqn, + line, + p); + if (intersects == INTERSECT_NONE || intersects == INTERSECT_BAD) { + return false; + } + + carve::geom2d::P2 proj_p(face::project(this, p)); + if (carve::geom2d::pointInPolySimple(vertices, projector(), proj_p)) { + intersection = p; + return true; + } + return false; + } + + // XXX: should try to return a pre-existing vertex in the case of a + // line-vertex intersection. as it stands, this code isn't used, + // so... meh. + template<unsigned ndim> + IntersectionClass Face<ndim>::lineSegmentIntersection(const carve::geom::linesegment<ndim> &line, + vector_t &intersection) const { + if (!line.OK()) return INTERSECT_NONE; + + + carve::geom3d::Vector p; + IntersectionClass intersects = carve::geom3d::lineSegmentPlaneIntersection(plane_eqn, + line, + p); + if (intersects == INTERSECT_NONE || intersects == INTERSECT_BAD) { + return intersects; + } + + carve::geom2d::P2 proj_p(face::project(this, p)); + + carve::geom2d::PolyInclusionInfo pi = carve::geom2d::pointInPoly(vertices, projector(), proj_p); + switch (pi.iclass) { + case POINT_VERTEX: + intersection = p; + return INTERSECT_VERTEX; + + case POINT_EDGE: + intersection = p; + return INTERSECT_EDGE; + + case POINT_IN: + intersection = p; + return INTERSECT_FACE; + + case POINT_OUT: + return INTERSECT_NONE; + + default: + break; + } + return INTERSECT_NONE; + } + + + } +} + +// explicit instantiations. +template class carve::poly::Face<3>; diff --git a/extern/carve/lib/geom2d.cpp b/extern/carve/lib/geom2d.cpp new file mode 100644 index 00000000000..bfa84f5fd24 --- /dev/null +++ b/extern/carve/lib/geom2d.cpp @@ -0,0 +1,260 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/geom2d.hpp> +#include <carve/math.hpp> +#include <carve/aabb.hpp> + +#include <algorithm> +#include <iostream> + +namespace carve { + namespace geom2d { + + bool lineSegmentIntersection_simple(const P2 &l1v1, const P2 &l1v2, + const P2 &l2v1, const P2 &l2v2) { + geom::aabb<2> l1_aabb, l2_aabb; + l1_aabb.fit(l1v1, l1v2); + l2_aabb.fit(l2v1, l2v2); + + if (l1_aabb.maxAxisSeparation(l2_aabb) > 0.0) { + return false; + } + + double l1v1_side = orient2d(l2v1, l2v2, l1v1); + double l1v2_side = orient2d(l2v1, l2v2, l1v2); + + double l2v1_side = orient2d(l1v1, l1v2, l2v1); + double l2v2_side = orient2d(l1v1, l1v2, l2v2); + + if (l1v1_side * l1v2_side > 0.0 || l2v1_side * l2v2_side > 0.0) { + return false; + } + + return true; + } + + bool lineSegmentIntersection_simple(const LineSegment2 &l1, + const LineSegment2 &l2) { + return lineSegmentIntersection_simple(l1.v1, l1.v2, l2.v1, l2.v2); + } + + LineIntersectionInfo lineSegmentIntersection(const P2 &l1v1, const P2 &l1v2, + const P2 &l2v1, const P2 &l2v2) { + geom::aabb<2> l1_aabb, l2_aabb; + l1_aabb.fit(l1v1, l1v2); + l2_aabb.fit(l2v1, l2v2); + + if (l1_aabb.maxAxisSeparation(l2_aabb) > EPSILON) { + return LineIntersectionInfo(NO_INTERSECTION); + } + + if (carve::geom::equal(l1v1, l1v2) || carve::geom::equal(l2v1, l2v2)) { + throw carve::exception("zero length line in intersection test"); + } + + double dx13 = l1v1.x - l2v1.x; + double dy13 = l1v1.y - l2v1.y; + double dx43 = l2v2.x - l2v1.x; + double dy43 = l2v2.y - l2v1.y; + double dx21 = l1v2.x - l1v1.x; + double dy21 = l1v2.y - l1v1.y; + double ua_n = dx43 * dy13 - dy43 * dx13; + double ub_n = dx21 * dy13 - dy21 * dx13; + double u_d = dy43 * dx21 - dx43 * dy21; + + if (carve::math::ZERO(u_d)) { + if (carve::math::ZERO(ua_n)) { + if (carve::geom::equal(l1v2, l2v1)) { + return LineIntersectionInfo(INTERSECTION_PP, l1v2, 1, 2); + } + if (carve::geom::equal(l1v1, l2v2)) { + return LineIntersectionInfo(INTERSECTION_PP, l1v1, 0, 4); + } + if (l1v2.x > l2v1.x && l1v1.x < l2v2.x) { + return LineIntersectionInfo(COLINEAR); + } + } + return LineIntersectionInfo(NO_INTERSECTION); + } + + double ua = ua_n / u_d; + double ub = ub_n / u_d; + + if (-EPSILON <= ua && ua <= 1.0 + EPSILON && -EPSILON <= ub && ub <= 1.0 + EPSILON) { + double x = l1v1.x + ua * (l1v2.x - l1v1.x); + double y = l1v1.y + ua * (l1v2.y - l1v1.y); + + P2 p = carve::geom::VECTOR(x, y); + + double d1 = distance2(p, l1v1); + double d2 = distance2(p, l1v2); + double d3 = distance2(p, l2v1); + double d4 = distance2(p, l2v2); + + int n = -1; + + if (std::min(d1, d2) < EPSILON2) { + if (d1 < d2) { + p = l1v1; n = 0; + } else { + p = l1v2; n = 1; + } + if (std::min(d3, d4) < EPSILON2) { + if (d3 < d4) { + return LineIntersectionInfo(INTERSECTION_PP, p, n, 2); + } else { + return LineIntersectionInfo(INTERSECTION_PP, p, n, 3); + } + } else { + return LineIntersectionInfo(INTERSECTION_PL, p, n, -1); + } + } else if (std::min(d3, d4) < EPSILON2) { + if (d3 < d4) { + return LineIntersectionInfo(INTERSECTION_LP, l2v1, -1, 2); + } else { + return LineIntersectionInfo(INTERSECTION_LP, l2v2, -1, 3); + } + } else { + return LineIntersectionInfo(INTERSECTION_LL, p, -1, -1); + } + } + return LineIntersectionInfo(NO_INTERSECTION); + } + + LineIntersectionInfo lineSegmentIntersection(const LineSegment2 &l1, + const LineSegment2 &l2) { + return lineSegmentIntersection(l1.v1, l1.v2, l2.v1, l2.v2); + } + + double signedArea(const P2Vector &points) { + return signedArea(points, p2_adapt_ident()); + } + + bool pointInPolySimple(const P2Vector &points, const P2 &p) { + return pointInPolySimple(points, p2_adapt_ident(), p); + } + + PolyInclusionInfo pointInPoly(const P2Vector &points, const P2 &p) { + return pointInPoly(points, p2_adapt_ident(), p); + } + + int lineSegmentPolyIntersections(const P2Vector &points, + LineSegment2 line, + std::vector<PolyIntersectionInfo> &out) { + int count = 0; + + if (line.v2 < line.v1) { line.flip(); } + out.clear(); + + for (P2Vector::size_type i = 0, l = points.size(); i < l; i++) { + P2Vector::size_type j = (i + 1) % l; + LineIntersectionInfo e = + lineSegmentIntersection(LineSegment2(points[i], points[j]), line); + + switch (e.iclass) { + case INTERSECTION_PL: { + out.push_back(PolyIntersectionInfo(INTERSECT_EDGE, e.ipoint, i)); + count++; + break; + } + case INTERSECTION_PP: { + out.push_back(PolyIntersectionInfo(INTERSECT_VERTEX, e.ipoint, i + e.p2 - 2)); + count++; + break; + } + case INTERSECTION_LP: { + out.push_back(PolyIntersectionInfo(INTERSECT_VERTEX, e.ipoint, i + e.p2 - 2)); + count++; + break; + } + case INTERSECTION_LL: { + out.push_back(PolyIntersectionInfo(INTERSECT_EDGE, e.ipoint, i)); + count++; + break; + } + case COLINEAR: { + int n1 = (int)i, n2 = (int)j; + P2 q1 = points[i], q2 = points[j]; + + if (q2 < q1) { std::swap(q1, q2); std::swap(n1, n2); } + + if (equal(q1, line.v1)) { + out.push_back(PolyIntersectionInfo(INTERSECT_VERTEX, q1, n1)); + } else if (q1.x < line.v1.x) { + out.push_back(PolyIntersectionInfo(INTERSECT_EDGE, line.v1, i)); + } else { + out.push_back(PolyIntersectionInfo(INTERSECT_VERTEX, q1, n1)); + } + if (equal(q2, line.v2)) { + out.push_back(PolyIntersectionInfo(INTERSECT_VERTEX, q2, n2)); + } else if (line.v2.x < q2.x) { + out.push_back(PolyIntersectionInfo(INTERSECT_EDGE, line.v2, i)); + } else { + out.push_back(PolyIntersectionInfo(INTERSECT_VERTEX, q2, n2)); + } + + count += 2; + + break; + } + default: + break; + } + } + return count; + } + + struct FwdSort { + bool operator()(const PolyIntersectionInfo &a, + const PolyIntersectionInfo &b) const { + return a.ipoint < b.ipoint; + } + }; + + struct RevSort { + bool operator()(const PolyIntersectionInfo &a, + const PolyIntersectionInfo &b) const { + return a.ipoint < b.ipoint; + } + }; + + int sortedLineSegmentPolyIntersections(const P2Vector &points, + LineSegment2 line, + std::vector<PolyIntersectionInfo> &out) { + + bool swapped = line.v2 < line.v1; + + int count = lineSegmentPolyIntersections(points, line, out); + if (swapped) { + std::sort(out.begin(), out.end(), RevSort()); + } else { + std::sort(out.begin(), out.end(), FwdSort()); + } + return count; + } + + bool pickContainedPoint(const std::vector<P2> &poly, P2 &result) { + return pickContainedPoint(poly, p2_adapt_ident(), result); + } + + } +} diff --git a/extern/carve/lib/geom3d.cpp b/extern/carve/lib/geom3d.cpp new file mode 100644 index 00000000000..061dfe91802 --- /dev/null +++ b/extern/carve/lib/geom3d.cpp @@ -0,0 +1,164 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/math.hpp> +#include <carve/geom3d.hpp> + +#include <algorithm> + +namespace carve { + namespace geom3d { + + namespace { + int is_same(const std::vector<const Vector *> &a, + const std::vector<const Vector *> &b) { + if (a.size() != b.size()) return false; + + const size_t S = a.size(); + size_t i, j, p; + + for (p = 0; p < S; ++p) { + if (a[0] == b[p]) break; + } + if (p == S) return 0; + + for (i = 1, j = p + 1; j < S; ++i, ++j) if (a[i] != b[j]) goto not_fwd; + for ( j = 0; i < S; ++i, ++j) if (a[i] != b[j]) goto not_fwd; + return +1; + +not_fwd: + for (i = 1, j = p - 1; j != (size_t)-1; ++i, --j) if (a[i] != b[j]) goto not_rev; + for ( j = S - 1; i < S; ++i, --j) if (a[i] != b[j]) goto not_rev; + return -1; + +not_rev: + return 0; + } + } + + bool planeIntersection(const Plane &a, const Plane &b, Ray &r) { + Vector N = cross(a.N, b.N); + if (N.isZero()) { + return false; + } + N.normalize(); + + double dot_aa = dot(a.N, a.N); + double dot_bb = dot(b.N, b.N); + double dot_ab = dot(a.N, b.N); + + double determinant = dot_aa * dot_bb - dot_ab * dot_ab; + + double c1 = ( a.d * dot_bb - b.d * dot_ab) / determinant; + double c2 = ( b.d * dot_aa - a.d * dot_ab) / determinant; + + r.D = N; + r.v = c1 * a.N + c2 * b.N; + + return true; + } + + IntersectionClass rayPlaneIntersection(const Plane &p, + const Vector &v1, + const Vector &v2, + Vector &v, + double &t) { + Vector Rd = v2 - v1; + double Vd = dot(p.N, Rd); + double V0 = dot(p.N, v1) + p.d; + + if (carve::math::ZERO(Vd)) { + if (carve::math::ZERO(V0)) { + return INTERSECT_BAD; + } else { + return INTERSECT_NONE; + } + } + + t = -V0 / Vd; + v = v1 + t * Rd; + return INTERSECT_PLANE; + } + + IntersectionClass lineSegmentPlaneIntersection(const Plane &p, + const LineSegment &line, + Vector &v) { + double t; + IntersectionClass r = rayPlaneIntersection(p, line.v1, line.v2, v, t); + + if (r <= 0) return r; + + if ((t < 0.0 && !equal(v, line.v1)) || (t > 1.0 && !equal(v, line.v2))) + return INTERSECT_NONE; + + return INTERSECT_PLANE; + } + + RayIntersectionClass rayRayIntersection(const Ray &r1, + const Ray &r2, + Vector &v1, + Vector &v2, + double &mu1, + double &mu2) { + if (!r1.OK() || !r2.OK()) return RR_DEGENERATE; + + Vector v_13 = r1.v - r2.v; + + double d1343 = dot(v_13, r2.D); + double d4321 = dot(r2.D, r1.D); + double d1321 = dot(v_13, r1.D); + double d4343 = dot(r2.D, r2.D); + double d2121 = dot(r1.D, r1.D); + + double numer = d1343 * d4321 - d1321 * d4343; + double denom = d2121 * d4343 - d4321 * d4321; + + // dc - eb + // ------- + // ab - cc + + // dc/eb - 1 + // --------- + // a/e - cc/eb + + // dc/b - e + // -------- + // a - cc/b + + // d/b - e/c + // --------- + // a/c - c/b + + if (fabs(denom) * double(1<<10) <= fabs(numer)) { + return RR_PARALLEL; + } + + mu1 = numer / denom; + mu2 = (d1343 + d4321 * mu1) / d4343; + + v1 = r1.v + mu1 * r1.D; + v2 = r2.v + mu2 * r2.D; + + return (equal(v1, v2)) ? RR_INTERSECTION : RR_NO_INTERSECTION; + } + + } +} diff --git a/extern/carve/lib/intersect.cpp b/extern/carve/lib/intersect.cpp new file mode 100644 index 00000000000..35166a6411e --- /dev/null +++ b/extern/carve/lib/intersect.cpp @@ -0,0 +1,1668 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/csg.hpp> +#include <carve/pointset.hpp> +#include <carve/polyline.hpp> + +#include <list> +#include <set> +#include <iostream> + +#include <algorithm> + +#include "csg_detail.hpp" +#include "csg_data.hpp" + +#include "intersect_debug.hpp" +#include "intersect_common.hpp" +#include "intersect_classify_common.hpp" + +#include "csg_collector.hpp" + +#include <carve/timing.hpp> +#include <carve/colour.hpp> + + + + +carve::csg::VertexPool::VertexPool() { +} + +carve::csg::VertexPool::~VertexPool() { +} + +void carve::csg::VertexPool::reset() { + pool.clear(); +} + +carve::csg::VertexPool::vertex_t *carve::csg::VertexPool::get(const vertex_t::vector_t &v) { + if (!pool.size() || pool.back().size() == blocksize) { + pool.push_back(std::vector<vertex_t>()); + pool.back().reserve(blocksize); + } + pool.back().push_back(vertex_t(v)); + return &pool.back().back(); +} + +bool carve::csg::VertexPool::inPool(vertex_t *v) const { + for (pool_t::const_iterator i = pool.begin(); i != pool.end(); ++i) { + if (v >= &(i->front()) && v <= &(i->back())) return true; + } + return false; +} + + + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) +void writePLY(const std::string &out_file, const carve::point::PointSet *points, bool ascii); +void writePLY(const std::string &out_file, const carve::line::PolylineSet *lines, bool ascii); +void writePLY(const std::string &out_file, const carve::mesh::MeshSet<3> *poly, bool ascii); + +static carve::mesh::MeshSet<3> *faceLoopsToPolyhedron(const carve::csg::FaceLoopList &fl) { + std::vector<carve::mesh::MeshSet<3>::face_t *> faces; + faces.reserve(fl.size()); + for (carve::csg::FaceLoop *f = fl.head; f; f = f->next) { + faces.push_back(f->orig_face->create(f->vertices.begin(), f->vertices.end(), false)); + } + carve::mesh::MeshSet<3> *poly = new carve::mesh::MeshSet<3>(faces); + + return poly; +} +#endif + +namespace { + /** + * \brief Sort a range [\a beg, \a end) of vertices in order of increasing dot product of vertex - \a base on \dir. + * + * @tparam[in] T a forward iterator type. + * @param[in] dir The direction in which to sort vertices. + * @param[in] base + * @param[in] beg The start of the vertex range to sort. + * @param[in] end The end of the vertex range to sort. + * @param[out] out The sorted vertex result. + * @param[in] size_hint A hint regarding the size of the output + * vector (to avoid needing to be able to calculate \a + * end - \a beg). + */ + template<typename iter_t> + void orderVertices(iter_t beg, const iter_t end, + const carve::mesh::MeshSet<3>::vertex_t::vector_t &dir, + const carve::mesh::MeshSet<3>::vertex_t::vector_t &base, + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &out) { + typedef std::vector<std::pair<double, carve::mesh::MeshSet<3>::vertex_t *> > DVVector; + std::vector<std::pair<double, carve::mesh::MeshSet<3>::vertex_t *> > ordered_vertices; + + ordered_vertices.reserve(std::distance(beg, end)); + + for (; beg != end; ++beg) { + carve::mesh::MeshSet<3>::vertex_t *v = (*beg); + ordered_vertices.push_back(std::make_pair(carve::geom::dot(v->v - base, dir), v)); + } + + std::sort(ordered_vertices.begin(), ordered_vertices.end()); + + out.clear(); + out.reserve(ordered_vertices.size()); + for (DVVector::const_iterator + i = ordered_vertices.begin(), e = ordered_vertices.end(); + i != e; + ++i) { + out.push_back((*i).second); + } + } + + + + /** + * + * + * @param dir + * @param base + * @param beg + * @param end + */ + template<typename iter_t> + void selectOrderingProjection(iter_t beg, const iter_t end, + carve::mesh::MeshSet<3>::vertex_t::vector_t &dir, + carve::mesh::MeshSet<3>::vertex_t::vector_t &base) { + double dx, dy, dz; + carve::mesh::MeshSet<3>::vertex_t *min_x, *min_y, *min_z, *max_x, *max_y, *max_z; + if (beg == end) return; + min_x = max_x = min_y = max_y = min_z = max_z = *beg++; + for (; beg != end; ++beg) { + if (min_x->v.x > (*beg)->v.x) min_x = *beg; + if (min_y->v.y > (*beg)->v.y) min_y = *beg; + if (min_z->v.z > (*beg)->v.z) min_z = *beg; + if (max_x->v.x < (*beg)->v.x) max_x = *beg; + if (max_y->v.y < (*beg)->v.y) max_y = *beg; + if (max_z->v.z < (*beg)->v.z) max_z = *beg; + } + + dx = max_x->v.x - min_x->v.x; + dy = max_y->v.y - min_y->v.y; + dz = max_z->v.z - min_z->v.z; + + if (dx > dy) { + if (dx > dz) { + dir = max_x->v - min_x->v; base = min_x->v; + } else { + dir = max_z->v - min_z->v; base = min_z->v; + } + } else { + if (dy > dz) { + dir = max_y->v - min_y->v; base = min_y->v; + } else { + dir = max_z->v - min_z->v; base = min_z->v; + } + } + } +} + +namespace { + struct dump_data { + carve::mesh::MeshSet<3>::vertex_t *i_pt; + carve::csg::IObj i_src; + carve::csg::IObj i_tgt; + dump_data(carve::mesh::MeshSet<3>::vertex_t *_i_pt, + carve::csg::IObj _i_src, + carve::csg::IObj _i_tgt) : i_pt(_i_pt), i_src(_i_src), i_tgt(_i_tgt) { + } + }; + + + + struct dump_sort { + bool operator()(const dump_data &a, const dump_data &b) const { + if (a.i_pt->v.x < b.i_pt->v.x) return true; + if (a.i_pt->v.x > b.i_pt->v.x) return false; + if (a.i_pt->v.y < b.i_pt->v.y) return true; + if (a.i_pt->v.y > b.i_pt->v.y) return false; + if (a.i_pt->v.z < b.i_pt->v.z) return true; + if (a.i_pt->v.z > b.i_pt->v.z) return false; + return false; + } + }; + + + + void dump_intersections(std::ostream &out, carve::csg::Intersections &csg_intersections) { + std::vector<dump_data> temp; + + for (carve::csg::Intersections::const_iterator + i = csg_intersections.begin(), + ie = csg_intersections.end(); + i != ie; + ++i) { + const carve::csg::IObj &i_src = ((*i).first); + + for (carve::csg::Intersections::mapped_type::const_iterator + j = (*i).second.begin(), + je = (*i).second.end(); + j != je; + ++j) { + const carve::csg::IObj &i_tgt = ((*j).first); + carve::mesh::MeshSet<3>::vertex_t *i_pt = ((*j).second); + temp.push_back(dump_data(i_pt, i_src, i_tgt)); + } + } + + std::sort(temp.begin(), temp.end(), dump_sort()); + + for (size_t i = 0; i < temp.size(); ++i) { + const carve::csg::IObj &i_src = temp[i].i_src; + const carve::csg::IObj &i_tgt = temp[i].i_tgt; + out + << "INTERSECTION: " << temp[i].i_pt << " (" << temp[i].i_pt->v << ") " + << "is " << i_src << ".." << i_tgt << std::endl; + } + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + std::vector<carve::geom3d::Vector> vertices; + + for (carve::csg::Intersections::const_iterator + i = csg_intersections.begin(), + ie = csg_intersections.end(); + i != ie; + ++i) { + for (carve::csg::Intersections::mapped_type::const_iterator + j = (*i).second.begin(), + je = (*i).second.end(); + j != je; + ++j) { + carve::mesh::MeshSet<3>::vertex_t *i_pt = ((*j).second); + vertices.push_back(i_pt->v); + } + } + + carve::point::PointSet points(vertices); + + std::string outf("/tmp/intersection-points.ply"); + ::writePLY(outf, &points, true); +#endif + } + + + + /** + * \brief Populate a collection with the faces adjoining an edge. + * + * @tparam face_set_t A collection type. + * @param e The edge for which to collect adjoining faces. + * @param faces + */ + template<typename face_set_t> + inline void facesForVertex(carve::mesh::MeshSet<3>::vertex_t *v, + const carve::csg::detail::VEVecMap &ve, + face_set_t &faces) { + carve::csg::detail::VEVecMap::const_iterator vi = ve.find(v); + if (vi != ve.end()) { + for (carve::csg::detail::VEVecMap::data_type::const_iterator i = (*vi).second.begin(); i != (*vi).second.end(); ++i) { + faces.insert((*i)->face); + } + } + } + + /** + * \brief Populate a collection with the faces adjoining an edge. + * + * @tparam face_set_t A collection type. + * @param e The edge for which to collect adjoining faces. + * @param faces + */ + template<typename face_set_t> + inline void facesForEdge(carve::mesh::MeshSet<3>::edge_t *e, + face_set_t &faces) { + faces.insert(e->face); + } + + /** + * \brief Populate a collection with the faces adjoining a face. + * + * @tparam face_set_t A collection type. + * @param f The face for which to collect adjoining faces. + * @param faces + */ + template<typename face_set_t> + inline void facesForFace(carve::mesh::MeshSet<3>::face_t *f, + face_set_t &faces) { + faces.insert(f); + } + + /** + * \brief Populate a collection with the faces adjoining an intersection object. + * + * @tparam face_set_t A collection type holding const carve::poly::Polyhedron::face_t *. + * @param obj The intersection object for which to collect adjoining faces. + * @param faces + */ + template<typename face_set_t> + void facesForObject(const carve::csg::IObj &obj, + const carve::csg::detail::VEVecMap &ve, + face_set_t &faces) { + switch (obj.obtype) { + case carve::csg::IObj::OBTYPE_VERTEX: + facesForVertex(obj.vertex, ve, faces); + break; + + case carve::csg::IObj::OBTYPE_EDGE: + facesForEdge(obj.edge, faces); + break; + + case carve::csg::IObj::OBTYPE_FACE: + facesForFace(obj.face, faces); + break; + + default: + break; + } + } + + + +} + + + +bool carve::csg::CSG::Hooks::hasHook(unsigned hook_num) { + return hooks[hook_num].size() > 0; +} + +void carve::csg::CSG::Hooks::intersectionVertex(const carve::mesh::MeshSet<3>::vertex_t *vertex, + const IObjPairSet &intersections) { + for (std::list<Hook *>::iterator j = hooks[INTERSECTION_VERTEX_HOOK].begin(); + j != hooks[INTERSECTION_VERTEX_HOOK].end(); + ++j) { + (*j)->intersectionVertex(vertex, intersections); + } +} + +void carve::csg::CSG::Hooks::processOutputFace(std::vector<carve::mesh::MeshSet<3>::face_t *> &faces, + const carve::mesh::MeshSet<3>::face_t *orig_face, + bool flipped) { + for (std::list<Hook *>::iterator j = hooks[PROCESS_OUTPUT_FACE_HOOK].begin(); + j != hooks[PROCESS_OUTPUT_FACE_HOOK].end(); + ++j) { + (*j)->processOutputFace(faces, orig_face, flipped); + } +} + +void carve::csg::CSG::Hooks::resultFace(const carve::mesh::MeshSet<3>::face_t *new_face, + const carve::mesh::MeshSet<3>::face_t *orig_face, + bool flipped) { + for (std::list<Hook *>::iterator j = hooks[RESULT_FACE_HOOK].begin(); + j != hooks[RESULT_FACE_HOOK].end(); + ++j) { + (*j)->resultFace(new_face, orig_face, flipped); + } +} + +void carve::csg::CSG::Hooks::registerHook(Hook *hook, unsigned hook_bits) { + for (unsigned i = 0; i < HOOK_MAX; ++i) { + if (hook_bits & (1U << i)) { + hooks[i].push_back(hook); + } + } +} + +void carve::csg::CSG::Hooks::unregisterHook(Hook *hook) { + for (unsigned i = 0; i < HOOK_MAX; ++i) { + hooks[i].erase(std::remove(hooks[i].begin(), hooks[i].end(), hook), hooks[i].end()); + } +} + +void carve::csg::CSG::Hooks::reset() { + for (unsigned i = 0; i < HOOK_MAX; ++i) { + for (std::list<Hook *>::iterator j = hooks[i].begin(); j != hooks[i].end(); ++j) { + delete (*j); + } + hooks[i].clear(); + } +} + +carve::csg::CSG::Hooks::Hooks() : hooks() { + hooks.resize(HOOK_MAX); +} + +carve::csg::CSG::Hooks::~Hooks() { + reset(); +} + + + +void carve::csg::CSG::makeVertexIntersections() { + static carve::TimingName FUNC_NAME("CSG::makeVertexIntersections()"); + carve::TimingBlock block(FUNC_NAME); + vertex_intersections.clear(); + for (Intersections::const_iterator + i = intersections.begin(), + ie = intersections.end(); + i != ie; + ++i) { + const IObj &i_src = ((*i).first); + + for (Intersections::mapped_type::const_iterator + j = (*i).second.begin(), + je = (*i).second.end(); + j != je; + ++j) { + const IObj &i_tgt = ((*j).first); + carve::mesh::MeshSet<3>::vertex_t *i_pt = ((*j).second); + + vertex_intersections[i_pt].insert(std::make_pair(i_src, i_tgt)); + } + } +} + + + +static carve::mesh::MeshSet<3>::vertex_t *chooseWeldPoint( + const carve::csg::detail::VSet &equivalent, + carve::csg::VertexPool &vertex_pool) { + // XXX: choose a better weld point. + if (!equivalent.size()) return NULL; + + for (carve::csg::detail::VSet::const_iterator + i = equivalent.begin(), e = equivalent.end(); + i != e; + ++i) { + if (!vertex_pool.inPool((*i))) return (*i); + } + return *equivalent.begin(); +} + + + +static const carve::mesh::MeshSet<3>::vertex_t *weld( + const carve::csg::detail::VSet &equivalent, + carve::csg::VertexIntersections &vertex_intersections, + carve::csg::VertexPool &vertex_pool) { + carve::mesh::MeshSet<3>::vertex_t *weld_point = chooseWeldPoint(equivalent, vertex_pool); + +#if defined(CARVE_DEBUG) + std::cerr << "weld: " << equivalent.size() << " vertices ( "; + for (carve::csg::detail::VSet::const_iterator + i = equivalent.begin(), e = equivalent.end(); + i != e; + ++i) { + const carve::mesh::MeshSet<3>::vertex_t *v = (*i); + std::cerr << " " << v; + } + std::cerr << ") to " << weld_point << std::endl; +#endif + + if (!weld_point) return NULL; + + carve::csg::VertexIntersections::mapped_type &weld_tgt = (vertex_intersections[weld_point]); + + for (carve::csg::detail::VSet::const_iterator + i = equivalent.begin(), e = equivalent.end(); + i != e; + ++i) { + carve::mesh::MeshSet<3>::vertex_t *v = (*i); + + if (v != weld_point) { + carve::csg::VertexIntersections::iterator j = vertex_intersections.find(v); + + if (j != vertex_intersections.end()) { + weld_tgt.insert((*j).second.begin(), (*j).second.end()); + vertex_intersections.erase(j); + } + } + } + return weld_point; +} + + + +void carve::csg::CSG::groupIntersections() { +#if 0 // old code, to be removed. + static carve::TimingName GROUP_INTERSECTONS("groupIntersections()"); + + carve::TimingBlock block(GROUP_INTERSECTONS); + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> vertices; + detail::VVSMap graph; +#if defined(CARVE_DEBUG) + std::cerr << "groupIntersections()" << ": vertex_intersections.size()==" << vertex_intersections.size() << std::endl; +#endif + + vertices.reserve(vertex_intersections.size()); + for (carve::csg::VertexIntersections::const_iterator + i = vertex_intersections.begin(), + e = vertex_intersections.end(); + i != e; + ++i) + { + vertices.push_back((*i).first); + } + carve::geom3d::AABB aabb; + aabb.fit(vertices.begin(), vertices.end(), carve::poly::vec_adapt_vertex_ptr()); + Octree vertex_intersections_octree; + vertex_intersections_octree.setBounds(aabb); + + vertex_intersections_octree.addVertices(vertices); + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> out; + for (size_t i = 0, l = vertices.size(); i != l; ++i) { + // let's find all the vertices near this one. + out.clear(); + vertex_intersections_octree.findVerticesNearAllowDupes(vertices[i]->v, out); + + for (size_t j = 0; j < out.size(); ++j) { + if (vertices[i] != out[j] && carve::geom::equal(vertices[i]->v, out[j]->v)) { +#if defined(CARVE_DEBUG) + std::cerr << "EQ: " << vertices[i] << "," << out[j] << " " << vertices[i]->v << "," << out[j]->v << std::endl; +#endif + graph[vertices[i]].insert(out[j]); + graph[out[j]].insert(vertices[i]); + } + } + } + + detail::VSet visited, open; + while (graph.size()) { + visited.clear(); + open.clear(); + detail::VVSMap::iterator i = graph.begin(); + open.insert((*i).first); + while (open.size()) { + detail::VSet::iterator t = open.begin(); + const carve::mesh::MeshSet<3>::vertex_t *o = (*t); + open.erase(t); + i = graph.find(o); + CARVE_ASSERT(i != graph.end()); + visited.insert(o); + for (detail::VVSMap::mapped_type::const_iterator + j = (*i).second.begin(), + je = (*i).second.end(); + j != je; + ++j) { + if (visited.count((*j)) == 0) { + open.insert((*j)); + } + } + graph.erase(i); + } + weld(visited, vertex_intersections, vertex_pool); + } +#endif +} + + + +void carve::csg::CSG::intersectingFacePairs(detail::Data &data) { + static carve::TimingName FUNC_NAME("CSG::intersectingFacePairs()"); + carve::TimingBlock block(FUNC_NAME); + + // iterate over all intersection points. + for (VertexIntersections::const_iterator i = vertex_intersections.begin(), ie = vertex_intersections.end(); i != ie; ++i) { + carve::mesh::MeshSet<3>::vertex_t *i_pt = ((*i).first); + detail::VFSMap::mapped_type &face_set = (data.fmap_rev[i_pt]); + + // for all pairs of intersecting objects at this point + for (VertexIntersections::data_type::const_iterator j = (*i).second.begin(), je = (*i).second.end(); j != je; ++j) { + const IObj &i_src = ((*j).first); + const IObj &i_tgt = ((*j).second); + + // work out the faces involved. this updates fmap_rev. + facesForObject(i_src, data.vert_to_edges, face_set); + facesForObject(i_tgt, data.vert_to_edges, face_set); + + // record the intersection with respect to any involved vertex. + if (i_src.obtype == IObj::OBTYPE_VERTEX) data.vmap[i_src.vertex] = i_pt; + if (i_tgt.obtype == IObj::OBTYPE_VERTEX) data.vmap[i_tgt.vertex] = i_pt; + + // record the intersection with respect to any involved edge. + if (i_src.obtype == IObj::OBTYPE_EDGE) data.emap[i_src.edge].insert(i_pt); + if (i_tgt.obtype == IObj::OBTYPE_EDGE) data.emap[i_tgt.edge].insert(i_pt); + } + + // record the intersection with respect to each face. + for (carve::csg::detail::VFSMap::mapped_type::const_iterator k = face_set.begin(), ke = face_set.end(); k != ke; ++k) { + carve::mesh::MeshSet<3>::face_t *f = (*k); + data.fmap[f].insert(i_pt); + } + } +} + + + +void carve::csg::CSG::_generateVertexVertexIntersections(carve::mesh::MeshSet<3>::vertex_t *va, + carve::mesh::MeshSet<3>::edge_t *eb) { + if (intersections.intersects(va, eb->v1())) { + return; + } + + double d_v1 = carve::geom::distance2(va->v, eb->v1()->v); + + if (d_v1 < carve::EPSILON2) { + intersections.record(va, eb->v1(), va); + } +} + + + +void carve::csg::CSG::generateVertexVertexIntersections(carve::mesh::MeshSet<3>::face_t *a, + const std::vector<carve::mesh::MeshSet<3>::face_t *> &b) { + carve::mesh::MeshSet<3>::edge_t *ea, *eb; + + ea = a->edge; + do { + for (size_t i = 0; i < b.size(); ++i) { + carve::mesh::MeshSet<3>::face_t *t = b[i]; + eb = t->edge; + do { + _generateVertexVertexIntersections(ea->v1(), eb); + eb = eb->next; + } while (eb != t->edge); + } + ea = ea->next; + } while (ea != a->edge); +} + + + +void carve::csg::CSG::_generateVertexEdgeIntersections(carve::mesh::MeshSet<3>::vertex_t *va, + carve::mesh::MeshSet<3>::edge_t *eb) { + if (intersections.intersects(va, eb)) { + return; + } + + if (std::min(eb->v1()->v.x, eb->v2()->v.x) - carve::EPSILON > va->v.x || + std::max(eb->v1()->v.x, eb->v2()->v.x) + carve::EPSILON < va->v.x || + std::min(eb->v1()->v.y, eb->v2()->v.y) - carve::EPSILON > va->v.y || + std::max(eb->v1()->v.y, eb->v2()->v.y) + carve::EPSILON < va->v.y || + std::min(eb->v1()->v.z, eb->v2()->v.z) - carve::EPSILON > va->v.z || + std::max(eb->v1()->v.z, eb->v2()->v.z) + carve::EPSILON < va->v.z) { + return; + } + + double a = cross(eb->v2()->v - eb->v1()->v, va->v - eb->v1()->v).length2(); + double b = (eb->v2()->v - eb->v1()->v).length2(); + + if (a < b * carve::EPSILON2) { + // vertex-edge intersection + intersections.record(eb, va, va); + if (eb->rev) intersections.record(eb->rev, va, va); + } +} + + + +void carve::csg::CSG::generateVertexEdgeIntersections(carve::mesh::MeshSet<3>::face_t *a, + const std::vector<carve::mesh::MeshSet<3>::face_t *> &b) { + carve::mesh::MeshSet<3>::edge_t *ea, *eb; + + ea = a->edge; + do { + for (size_t i = 0; i < b.size(); ++i) { + carve::mesh::MeshSet<3>::face_t *t = b[i]; + eb = t->edge; + do { + _generateVertexEdgeIntersections(ea->v1(), eb); + eb = eb->next; + } while (eb != t->edge); + } + ea = ea->next; + } while (ea != a->edge); +} + + + +void carve::csg::CSG::_generateEdgeEdgeIntersections(carve::mesh::MeshSet<3>::edge_t *ea, + carve::mesh::MeshSet<3>::edge_t *eb) { + if (intersections.intersects(ea, eb)) { + return; + } + + carve::mesh::MeshSet<3>::vertex_t *v1 = ea->v1(), *v2 = ea->v2(); + carve::mesh::MeshSet<3>::vertex_t *v3 = eb->v1(), *v4 = eb->v2(); + + carve::geom::aabb<3> ea_aabb, eb_aabb; + ea_aabb.fit(v1->v, v2->v); + eb_aabb.fit(v3->v, v4->v); + if (ea_aabb.maxAxisSeparation(eb_aabb) > EPSILON) return; + + carve::mesh::MeshSet<3>::vertex_t::vector_t p1, p2; + double mu1, mu2; + + switch (carve::geom3d::rayRayIntersection(carve::geom3d::Ray(v2->v - v1->v, v1->v), + carve::geom3d::Ray(v4->v - v3->v, v3->v), + p1, p2, mu1, mu2)) { + case carve::RR_INTERSECTION: { + // edges intersect + if (mu1 >= 0.0 && mu1 <= 1.0 && mu2 >= 0.0 && mu2 <= 1.0) { + carve::mesh::MeshSet<3>::vertex_t *p = vertex_pool.get((p1 + p2) / 2.0); + intersections.record(ea, eb, p); + if (ea->rev) intersections.record(ea->rev, eb, p); + if (eb->rev) intersections.record(ea, eb->rev, p); + if (ea->rev && eb->rev) intersections.record(ea->rev, eb->rev, p); + } + break; + } + case carve::RR_PARALLEL: { + // edges parallel. any intersection of this type should have + // been handled by generateVertexEdgeIntersections(). + break; + } + case carve::RR_DEGENERATE: { + throw carve::exception("degenerate edge"); + break; + } + case carve::RR_NO_INTERSECTION: { + break; + } + } +} + + + +void carve::csg::CSG::generateEdgeEdgeIntersections(carve::mesh::MeshSet<3>::face_t *a, + const std::vector<carve::mesh::MeshSet<3>::face_t *> &b) { + carve::mesh::MeshSet<3>::edge_t *ea, *eb; + + ea = a->edge; + do { + for (size_t i = 0; i < b.size(); ++i) { + carve::mesh::MeshSet<3>::face_t *t = b[i]; + eb = t->edge; + do { + _generateEdgeEdgeIntersections(ea, eb); + eb = eb->next; + } while (eb != t->edge); + } + ea = ea->next; + } while (ea != a->edge); +} + + + +void carve::csg::CSG::_generateVertexFaceIntersections(carve::mesh::MeshSet<3>::face_t *fa, + carve::mesh::MeshSet<3>::edge_t *eb) { + if (intersections.intersects(eb->v1(), fa)) { + return; + } + + double d1 = carve::geom::distance(fa->plane, eb->v1()->v); + + if (fabs(d1) < carve::EPSILON && + fa->containsPoint(eb->v1()->v)) { + intersections.record(eb->v1(), fa, eb->v1()); + } +} + + + +void carve::csg::CSG::generateVertexFaceIntersections(carve::mesh::MeshSet<3>::face_t *a, + const std::vector<carve::mesh::MeshSet<3>::face_t *> &b) { + carve::mesh::MeshSet<3>::edge_t *ea, *eb; + + for (size_t i = 0; i < b.size(); ++i) { + carve::mesh::MeshSet<3>::face_t *t = b[i]; + eb = t->edge; + do { + _generateVertexFaceIntersections(a, eb); + eb = eb->next; + } while (eb != t->edge); + } +} + + + +void carve::csg::CSG::_generateEdgeFaceIntersections(carve::mesh::MeshSet<3>::face_t *fa, + carve::mesh::MeshSet<3>::edge_t *eb) { + if (intersections.intersects(eb, fa)) { + return; + } + + carve::mesh::MeshSet<3>::vertex_t::vector_t _p; + if (fa->simpleLineSegmentIntersection(carve::geom3d::LineSegment(eb->v1()->v, eb->v2()->v), _p)) { + carve::mesh::MeshSet<3>::vertex_t *p = vertex_pool.get(_p); + intersections.record(eb, fa, p); + if (eb->rev) intersections.record(eb->rev, fa, p); + } +} + + + +void carve::csg::CSG::generateEdgeFaceIntersections(carve::mesh::MeshSet<3>::face_t *a, + const std::vector<carve::mesh::MeshSet<3>::face_t *> &b) { + carve::mesh::MeshSet<3>::edge_t *ea, *eb; + + for (size_t i = 0; i < b.size(); ++i) { + carve::mesh::MeshSet<3>::face_t *t = b[i]; + eb = t->edge; + do { + _generateEdgeFaceIntersections(a, eb); + eb = eb->next; + } while (eb != t->edge); + } +} + + + +void carve::csg::CSG::generateIntersectionCandidates(carve::mesh::MeshSet<3> *a, + const face_rtree_t *a_node, + carve::mesh::MeshSet<3> *b, + const face_rtree_t *b_node, + face_pairs_t &face_pairs, + bool descend_a) { + if (!a_node->bbox.intersects(b_node->bbox)) { + return; + } + + if (a_node->child && (descend_a || !b_node->child)) { + for (face_rtree_t *node = a_node->child; node; node = node->sibling) { + generateIntersectionCandidates(a, node, b, b_node, face_pairs, false); + } + } else if (b_node->child) { + for (face_rtree_t *node = b_node->child; node; node = node->sibling) { + generateIntersectionCandidates(a, a_node, b, node, face_pairs, true); + } + } else { + for (size_t i = 0; i < a_node->data.size(); ++i) { + carve::mesh::MeshSet<3>::face_t *fa = a_node->data[i]; + carve::geom::aabb<3> aabb_a = fa->getAABB(); + if (aabb_a.maxAxisSeparation(b_node->bbox) > carve::EPSILON) continue; + + for (size_t j = 0; j < b_node->data.size(); ++j) { + carve::mesh::MeshSet<3>::face_t *fb = b_node->data[j]; + carve::geom::aabb<3> aabb_b = fb->getAABB(); + if (aabb_b.maxAxisSeparation(aabb_a) > carve::EPSILON) continue; + + std::pair<double, double> a_ra = fa->rangeInDirection(fa->plane.N, fa->edge->vert->v); + std::pair<double, double> b_ra = fb->rangeInDirection(fa->plane.N, fa->edge->vert->v); + if (carve::rangeSeparation(a_ra, b_ra) > carve::EPSILON) continue; + + std::pair<double, double> a_rb = fa->rangeInDirection(fb->plane.N, fb->edge->vert->v); + std::pair<double, double> b_rb = fb->rangeInDirection(fb->plane.N, fb->edge->vert->v); + if (carve::rangeSeparation(a_rb, b_rb) > carve::EPSILON) continue; + + if (!facesAreCoplanar(fa, fb)) { + face_pairs[fa].push_back(fb); + face_pairs[fb].push_back(fa); + } + } + } + } +} + + + + +void carve::csg::CSG::generateIntersections(carve::mesh::MeshSet<3> *a, + const face_rtree_t *a_rtree, + carve::mesh::MeshSet<3> *b, + const face_rtree_t *b_rtree, + detail::Data &data) { + face_pairs_t face_pairs; + generateIntersectionCandidates(a, a_rtree, b, b_rtree, face_pairs); + + for (face_pairs_t::const_iterator i = face_pairs.begin(); i != face_pairs.end(); ++i) { + carve::mesh::MeshSet<3>::face_t *f = (*i).first; + carve::mesh::MeshSet<3>::edge_t *e = f->edge; + do { + data.vert_to_edges[e->v1()].push_back(e); + e = e->next; + } while (e != f->edge); + } + + for (face_pairs_t::const_iterator i = face_pairs.begin(); i != face_pairs.end(); ++i) { + generateVertexVertexIntersections((*i).first, (*i).second); + } + + for (face_pairs_t::const_iterator i = face_pairs.begin(); i != face_pairs.end(); ++i) { + generateVertexEdgeIntersections((*i).first, (*i).second); + } + + for (face_pairs_t::const_iterator i = face_pairs.begin(); i != face_pairs.end(); ++i) { + generateEdgeEdgeIntersections((*i).first, (*i).second); + } + + for (face_pairs_t::const_iterator i = face_pairs.begin(); i != face_pairs.end(); ++i) { + generateVertexFaceIntersections((*i).first, (*i).second); + } + + for (face_pairs_t::const_iterator i = face_pairs.begin(); i != face_pairs.end(); ++i) { + generateEdgeFaceIntersections((*i).first, (*i).second); + } + + +#if defined(CARVE_DEBUG) + std::cerr << "makeVertexIntersections" << std::endl; +#endif + makeVertexIntersections(); + +#if defined(CARVE_DEBUG) + std::cerr << " intersections.size() " << intersections.size() << std::endl; + map_histogram(std::cerr, intersections); + std::cerr << " vertex_intersections.size() " << vertex_intersections.size() << std::endl; + map_histogram(std::cerr, vertex_intersections); +#endif + +#if defined(CARVE_DEBUG) && defined(DEBUG_DRAW_INTERSECTIONS) + HOOK(drawIntersections(vertex_intersections);); +#endif + +#if defined(CARVE_DEBUG) + std::cerr << " intersections.size() " << intersections.size() << std::endl; + std::cerr << " vertex_intersections.size() " << vertex_intersections.size() << std::endl; +#endif + + // notify about intersections. + if (hooks.hasHook(Hooks::INTERSECTION_VERTEX_HOOK)) { + for (VertexIntersections::const_iterator i = vertex_intersections.begin(); + i != vertex_intersections.end(); + ++i) { + hooks.intersectionVertex((*i).first, (*i).second); + } + } + + // from here on, only vertex_intersections is used for intersection + // information. + + // intersections still contains the vertex_to_face map. maybe that + // should be moved out into another class. + static_cast<Intersections::super>(intersections).clear(); +} + + + +carve::csg::CSG::CSG() { +} + + + +/** + * \brief For each intersected edge, decompose into a set of vertex pairs representing an ordered set of edge fragments. + * + * @tparam[in,out] data Internal intersection data. data.emap is used to produce data.divided_edges. + */ +void carve::csg::CSG::divideIntersectedEdges(detail::Data &data) { + static carve::TimingName FUNC_NAME("CSG::divideIntersectedEdges()"); + carve::TimingBlock block(FUNC_NAME); + + for (detail::EVSMap::const_iterator i = data.emap.begin(), ei = data.emap.end(); i != ei; ++i) { + carve::mesh::MeshSet<3>::edge_t *edge = (*i).first; + const detail::EVSMap::mapped_type &vertices = (*i).second; + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &verts = data.divided_edges[edge]; + orderVertices(vertices.begin(), vertices.end(), + edge->v2()->v - edge->v1()->v, edge->v1()->v, + verts); + } +} + + + +carve::csg::CSG::~CSG() { +} + + + +void carve::csg::CSG::makeFaceEdges(carve::csg::EdgeClassification &eclass, + detail::Data &data) { + detail::FSet face_b_set; + for (detail::FVSMap::const_iterator + i = data.fmap.begin(), ie = data.fmap.end(); + i != ie; + ++i) { + carve::mesh::MeshSet<3>::face_t *face_a = (*i).first; + const detail::FVSMap::mapped_type &face_a_intersections = ((*i).second); + face_b_set.clear(); + + // work out the set of faces from the opposing polyhedron that intersect face_a. + for (detail::FVSMap::mapped_type::const_iterator + j = face_a_intersections.begin(), je = face_a_intersections.end(); + j != je; + ++j) { + for (detail::VFSMap::mapped_type::const_iterator + k = data.fmap_rev[*j].begin(), ke = data.fmap_rev[*j].end(); + k != ke; + ++k) { + carve::mesh::MeshSet<3>::face_t *face_b = (*k); + if (face_a != face_b && face_b->mesh->meshset != face_a->mesh->meshset) { + face_b_set.insert(face_b); + } + } + } + + // run through each intersecting face. + for (detail::FSet::const_iterator + j = face_b_set.begin(), je = face_b_set.end(); + j != je; + ++j) { + carve::mesh::MeshSet<3>::face_t *face_b = (*j); + const detail::FVSMap::mapped_type &face_b_intersections = (data.fmap[face_b]); + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> vertices; + vertices.reserve(std::min(face_a_intersections.size(), face_b_intersections.size())); + + // record the points of intersection between face_a and face_b + std::set_intersection(face_a_intersections.begin(), + face_a_intersections.end(), + face_b_intersections.begin(), + face_b_intersections.end(), + std::back_inserter(vertices)); + +#if defined(CARVE_DEBUG) + std::cerr << "face pair: " + << face_a << ":" << face_b + << " N(verts) " << vertices.size() << std::endl; + for (std::vector<carve::mesh::MeshSet<3>::vertex_t *>::const_iterator i = vertices.begin(), e = vertices.end(); i != e; ++i) { + std::cerr << (*i) << " " << (*i)->v << " (" + << carve::geom::distance(face_a->plane, (*i)->v) << "," + << carve::geom::distance(face_b->plane, (*i)->v) << ")" + << std::endl; + //CARVE_ASSERT(carve::geom3d::distance(face_a->plane_eqn, *(*i)) < EPSILON); + //CARVE_ASSERT(carve::geom3d::distance(face_b->plane_eqn, *(*i)) < EPSILON); + } +#endif + + // if there are two points of intersection, then the added edge is simple to determine. + if (vertices.size() == 2) { + carve::mesh::MeshSet<3>::vertex_t *v1 = vertices[0]; + carve::mesh::MeshSet<3>::vertex_t *v2 = vertices[1]; + carve::geom3d::Vector c = (v1->v + v2->v) / 2; + + // determine whether the midpoint of the implied edge is contained in face_a and face_b + +#if defined(CARVE_DEBUG) + std::cerr << "face_a->nVertices() = " << face_a->nVertices() << " face_a->containsPointInProjection(c) = " << face_a->containsPointInProjection(c) << std::endl; + std::cerr << "face_b->nVertices() = " << face_b->nVertices() << " face_b->containsPointInProjection(c) = " << face_b->containsPointInProjection(c) << std::endl; +#endif + + if (face_a->containsPointInProjection(c) && face_b->containsPointInProjection(c)) { +#if defined(CARVE_DEBUG) + std::cerr << "adding edge: " << v1 << "-" << v2 << std::endl; +#if defined(DEBUG_DRAW_FACE_EDGES) + HOOK(drawEdge(v1, v2, 1, 1, 1, 1, 1, 1, 1, 1, 2.0);); +#endif +#endif + // record the edge, with class information. + if (v1 > v2) std::swap(v1, v2); + eclass[ordered_edge(v1, v2)] = carve::csg::EC2(carve::csg::EDGE_ON, carve::csg::EDGE_ON); + data.face_split_edges[face_a].insert(std::make_pair(v1, v2)); + data.face_split_edges[face_b].insert(std::make_pair(v1, v2)); + } + continue; + } + + // otherwise, it's more complex. + carve::geom3d::Vector base, dir; + std::vector<carve::mesh::MeshSet<3>::vertex_t *> ordered; + + // skip coplanar edges. this simplifies the resulting + // mesh. eventually all coplanar face regions of two polyhedra + // must reach a point where they are no longer coplanar (or the + // polyhedra are identical). + if (!facesAreCoplanar(face_a, face_b)) { + // order the intersection vertices (they must lie along a + // vector, as the faces aren't coplanar). + selectOrderingProjection(vertices.begin(), vertices.end(), dir, base); + orderVertices(vertices.begin(), vertices.end(), dir, base, ordered); + + // for each possible edge in the ordering, test the midpoint, + // and record if it's contained in face_a and face_b. + for (int k = 0, ke = (int)ordered.size() - 1; k < ke; ++k) { + carve::mesh::MeshSet<3>::vertex_t *v1 = ordered[k]; + carve::mesh::MeshSet<3>::vertex_t *v2 = ordered[k + 1]; + carve::geom3d::Vector c = (v1->v + v2->v) / 2; + +#if defined(CARVE_DEBUG) + std::cerr << "testing edge: " << v1 << "-" << v2 << " at " << c << std::endl; + std::cerr << "a: " << face_a->containsPointInProjection(c) << " b: " << face_b->containsPointInProjection(c) << std::endl; + std::cerr << "face_a->containsPointInProjection(c): " << face_a->containsPointInProjection(c) << std::endl; + std::cerr << "face_b->containsPointInProjection(c): " << face_b->containsPointInProjection(c) << std::endl; +#endif + + if (face_a->containsPointInProjection(c) && face_b->containsPointInProjection(c)) { +#if defined(CARVE_DEBUG) + std::cerr << "adding edge: " << v1 << "-" << v2 << std::endl; +#if defined(DEBUG_DRAW_FACE_EDGES) + HOOK(drawEdge(v1, v2, .5, .5, .5, 1, .5, .5, .5, 1, 2.0);); +#endif +#endif + // record the edge, with class information. + if (v1 > v2) std::swap(v1, v2); + eclass[ordered_edge(v1, v2)] = carve::csg::EC2(carve::csg::EDGE_ON, carve::csg::EDGE_ON); + data.face_split_edges[face_a].insert(std::make_pair(v1, v2)); + data.face_split_edges[face_b].insert(std::make_pair(v1, v2)); + } + } + } + } + } + + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + { + V2Set edges; + for (detail::FV2SMap::const_iterator i = data.face_split_edges.begin(); i != data.face_split_edges.end(); ++i) { + edges.insert((*i).second.begin(), (*i).second.end()); + } + + detail::VSet vertices; + for (V2Set::const_iterator i = edges.begin(); i != edges.end(); ++i) { + vertices.insert((*i).first); + vertices.insert((*i).second); + } + + carve::line::PolylineSet intersection_graph; + intersection_graph.vertices.resize(vertices.size()); + std::map<const carve::mesh::MeshSet<3>::vertex_t *, size_t> vmap; + + size_t j = 0; + for (detail::VSet::const_iterator i = vertices.begin(); i != vertices.end(); ++i) { + intersection_graph.vertices[j].v = (*i)->v; + vmap[(*i)] = j++; + } + + for (V2Set::const_iterator i = edges.begin(); i != edges.end(); ++i) { + size_t line[2]; + line[0] = vmap[(*i).first]; + line[1] = vmap[(*i).second]; + intersection_graph.addPolyline(false, line, line + 2); + } + + std::string out("/tmp/intersection-edges.ply"); + ::writePLY(out, &intersection_graph, true); + } +#endif +} + + + +/** + * + * + * @param fll + */ +static void checkFaceLoopIntegrity(carve::csg::FaceLoopList &fll) { + static carve::TimingName FUNC_NAME("CSG::checkFaceLoopIntegrity()"); + carve::TimingBlock block(FUNC_NAME); + + std::unordered_map<carve::csg::V2, int> counts; + for (carve::csg::FaceLoop *fl = fll.head; fl; fl = fl->next) { + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &loop = (fl->vertices); + carve::mesh::MeshSet<3>::vertex_t *v1, *v2; + v1 = loop[loop.size() - 1]; + for (unsigned i = 0; i < loop.size(); ++i) { + v2 = loop[i]; + if (v1 < v2) { + counts[std::make_pair(v1, v2)]++; + } else { + counts[std::make_pair(v2, v1)]--; + } + v1 = v2; + } + } + for (std::unordered_map<carve::csg::V2, int>::const_iterator + x = counts.begin(), xe = counts.end(); x != xe; ++x) { + if ((*x).second) { + std::cerr << "FACE LOOP ERROR: " << (*x).first.first << "-" << (*x).first.second << " : " << (*x).second << std::endl; + } + } +} + + + +/** + * + * + * @param a + * @param b + * @param vclass + * @param eclass + * @param a_face_loops + * @param b_face_loops + * @param a_edge_count + * @param b_edge_count + * @param hooks + */ +void carve::csg::CSG::calc(carve::mesh::MeshSet<3> *a, + const face_rtree_t *a_rtree, + carve::mesh::MeshSet<3> *b, + const face_rtree_t *b_rtree, + carve::csg::VertexClassification &vclass, + carve::csg::EdgeClassification &eclass, + carve::csg::FaceLoopList &a_face_loops, + carve::csg::FaceLoopList &b_face_loops, + size_t &a_edge_count, + size_t &b_edge_count) { + detail::Data data; + +#if defined(CARVE_DEBUG) + std::cerr << "init" << std::endl; +#endif + init(); + + generateIntersections(a, a_rtree, b, b_rtree, data); + +#if defined(CARVE_DEBUG) + std::cerr << "intersectingFacePairs" << std::endl; +#endif + intersectingFacePairs(data); + +#if defined(CARVE_DEBUG) + std::cerr << "emap:" << std::endl; + map_histogram(std::cerr, data.emap); + std::cerr << "fmap:" << std::endl; + map_histogram(std::cerr, data.fmap); + std::cerr << "fmap_rev:" << std::endl; + map_histogram(std::cerr, data.fmap_rev); +#endif + + // std::cerr << "removeCoplanarFaces" << std::endl; + // fp_intersections.removeCoplanarFaces(); + +#if defined(CARVE_DEBUG) && defined(DEBUG_DRAW_OCTREE) + HOOK(drawOctree(a->octree);); + HOOK(drawOctree(b->octree);); +#endif + +#if defined(CARVE_DEBUG) + std::cerr << "divideIntersectedEdges" << std::endl; +#endif + divideIntersectedEdges(data); + +#if defined(CARVE_DEBUG) + std::cerr << "makeFaceEdges" << std::endl; +#endif + // makeFaceEdges(data.face_split_edges, eclass, data.fmap, data.fmap_rev); + makeFaceEdges(eclass, data); + +#if defined(CARVE_DEBUG) + std::cerr << "generateFaceLoops" << std::endl; +#endif + a_edge_count = generateFaceLoops(a, data, a_face_loops); + b_edge_count = generateFaceLoops(b, data, b_face_loops); + +#if defined(CARVE_DEBUG) + std::cerr << "generated " << a_edge_count << " edges for poly a" << std::endl; + std::cerr << "generated " << b_edge_count << " edges for poly b" << std::endl; +#endif + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + { + std::string out("/tmp/a_split.ply"); + writePLY(out, faceLoopsToPolyhedron(a_face_loops), false); + } + { + std::string out("/tmp/b_split.ply"); + writePLY(out, faceLoopsToPolyhedron(b_face_loops), false); + } +#endif + + checkFaceLoopIntegrity(a_face_loops); + checkFaceLoopIntegrity(b_face_loops); + +#if defined(CARVE_DEBUG) + std::cerr << "classify" << std::endl; +#endif + // initialize some classification information. + for (std::vector<carve::mesh::MeshSet<3>::vertex_t>::iterator + i = a->vertex_storage.begin(), e = a->vertex_storage.end(); i != e; ++i) { + vclass[map_vertex(data.vmap, &(*i))].cls[0] = POINT_ON; + } + for (std::vector<carve::mesh::MeshSet<3>::vertex_t>::iterator + i = b->vertex_storage.begin(), e = b->vertex_storage.end(); i != e; ++i) { + vclass[map_vertex(data.vmap, &(*i))].cls[1] = POINT_ON; + } + for (VertexIntersections::const_iterator + i = vertex_intersections.begin(), e = vertex_intersections.end(); i != e; ++i) { + vclass[(*i).first] = PC2(POINT_ON, POINT_ON); + } + +#if defined(CARVE_DEBUG) + std::cerr << data.divided_edges.size() << " edges are split" << std::endl; + std::cerr << data.face_split_edges.size() << " faces are split" << std::endl; + + std::cerr << "poly a: " << a_face_loops.size() << " face loops" << std::endl; + std::cerr << "poly b: " << b_face_loops.size() << " face loops" << std::endl; +#endif + + // std::cerr << "OCTREE A:" << std::endl; + // dump_octree_stats(a->octree.root, 0); + // std::cerr << "OCTREE B:" << std::endl; + // dump_octree_stats(b->octree.root, 0); +} + + + +/** + * + * + * @param shared_edges + * @param result_list + * @param shared_edge_ptr + */ +void returnSharedEdges(carve::csg::V2Set &shared_edges, + std::list<carve::mesh::MeshSet<3> *> &result_list, + carve::csg::V2Set *shared_edge_ptr) { + // need to convert shared edges to point into result + typedef std::map<carve::geom3d::Vector, carve::mesh::MeshSet<3>::vertex_t *> remap_type; + remap_type remap; + for (std::list<carve::mesh::MeshSet<3> *>::iterator list_it = + result_list.begin(); list_it != result_list.end(); list_it++) { + carve::mesh::MeshSet<3> *result = *list_it; + if (result) { + for (std::vector<carve::mesh::MeshSet<3>::vertex_t>::iterator it = + result->vertex_storage.begin(); it != result->vertex_storage.end(); it++) { + remap.insert(std::make_pair((*it).v, &(*it))); + } + } + } + for (carve::csg::V2Set::iterator it = shared_edges.begin(); + it != shared_edges.end(); it++) { + remap_type::iterator first_it = remap.find(((*it).first)->v); + remap_type::iterator second_it = remap.find(((*it).second)->v); + CARVE_ASSERT(first_it != remap.end() && second_it != remap.end()); + shared_edge_ptr->insert(std::make_pair(first_it->second, second_it->second)); + } +} + + + +/** + * + * + * @param a + * @param b + * @param collector + * @param hooks + * @param shared_edges_ptr + * @param classify_type + * + * @return + */ +carve::mesh::MeshSet<3> *carve::csg::CSG::compute(carve::mesh::MeshSet<3> *a, + carve::mesh::MeshSet<3> *b, + carve::csg::CSG::Collector &collector, + carve::csg::V2Set *shared_edges_ptr, + CLASSIFY_TYPE classify_type) { + static carve::TimingName FUNC_NAME("CSG::compute"); + carve::TimingBlock block(FUNC_NAME); + + VertexClassification vclass; + EdgeClassification eclass; + + FLGroupList a_loops_grouped; + FLGroupList b_loops_grouped; + + FaceLoopList a_face_loops; + FaceLoopList b_face_loops; + + size_t a_edge_count; + size_t b_edge_count; + + face_rtree_t *a_rtree = face_rtree_t::construct_STR(a->faceBegin(), a->faceEnd(), 4, 4); + face_rtree_t *b_rtree = face_rtree_t::construct_STR(b->faceBegin(), b->faceEnd(), 4, 4); + + { + static carve::TimingName FUNC_NAME("CSG::compute - calc()"); + carve::TimingBlock block(FUNC_NAME); + calc(a, a_rtree, b, b_rtree, vclass, eclass,a_face_loops, b_face_loops, a_edge_count, b_edge_count); + } + + detail::LoopEdges a_edge_map; + detail::LoopEdges b_edge_map; + + { + static carve::TimingName FUNC_NAME("CSG::compute - makeEdgeMap()"); + carve::TimingBlock block(FUNC_NAME); + makeEdgeMap(a_face_loops, a_edge_count, a_edge_map); + makeEdgeMap(b_face_loops, b_edge_count, b_edge_map); + + } + + { + static carve::TimingName FUNC_NAME("CSG::compute - sortFaceLoopLists()"); + carve::TimingBlock block(FUNC_NAME); + a_edge_map.sortFaceLoopLists(); + b_edge_map.sortFaceLoopLists(); + } + + V2Set shared_edges; + + { + static carve::TimingName FUNC_NAME("CSG::compute - findSharedEdges()"); + carve::TimingBlock block(FUNC_NAME); + findSharedEdges(a_edge_map, b_edge_map, shared_edges); + } + + { + static carve::TimingName FUNC_NAME("CSG::compute - groupFaceLoops()"); + carve::TimingBlock block(FUNC_NAME); + groupFaceLoops(a, a_face_loops, a_edge_map, shared_edges, a_loops_grouped); + groupFaceLoops(b, b_face_loops, b_edge_map, shared_edges, b_loops_grouped); +#if defined(CARVE_DEBUG) + std::cerr << "*** a_loops_grouped.size(): " << a_loops_grouped.size() << std::endl; + std::cerr << "*** b_loops_grouped.size(): " << b_loops_grouped.size() << std::endl; +#endif + } + +#if defined(CARVE_DEBUG) && defined(DEBUG_DRAW_GROUPS) + { + float n = 1.0 / (a_loops_grouped.size() + b_loops_grouped.size() + 1); + float H = 0.0, S = 1.0, V = 1.0; + float r, g, b; + for (FLGroupList::const_iterator i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) { + carve::colour::HSV2RGB(H, S, V, r, g, b); H += n; + drawFaceLoopList((*i).face_loops, r, g, b, 1.0, r * .5, g * .5, b * .5, 1.0, true); + } + for (FLGroupList::const_iterator i = b_loops_grouped.begin(); i != b_loops_grouped.end(); ++i) { + carve::colour::HSV2RGB(H, S, V, r, g, b); H += n; + drawFaceLoopList((*i).face_loops, r, g, b, 1.0, r * .5, g * .5, b * .5, 1.0, true); + } + + for (FLGroupList::const_iterator i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) { + drawFaceLoopListWireframe((*i).face_loops); + } + for (FLGroupList::const_iterator i = b_loops_grouped.begin(); i != b_loops_grouped.end(); ++i) { + drawFaceLoopListWireframe((*i).face_loops); + } + } +#endif + + switch (classify_type) { + case CLASSIFY_EDGE: + classifyFaceGroupsEdge(shared_edges, + vclass, + a, + a_rtree, + a_loops_grouped, + a_edge_map, + b, + b_rtree, + b_loops_grouped, + b_edge_map, + collector); + break; + case CLASSIFY_NORMAL: + classifyFaceGroups(shared_edges, + vclass, + a, + a_rtree, + a_loops_grouped, + a_edge_map, + b, + b_rtree, + b_loops_grouped, + b_edge_map, + collector); + break; + } + + carve::mesh::MeshSet<3> *result = collector.done(hooks); + if (result != NULL && shared_edges_ptr != NULL) { + std::list<carve::mesh::MeshSet<3> *> result_list; + result_list.push_back(result); + returnSharedEdges(shared_edges, result_list, shared_edges_ptr); + } + return result; +} + + + +/** + * + * + * @param a + * @param b + * @param op + * @param hooks + * @param shared_edges + * @param classify_type + * + * @return + */ +carve::mesh::MeshSet<3> *carve::csg::CSG::compute(carve::mesh::MeshSet<3> *a, + carve::mesh::MeshSet<3> *b, + carve::csg::CSG::OP op, + carve::csg::V2Set *shared_edges, + CLASSIFY_TYPE classify_type) { + Collector *coll = makeCollector(op, a, b); + if (!coll) return NULL; + + carve::mesh::MeshSet<3> *result = compute(a, b, *coll, shared_edges, classify_type); + + delete coll; + + return result; +} + + + +/** + * + * + * @param closed + * @param open + * @param FaceClass + * @param result + * @param hooks + * @param shared_edges_ptr + * + * @return + */ +bool carve::csg::CSG::sliceAndClassify(carve::mesh::MeshSet<3> *closed, + carve::mesh::MeshSet<3> *open, + std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> > &result, + carve::csg::V2Set *shared_edges_ptr) { + if (!closed->isClosed()) return false; + carve::csg::VertexClassification vclass; + carve::csg::EdgeClassification eclass; + + carve::csg::FLGroupList a_loops_grouped; + carve::csg::FLGroupList b_loops_grouped; + + carve::csg::FaceLoopList a_face_loops; + carve::csg::FaceLoopList b_face_loops; + + size_t a_edge_count; + size_t b_edge_count; + + face_rtree_t *closed_rtree = face_rtree_t::construct_STR(closed->faceBegin(), closed->faceEnd(), 4, 4); + face_rtree_t *open_rtree = face_rtree_t::construct_STR(open->faceBegin(), open->faceEnd(), 4, 4); + + calc(closed, closed_rtree, open, open_rtree, vclass, eclass,a_face_loops, b_face_loops, a_edge_count, b_edge_count); + + detail::LoopEdges a_edge_map; + detail::LoopEdges b_edge_map; + + makeEdgeMap(a_face_loops, a_edge_count, a_edge_map); + makeEdgeMap(b_face_loops, b_edge_count, b_edge_map); + + carve::csg::V2Set shared_edges; + + findSharedEdges(a_edge_map, b_edge_map, shared_edges); + + groupFaceLoops(closed, a_face_loops, a_edge_map, shared_edges, a_loops_grouped); + groupFaceLoops(open, b_face_loops, b_edge_map, shared_edges, b_loops_grouped); + + halfClassifyFaceGroups(shared_edges, + vclass, + closed, + closed_rtree, + a_loops_grouped, + a_edge_map, + open, + open_rtree, + b_loops_grouped, + b_edge_map, + result); + + if (shared_edges_ptr != NULL) { + std::list<carve::mesh::MeshSet<3> *> result_list; + for (std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> >::iterator it = result.begin(); it != result.end(); it++) { + result_list.push_back(it->second); + } + returnSharedEdges(shared_edges, result_list, shared_edges_ptr); + } + return true; +} + + + +/** + * + * + * @param a + * @param b + * @param a_sliced + * @param b_sliced + * @param hooks + * @param shared_edges_ptr + */ +void carve::csg::CSG::slice(carve::mesh::MeshSet<3> *a, + carve::mesh::MeshSet<3> *b, + std::list<carve::mesh::MeshSet<3> *> &a_sliced, + std::list<carve::mesh::MeshSet<3> *> &b_sliced, + carve::csg::V2Set *shared_edges_ptr) { + carve::csg::VertexClassification vclass; + carve::csg::EdgeClassification eclass; + + carve::csg::FLGroupList a_loops_grouped; + carve::csg::FLGroupList b_loops_grouped; + + carve::csg::FaceLoopList a_face_loops; + carve::csg::FaceLoopList b_face_loops; + + size_t a_edge_count; + size_t b_edge_count; + + face_rtree_t *a_rtree = face_rtree_t::construct_STR(a->faceBegin(), a->faceEnd(), 4, 4); + face_rtree_t *b_rtree = face_rtree_t::construct_STR(b->faceBegin(), b->faceEnd(), 4, 4); + + calc(a, a_rtree, b, b_rtree, vclass, eclass,a_face_loops, b_face_loops, a_edge_count, b_edge_count); + + detail::LoopEdges a_edge_map; + detail::LoopEdges b_edge_map; + + makeEdgeMap(a_face_loops, a_edge_count, a_edge_map); + makeEdgeMap(b_face_loops, b_edge_count, b_edge_map); + + carve::csg::V2Set shared_edges; + + findSharedEdges(a_edge_map, b_edge_map, shared_edges); + + groupFaceLoops(a, a_face_loops, a_edge_map, shared_edges, a_loops_grouped); + groupFaceLoops(b, b_face_loops, b_edge_map, shared_edges, b_loops_grouped); + + for (carve::csg::FLGroupList::iterator + i = a_loops_grouped.begin(), e = a_loops_grouped.end(); + i != e; ++i) { + Collector *all = makeCollector(ALL, a, b); + all->collect(&*i, hooks); + a_sliced.push_back(all->done(hooks)); + + delete all; + } + + for (carve::csg::FLGroupList::iterator + i = b_loops_grouped.begin(), e = b_loops_grouped.end(); + i != e; ++i) { + Collector *all = makeCollector(ALL, a, b); + all->collect(&*i, hooks); + b_sliced.push_back(all->done(hooks)); + + delete all; + } + if (shared_edges_ptr != NULL) { + std::list<carve::mesh::MeshSet<3> *> result_list; + result_list.insert(result_list.end(), a_sliced.begin(), a_sliced.end()); + result_list.insert(result_list.end(), b_sliced.begin(), b_sliced.end()); + returnSharedEdges(shared_edges, result_list, shared_edges_ptr); + } +} + + + +/** + * + * + */ +void carve::csg::CSG::init() { + intersections.clear(); + vertex_intersections.clear(); + vertex_pool.reset(); +} diff --git a/extern/carve/lib/intersect_classify_common.hpp b/extern/carve/lib/intersect_classify_common.hpp new file mode 100644 index 00000000000..359c9af0e64 --- /dev/null +++ b/extern/carve/lib/intersect_classify_common.hpp @@ -0,0 +1,46 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +#include "intersect_common.hpp" + +template<typename T> +static int is_same(const std::vector<T> &a, + const std::vector<T> &b) { + if (a.size() != b.size()) return false; + + const size_t S = a.size(); + size_t i, j, p; + + for (p = 0; p < S; ++p) { + if (a[0] == b[p]) break; + } + if (p == S) return 0; + + for (i = 1, j = p + 1; j < S; ++i, ++j) if (a[i] != b[j]) goto not_fwd; + for ( j = 0; i < S; ++i, ++j) if (a[i] != b[j]) goto not_fwd; + return +1; + +not_fwd: + for (i = 1, j = p - 1; j != (size_t)-1; ++i, --j) if (a[i] != b[j]) goto not_rev; + for ( j = S - 1; i < S; ++i, --j) if (a[i] != b[j]) goto not_rev; + return -1; + +not_rev: + return 0; +} diff --git a/extern/carve/lib/intersect_classify_common_impl.hpp b/extern/carve/lib/intersect_classify_common_impl.hpp new file mode 100644 index 00000000000..3c141c81151 --- /dev/null +++ b/extern/carve/lib/intersect_classify_common_impl.hpp @@ -0,0 +1,362 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + +namespace carve { + namespace csg { + typedef std::unordered_map< + carve::mesh::MeshSet<3>::vertex_t *, + std::list<FLGroupList::iterator> > GroupLookup; + + + inline bool isSameFwd(const V2Set &a, const V2Set &b) { + if (a.size() != b.size()) return false; + for (V2Set::const_iterator i = a.begin(), e = a.end(); i != e; ++i) { + if (b.find((*i)) == b.end()) return false; + } + return true; + } + + inline bool isSameRev(const V2Set &a, const V2Set &b) { + if (a.size() != b.size()) return false; + for (V2Set::const_iterator i = a.begin(), e = a.end(); i != e; ++i) { + if (b.find(std::make_pair((*i).second, (*i).first)) == b.end()) return false; + } + return true; + } + + + static void performClassifySimpleOnFaceGroups(FLGroupList &a_groups, + FLGroupList &b_groups, + carve::mesh::MeshSet<3> *poly_a, + carve::mesh::MeshSet<3> *poly_b, + CSG::Collector &collector, + CSG::Hooks &hooks) { + // Simple ON faces groups are face groups that consist of a single + // face, and which have copy in both inputs. These are trivially ON. + // This has the side effect of short circuiting the case where the + // two inputs share geometry. + GroupLookup a_map, b_map; + + // First, hash FaceLoopGroups with one FaceLoop based upon their + // minimum vertex pointer - this pointer must be shared between + // FaceLoops that this test catches. + for (FLGroupList::iterator i = a_groups.begin(); i != a_groups.end(); ++i) { + if ((*i).face_loops.size() != 1) continue; + FaceLoop *f = (*i).face_loops.head; + carve::mesh::MeshSet<3>::vertex_t *v = *std::min_element(f->vertices.begin(), f->vertices.end()); + a_map[v].push_back(i); + } + + for (FLGroupList::iterator i = b_groups.begin(); i != b_groups.end(); ++i) { + if ((*i).face_loops.size() != 1) continue; + FaceLoop *f = (*i).face_loops.head; + carve::mesh::MeshSet<3>::vertex_t *v = *std::min_element(f->vertices.begin(), f->vertices.end()); + if (a_map.find(v) != a_map.end()) { + b_map[v].push_back(i); + } + } + + // Then, iterate through the FaceLoops hashed in the first map, and + // find candidate matches in the second map. + for (GroupLookup::iterator j = b_map.begin(), je = b_map.end(); j != je; ++j) { + carve::mesh::MeshSet<3>::vertex_t *v = (*j).first; + GroupLookup::iterator i = a_map.find(v); + + for (std::list<FLGroupList::iterator>::iterator bi = (*j).second.begin(), be = (*j).second.end(); bi != be;) { + FLGroupList::iterator b(*bi); + FaceLoop *f_b = (*b).face_loops.head; + + // For each candidate match pair, see if their vertex pointers + // are the same, allowing for rotation and inversion. + for (std::list<FLGroupList::iterator>::iterator ai = (*i).second.begin(), ae = (*i).second.end(); ai != ae; ++ai) { + FLGroupList::iterator a(*ai); + FaceLoop *f_a = (*a).face_loops.head; + + int s = is_same(f_a->vertices, f_b->vertices); + if (!s) continue; + + // if they are ordered in the same direction, then they are + // oriented out, otherwise oriented in. + FaceClass fc = s == +1 ? FACE_ON_ORIENT_OUT : FACE_ON_ORIENT_IN; + + (*a).classification.push_back(ClassificationInfo(NULL, fc)); + (*b).classification.push_back(ClassificationInfo(NULL, fc)); + + collector.collect(&*a, hooks); + collector.collect(&*b, hooks); + + a_groups.erase(a); + b_groups.erase(b); + + (*i).second.erase(ai); + bi = (*j).second.erase(bi); + + goto done; + } + ++bi; + done:; + } + } + } + + template <typename CLASSIFIER> + static void performClassifyEasyFaceGroups(FLGroupList &group, + carve::mesh::MeshSet<3> *poly_a, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree, + VertexClassification &vclass, + const CLASSIFIER &classifier, + CSG::Collector &collector, + CSG::Hooks &hooks) { + + for (FLGroupList::iterator i = group.begin(); i != group.end();) { +#if defined(CARVE_DEBUG) + std::cerr << "............group " << &(*i) << std::endl; +#endif + FaceLoopGroup &grp = (*i); + FaceLoopList &curr = (grp.face_loops); + FaceClass fc; + + for (FaceLoop *f = curr.head; f; f = f->next) { + for (size_t j = 0; j < f->vertices.size(); ++j) { + if (!classifier.pointOn(vclass, f, j)) { + PointClass pc = carve::mesh::classifyPoint(poly_a, poly_a_rtree, f->vertices[j]->v); + if (pc == POINT_IN || pc == POINT_OUT) { + classifier.explain(f, j, pc); + } + if (pc == POINT_IN) { fc = FACE_IN; goto accept; } + if (pc == POINT_OUT) { fc = FACE_OUT; goto accept; } + } + } + } + ++i; + continue; + accept: { + grp.classification.push_back(ClassificationInfo(NULL, fc)); + collector.collect(&grp, hooks); + i = group.erase(i); + } + } + } + + + template <typename CLASSIFIER> + static void performClassifyHardFaceGroups(FLGroupList &group, + carve::mesh::MeshSet<3> *poly_a, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree, + const CLASSIFIER & /* classifier */, + CSG::Collector &collector, + CSG::Hooks &hooks) { + for (FLGroupList::iterator + i = group.begin(); i != group.end();) { + int n_in = 0, n_out = 0, n_on = 0; + FaceLoopGroup &grp = (*i); + FaceLoopList &curr = (grp.face_loops); + V2Set &perim = ((*i).perimeter); + FaceClass fc =FACE_UNCLASSIFIED; + + for (FaceLoop *f = curr.head; f; f = f->next) { + carve::mesh::MeshSet<3>::vertex_t *v1, *v2; + v1 = f->vertices.back(); + for (size_t j = 0; j < f->vertices.size(); ++j) { + v2 = f->vertices[j]; + if (v1 < v2 && perim.find(std::make_pair(v1, v2)) == perim.end()) { + carve::geom3d::Vector c = (v1->v + v2->v) / 2.0; + + PointClass pc = carve::mesh::classifyPoint(poly_a, poly_a_rtree, c); + + switch (pc) { + case POINT_IN: n_in++; break; + case POINT_OUT: n_out++; break; + case POINT_ON: n_on++; break; + default: break; // does not happen. + } + } + v1 = v2; + } + } + +#if defined(CARVE_DEBUG) + std::cerr << ">>> n_in: " << n_in << " n_on: " << n_on << " n_out: " << n_out << std::endl; +#endif + + if (!n_in && !n_out) { + ++i; + continue; + } + + if (n_in) fc = FACE_IN; + if (n_out) fc = FACE_OUT; + + grp.classification.push_back(ClassificationInfo(NULL, fc)); + collector.collect(&grp, hooks); + i = group.erase(i); + } + } + + template <typename CLASSIFIER> + void performFaceLoopWork(carve::mesh::MeshSet<3> *poly_a, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree, + FLGroupList &b_loops_grouped, + const CLASSIFIER &classifier, + CSG::Collector &collector, + CSG::Hooks &hooks) { + for (FLGroupList::iterator i = b_loops_grouped.begin(), e = b_loops_grouped.end(); i != e;) { + FaceClass fc; + + if (classifier.faceLoopSanityChecker(*i)) { + std::cerr << "UNEXPECTED face loop with size != 1." << std::endl; + ++i; + continue; + } + CARVE_ASSERT((*i).face_loops.size() == 1); + + FaceLoop *fla = (*i).face_loops.head; + + const carve::mesh::MeshSet<3>::face_t *f = (fla->orig_face); + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &loop = (fla->vertices); + std::vector<carve::geom2d::P2> proj; + proj.reserve(loop.size()); + for (unsigned j = 0; j < loop.size(); ++j) { + proj.push_back(f->project(loop[j]->v)); + } + carve::geom2d::P2 pv; + if (!carve::geom2d::pickContainedPoint(proj, pv)) { + CARVE_FAIL("Failed"); + } + carve::geom3d::Vector v = f->unproject(pv, f->plane); + + const carve::mesh::MeshSet<3>::face_t *hit_face; + PointClass pc = carve::mesh::classifyPoint(poly_a, poly_a_rtree, v, false, NULL, &hit_face); + switch (pc) { + case POINT_IN: fc = FACE_IN; break; + case POINT_OUT: fc = FACE_OUT; break; + case POINT_ON: { + double d = carve::geom::distance(hit_face->plane, v); +#if defined(CARVE_DEBUG) + std::cerr << "d = " << d << std::endl; +#endif + fc = d < 0 ? FACE_IN : FACE_OUT; + break; + } + default: + CARVE_FAIL("unhandled switch case -- should not happen"); + } +#if defined(CARVE_DEBUG) + std::cerr << "CLASS: " << (fc == FACE_IN ? "FACE_IN" : "FACE_OUT" ) << std::endl; +#endif + + (*i).classification.push_back(ClassificationInfo(NULL, fc)); + collector.collect(&*i, hooks); + i = b_loops_grouped.erase(i); + } + + } + + template <typename CLASSIFIER> + void performClassifyFaceGroups(FLGroupList &a_loops_grouped, + FLGroupList &b_loops_grouped, + VertexClassification &vclass, + carve::mesh::MeshSet<3> *poly_a, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree, + carve::mesh::MeshSet<3> *poly_b, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree, + const CLASSIFIER &classifier, + CSG::Collector &collector, + CSG::Hooks &hooks) { + + classifier.classifySimple(a_loops_grouped, b_loops_grouped, vclass, poly_a, poly_b); + classifier.classifyEasy(a_loops_grouped, b_loops_grouped, vclass, poly_a, poly_a_rtree, poly_b, poly_b_rtree); + classifier.classifyHard(a_loops_grouped, b_loops_grouped, vclass, poly_a, poly_a_rtree, poly_b, poly_b_rtree); + + { + GroupLookup a_map; + FLGroupList::iterator i, j; + FaceClass fc; + + for (i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) { + V2Set::iterator it_end = (*i).perimeter.end(); + V2Set::iterator it_begin = (*i).perimeter.begin(); + + if(it_begin != it_end) { + a_map[std::min_element(it_begin, it_end)->first].push_back(i); + } + } + + for (i = b_loops_grouped.begin(); i != b_loops_grouped.end();) { + GroupLookup::iterator a = a_map.end(); + + V2Set::iterator it_end = (*i).perimeter.end(); + V2Set::iterator it_begin = (*i).perimeter.begin(); + + if(it_begin != it_end) { + a = a_map.find(std::min_element(it_begin, it_end)->first); + } + + if (a == a_map.end()) { ++i; continue; } + + for (std::list<FLGroupList::iterator>::iterator ji = (*a).second.begin(), je = (*a).second.end(); ji != je; ++ji) { + j = (*ji); + if (isSameFwd((*i).perimeter, (*j).perimeter)) { +#if defined(CARVE_DEBUG) + std::cerr << "SAME FWD PAIR" << std::endl; +#endif + fc = FACE_ON_ORIENT_OUT; + goto face_pair; + } else if (isSameRev((*i).perimeter, (*j).perimeter)) { +#if defined(CARVE_DEBUG) + std::cerr << "SAME REV PAIR" << std::endl; +#endif + fc = FACE_ON_ORIENT_IN; + goto face_pair; + } + } + ++i; + continue; + + face_pair: { + V2Set::iterator it_end = (*j).perimeter.end(); + V2Set::iterator it_begin = (*j).perimeter.begin(); + + if(it_begin != it_end) { + a_map[std::min_element(it_begin, it_end)->first].remove(j); + } + + (*i).classification.push_back(ClassificationInfo(NULL, fc)); + (*j).classification.push_back(ClassificationInfo(NULL, fc)); + + collector.collect(&*i, hooks); + collector.collect(&*j, hooks); + + j = a_loops_grouped.erase(j); + i = b_loops_grouped.erase(i); + } + } + } + + // XXX: this may leave some face groups that are IN or OUT, and + // consist of a single face loop. + classifier.postRemovalCheck(a_loops_grouped, b_loops_grouped); + + classifier.faceLoopWork(a_loops_grouped, b_loops_grouped, vclass, poly_a, poly_a_rtree, poly_b, poly_b_rtree); + + classifier.finish(a_loops_grouped, b_loops_grouped); + } + + } +} diff --git a/extern/carve/lib/intersect_classify_edge.cpp b/extern/carve/lib/intersect_classify_edge.cpp new file mode 100644 index 00000000000..d2c1fdd7c24 --- /dev/null +++ b/extern/carve/lib/intersect_classify_edge.cpp @@ -0,0 +1,820 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#if defined(HAVE_STDINT_H) +#include <stdint.h> +#endif + +#include <carve/csg.hpp> +#include <carve/debug_hooks.hpp> +#include <carve/colour.hpp> + +#include <list> +#include <set> +#include <iostream> + +#include <algorithm> + +#include "csg_detail.hpp" + +#include "intersect_common.hpp" +#include "intersect_classify_common.hpp" + +#define ANGLE_EPSILON 1e-6 + +namespace carve { + namespace csg { + + namespace { + + inline bool single_bit_set(uint32_t v) { + v &= v - 1; + return v == 0; + } + + struct EdgeSurface { + FaceLoop *fwd; + double fwd_ang; + FaceLoop *rev; + double rev_ang; + + EdgeSurface() : fwd(NULL), fwd_ang(0.0), rev(NULL), rev_ang(0.0) { } + }; + + + typedef std::map<const carve::mesh::MeshSet<3>::mesh_t *, EdgeSurface> GrpEdgeSurfMap; + + typedef std::pair<FaceLoopGroup *, const carve::mesh::MeshSet<3>::mesh_t *> ClassificationKey; + + struct ClassificationData { + uint32_t class_bits : 5; + uint32_t class_decided : 1; + + int c[5]; + + ClassificationData() { + class_bits = FACE_ANY_BIT; + class_decided = 0; + memset(c, 0, sizeof(c)); + } + }; + + struct hash_classification { + size_t operator()(const ClassificationKey &f) const { + return (size_t)f.first ^ (size_t)f.second; + } + }; + + typedef std::unordered_map<ClassificationKey, ClassificationData, hash_classification> Classification; + + + struct hash_group_ptr { + size_t operator()(const FaceLoopGroup * const &f) const { + return (size_t)f; + } + }; + + + typedef std::pair<size_t, const carve::mesh::MeshSet<3>::vertex_t *> PerimKey; + + struct hash_perim_key { + size_t operator()(const PerimKey &v) const { + return (size_t)v.first ^ (size_t)v.second; + } + }; + + typedef std::unordered_map<std::pair<size_t, const carve::mesh::MeshSet<3>::vertex_t *>, + std::unordered_set<FaceLoopGroup *, hash_group_ptr>, + hash_perim_key> PerimMap; + + + + struct hash_group_pair { + size_t operator()(const std::pair<int, const FaceLoopGroup *> &v) const { + return (size_t)v.first ^ (size_t)v.second; + } + }; + + typedef std::unordered_map<const FaceLoopGroup *, + std::unordered_set<std::pair<int, const FaceLoopGroup *>, hash_group_pair>, + hash_group_ptr> CandidateOnMap; + + + + static inline void remove(carve::mesh::MeshSet<3>::vertex_t *a, + carve::mesh::MeshSet<3>::vertex_t *b, + carve::csg::detail::VVSMap &shared_edge_graph) { + carve::csg::detail::VVSMap::iterator i = shared_edge_graph.find(a); + CARVE_ASSERT(i != shared_edge_graph.end()); + size_t n = (*i).second.erase(b); + CARVE_ASSERT(n == 1); + if ((*i).second.size() == 0) shared_edge_graph.erase(i); + } + + + + static inline void remove(V2 edge, + carve::csg::detail::VVSMap &shared_edge_graph) { + remove(edge.first, edge.second, shared_edge_graph); + remove(edge.second, edge.first, shared_edge_graph); + } + + + + static void walkGraphSegment(carve::csg::detail::VVSMap &shared_edge_graph, + const carve::csg::detail::VSet &branch_points, + V2 initial, + const carve::csg::detail::LoopEdges & /* a_edge_map */, + const carve::csg::detail::LoopEdges & /* b_edge_map */, + std::list<V2> &out) { + V2 curr; + curr = initial; + bool closed = false; + + out.clear(); + for (;;) { + // walk forward. + out.push_back(curr); + remove(curr, shared_edge_graph); + + if (curr.second == initial.first) { closed = true; break; } + if (branch_points.find(curr.second) != branch_points.end()) break; + carve::csg::detail::VVSMap::const_iterator o = shared_edge_graph.find(curr.second); + if (o == shared_edge_graph.end()) break; + CARVE_ASSERT((*o).second.size() == 1); + curr.first = curr.second; + curr.second = *((*o).second.begin()); + // test here that the set of incident groups hasn't changed. + } + + if (!closed) { + // walk backward. + curr = initial; + for (;;) { + if (branch_points.find(curr.first) != branch_points.end()) break; + carve::csg::detail::VVSMap::const_iterator o = shared_edge_graph.find(curr.first); + if (o == shared_edge_graph.end()) break; + curr.second = curr.first; + curr.first = *((*o).second.begin()); + // test here that the set of incident groups hasn't changed. + + out.push_front(curr); + remove(curr, shared_edge_graph); + } + } + +#if defined(CARVE_DEBUG) + std::cerr << "intersection segment: " << out.size() << " edges." << std::endl; +#if defined(DEBUG_DRAW_INTERSECTION_LINE) + { + static float H = 0.0, S = 1.0, V = 1.0; + float r, g, b; + + H = fmod((H + .37), 1.0); + S = 0.5 + fmod((S - 0.37), 0.5); + carve::colour::HSV2RGB(H, S, V, r, g, b); + + if (out.size() > 1) { + drawEdges(out.begin(), ++out.begin(), + 0.0, 0.0, 0.0, 1.0, + r, g, b, 1.0, + 3.0); + drawEdges(++out.begin(), --out.end(), + r, g, b, 1.0, + r, g, b, 1.0, + 3.0); + drawEdges(--out.end(), out.end(), + r, g, b, 1.0, + 1.0, 1.0, 1.0, 1.0, + 3.0); + } else { + drawEdges(out.begin(), out.end(), + r, g, b, 1.0, + r, g, b, 1.0, + 3.0); + } + } +#endif +#endif + } + + + + static carve::geom3d::Vector perpendicular(const carve::geom3d::Vector &v) { + if (fabs(v.x) < fabs(v.y)) { + if (fabs(v.x) < fabs(v.z)) { + return cross(v, carve::geom::VECTOR(1.0, 0.0, 0.0)).normalized(); + } else { + return cross(v, carve::geom::VECTOR(0.0, 0.0, 1.0)).normalized(); + } + } else { + if (fabs(v.y) < fabs(v.z)) { + return cross(v, carve::geom::VECTOR(0.0, 1.0, 0.0)).normalized(); + } else { + return cross(v, carve::geom::VECTOR(1.0, 0.0, 1.0)).normalized(); + } + } + } + + + + static void classifyAB(const GrpEdgeSurfMap &a_edge_surfaces, + const GrpEdgeSurfMap &b_edge_surfaces, + Classification &classifications) { + // two faces in the a surface + for (GrpEdgeSurfMap::const_iterator ib = b_edge_surfaces.begin(), eb = b_edge_surfaces.end(); ib != eb; ++ib) { + + if ((*ib).second.fwd) { + FaceLoopGroup *b_grp = ((*ib).second.fwd->group); + + for (GrpEdgeSurfMap::const_iterator ia = a_edge_surfaces.begin(), ea = a_edge_surfaces.end(); ia != ea; ++ia) { + + if ((*ia).second.fwd && (*ia).second.rev) { + const carve::mesh::MeshSet<3>::mesh_t *a_gid = (*ia).first; + + ClassificationData &data = classifications[std::make_pair(b_grp, a_gid)]; + if (data.class_decided) continue; + + // an angle between (*ia).fwd_ang and (*ia).rev_ang is outside/above group a. + FaceClass fc; + + if (fabs((*ib).second.fwd_ang - (*ia).second.fwd_ang) < ANGLE_EPSILON) { + fc = FACE_ON_ORIENT_OUT; + } else if (fabs((*ib).second.fwd_ang - (*ia).second.rev_ang) < ANGLE_EPSILON) { + fc = FACE_ON_ORIENT_IN; + } else { + double a1 = (*ia).second.fwd_ang; + double a2 = (*ia).second.rev_ang; + if (a1 < a2) { + if (a1 < (*ib).second.fwd_ang && (*ib).second.fwd_ang < a2) { + fc = FACE_IN; + } else { + fc = FACE_OUT; + } + } else { + if (a2 < (*ib).second.fwd_ang && (*ib).second.fwd_ang < a1) { + fc = FACE_OUT; + } else { + fc = FACE_IN; + } + } + } + data.c[fc + 2]++; + } + } + } + + if ((*ib).second.rev) { + FaceLoopGroup *b_grp = ((*ib).second.rev->group); + + for (GrpEdgeSurfMap::const_iterator ia = a_edge_surfaces.begin(), ea = a_edge_surfaces.end(); ia != ea; ++ia) { + + if ((*ia).second.fwd && (*ia).second.rev) { + const carve::mesh::MeshSet<3>::mesh_t *a_gid = (*ia).first; + + ClassificationData &data = (classifications[std::make_pair(b_grp, a_gid)]); + if (data.class_decided) continue; + + // an angle between (*ia).fwd_ang and (*ia).rev_ang is outside/above group a. + FaceClass fc; + + if (fabs((*ib).second.rev_ang - (*ia).second.fwd_ang) < ANGLE_EPSILON) { + fc = FACE_ON_ORIENT_IN; + } else if (fabs((*ib).second.rev_ang - (*ia).second.rev_ang) < ANGLE_EPSILON) { + fc = FACE_ON_ORIENT_OUT; + } else { + double a1 = (*ia).second.fwd_ang; + double a2 = (*ia).second.rev_ang; + if (a1 < a2) { + if (a1 < (*ib).second.rev_ang && (*ib).second.rev_ang < a2) { + fc = FACE_IN; + } else { + fc = FACE_OUT; + } + } else { + if (a2 < (*ib).second.rev_ang && (*ib).second.rev_ang < a1) { + fc = FACE_OUT; + } else { + fc = FACE_IN; + } + } + } + data.c[fc + 2]++; + } + } + } + } + } + + + static bool processForwardEdgeSurfaces(GrpEdgeSurfMap &edge_surfaces, + const std::list<FaceLoop *> &fwd, + const carve::geom3d::Vector &edge_vector, + const carve::geom3d::Vector &base_vector) { + for (std::list<FaceLoop *>::const_iterator i = fwd.begin(), e = fwd.end(); i != e; ++i) { + EdgeSurface &es = (edge_surfaces[(*i)->orig_face->mesh]); + if (es.fwd != NULL) return false; + es.fwd = (*i); + es.fwd_ang = carve::geom3d::antiClockwiseAngle((*i)->orig_face->plane.N, base_vector, edge_vector); + } + return true; + } + + static bool processReverseEdgeSurfaces(GrpEdgeSurfMap &edge_surfaces, + const std::list<FaceLoop *> &rev, + const carve::geom3d::Vector &edge_vector, + const carve::geom3d::Vector &base_vector) { + for (std::list<FaceLoop *>::const_iterator i = rev.begin(), e = rev.end(); i != e; ++i) { + EdgeSurface &es = (edge_surfaces[(*i)->orig_face->mesh]); + if (es.rev != NULL) return false; + es.rev = (*i); + es.rev_ang = carve::geom3d::antiClockwiseAngle(-(*i)->orig_face->plane.N, base_vector, edge_vector); + } + return true; + } + + + + static void processOneEdge(const V2 &edge, + const carve::csg::detail::LoopEdges &a_edge_map, + const carve::csg::detail::LoopEdges &b_edge_map, + Classification &a_classification, + Classification &b_classification) { + GrpEdgeSurfMap a_edge_surfaces; + GrpEdgeSurfMap b_edge_surfaces; + + carve::geom3d::Vector edge_vector = (edge.second->v - edge.first->v).normalized(); + carve::geom3d::Vector base_vector = perpendicular(edge_vector); + + carve::csg::detail::LoopEdges::const_iterator ae_f = a_edge_map.find(edge); + carve::csg::detail::LoopEdges::const_iterator ae_r = a_edge_map.find(flip(edge)); + CARVE_ASSERT(ae_f != a_edge_map.end() || ae_r != a_edge_map.end()); + + carve::csg::detail::LoopEdges::const_iterator be_f = b_edge_map.find(edge); + carve::csg::detail::LoopEdges::const_iterator be_r = b_edge_map.find(flip(edge)); + CARVE_ASSERT(be_f != b_edge_map.end() || be_r != b_edge_map.end()); + + if (ae_f != a_edge_map.end() && !processForwardEdgeSurfaces(a_edge_surfaces, (*ae_f).second, edge_vector, base_vector)) return; + if (ae_r != a_edge_map.end() && !processReverseEdgeSurfaces(a_edge_surfaces, (*ae_r).second, edge_vector, base_vector)) return; + if (be_f != b_edge_map.end() && !processForwardEdgeSurfaces(b_edge_surfaces, (*be_f).second, edge_vector, base_vector)) return; + if (be_r != b_edge_map.end() && !processReverseEdgeSurfaces(b_edge_surfaces, (*be_r).second, edge_vector, base_vector)) return; + + classifyAB(a_edge_surfaces, b_edge_surfaces, b_classification); + classifyAB(b_edge_surfaces, a_edge_surfaces, a_classification); + } + + + + static void traceIntersectionGraph(const V2Set &shared_edges, + const FLGroupList & /* a_loops_grouped */, + const FLGroupList & /* b_loops_grouped */, + const carve::csg::detail::LoopEdges &a_edge_map, + const carve::csg::detail::LoopEdges &b_edge_map) { + + carve::csg::detail::VVSMap shared_edge_graph; + carve::csg::detail::VSet branch_points; + + // first, make the intersection graph. + for (V2Set::const_iterator i = shared_edges.begin(); i != shared_edges.end(); ++i) { + const V2Set::key_type &edge = (*i); + carve::csg::detail::VVSMap::mapped_type &out = (shared_edge_graph[edge.first]); + out.insert(edge.second); + if (out.size() == 3) branch_points.insert(edge.first); + +#if defined(CARVE_DEBUG) && defined(DEBUG_DRAW_INTERSECTION_LINE) + HOOK(drawEdge(edge.first, edge.second, 1, 1, 1, 1, 1, 1, 1, 1, 1.0);); +#endif + } +#if defined(CARVE_DEBUG) + std::cerr << "graph nodes: " << shared_edge_graph.size() << std::endl; + std::cerr << "branch nodes: " << branch_points.size() << std::endl; +#endif + + std::list<V2> out; + while (shared_edge_graph.size()) { + carve::csg::detail::VVSMap::iterator i = shared_edge_graph.begin(); + carve::mesh::MeshSet<3>::vertex_t *v1 = (*i).first; + carve::mesh::MeshSet<3>::vertex_t *v2 = *((*i).second.begin()); + walkGraphSegment(shared_edge_graph, branch_points, V2(v1, v2), a_edge_map, b_edge_map, out); + } + } + + void hashByPerimeter(FLGroupList &grp, PerimMap &perim_map) { + for (FLGroupList::iterator i = grp.begin(); i != grp.end(); ++i) { + size_t perim_size = (*i).perimeter.size(); + // can be the case for non intersecting groups. (and groups that intersect at a point?) + if (!perim_size) continue; + const carve::mesh::MeshSet<3>::vertex_t *perim_min = std::min_element((*i).perimeter.begin(), (*i).perimeter.end())->first; + perim_map[std::make_pair(perim_size, perim_min)].insert(&(*i)); + } + } + + + + bool same_edge_set_fwd(const V2Set &a, const V2Set &b) { + if (a.size() != b.size()) return false; + for (V2Set::const_iterator i = a.begin(), e = a.end(); i != e; ++i) { + if (b.find(*i) == b.end()) return false; + } + return true; + } + + + + bool same_edge_set_rev(const V2Set &a, const V2Set &b) { + if (a.size() != b.size()) return false; + for (V2Set::const_iterator i = a.begin(), e = a.end(); i != e; ++i) { + if (b.find(std::make_pair((*i).second, (*i).first)) == b.end()) return false; + } + return true; + } + + + + int same_edge_set(const V2Set &a, const V2Set &b) { + if (same_edge_set_fwd(a, b)) return +1; + if (same_edge_set_rev(a, b)) return -1; + return 0; + } + + + + void generateCandidateOnSets(FLGroupList &a_grp, + FLGroupList &b_grp, + CandidateOnMap &candidate_on_map, + Classification &a_classification, + Classification &b_classification) { + PerimMap a_grp_by_perim, b_grp_by_perim; + + hashByPerimeter(a_grp, a_grp_by_perim); + hashByPerimeter(b_grp, b_grp_by_perim); + + for (PerimMap::iterator i = a_grp_by_perim.begin(), ie = a_grp_by_perim.end(); i != ie; ++i) { + PerimMap::iterator j = b_grp_by_perim.find((*i).first); + if (j == b_grp_by_perim.end()) continue; + + for (PerimMap::mapped_type::iterator a = (*i).second.begin(), ae = (*i).second.end(); a != ae; ++a) { + for (PerimMap::mapped_type::iterator b = (*j).second.begin(), be = (*j).second.end(); b != be; ++b) { + int x = same_edge_set((*a)->perimeter, (*b)->perimeter); + if (!x) continue; + candidate_on_map[(*a)].insert(std::make_pair(x, (*b))); + if ((*a)->face_loops.count == 1 && (*b)->face_loops.count == 1) { + uint32_t fcb = x == +1 ? FACE_ON_ORIENT_OUT_BIT : FACE_ON_ORIENT_IN_BIT; + +#if defined(CARVE_DEBUG) + std::cerr << "paired groups: " << (*a) << ", " << (*b) << std::endl; +#endif + + ClassificationData &a_data = a_classification[std::make_pair((*a), (*b)->face_loops.head->orig_face->mesh)]; + a_data.class_bits = fcb; a_data.class_decided = 1; + + ClassificationData &b_data = b_classification[std::make_pair((*b), (*a)->face_loops.head->orig_face->mesh)]; + b_data.class_bits = fcb; b_data.class_decided = 1; + } + } + } + } + } + + } + + + static inline std::string CODE(const FaceLoopGroup *grp) { + const std::list<ClassificationInfo> &cinfo = (grp->classification); + if (cinfo.size() == 0) { + return "?"; + } + + FaceClass fc = FACE_UNCLASSIFIED; + + for (std::list<ClassificationInfo>::const_iterator i = grp->classification.begin(), e = grp->classification.end(); i != e; ++i) { + if ((*i).intersected_mesh == NULL) { + // classifier only returns global info + fc = (*i).classification; + break; + } + + if ((*i).intersectedMeshIsClosed()) { + if ((*i).classification == FACE_UNCLASSIFIED) continue; + if (fc == FACE_UNCLASSIFIED) { + fc = (*i).classification; + } else if (fc != (*i).classification) { + return "X"; + } + } + } + if (fc == FACE_IN) return "I"; + if (fc == FACE_ON_ORIENT_IN) return "<"; + if (fc == FACE_ON_ORIENT_OUT) return ">"; + if (fc == FACE_OUT) return "O"; + return "*"; + } + + void CSG::classifyFaceGroupsEdge(const V2Set &shared_edges, + VertexClassification &vclass, + carve::mesh::MeshSet<3> *poly_a, + const face_rtree_t *poly_a_rtree, + FLGroupList &a_loops_grouped, + const detail::LoopEdges &a_edge_map, + carve::mesh::MeshSet<3> *poly_b, + const face_rtree_t *poly_b_rtree, + FLGroupList &b_loops_grouped, + const detail::LoopEdges &b_edge_map, + CSG::Collector &collector) { + Classification a_classification; + Classification b_classification; + + CandidateOnMap candidate_on_map; + +#if defined(CARVE_DEBUG) + std::cerr << "a input loops (" << a_loops_grouped.size() << "): "; + for (FLGroupList::iterator i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) { + std::cerr << &*i << " "; + } + std::cerr << std::endl; + std::cerr << "b input loops (" << b_loops_grouped.size() << "): "; + for (FLGroupList::iterator i = b_loops_grouped.begin(); i != b_loops_grouped.end(); ++i) { + std::cerr << &*i << " "; + } + std::cerr << std::endl; +#endif + +#if defined(DISPLAY_GRP_GRAPH) + // XXX: this is hopelessly inefficient. + std::map<const FaceLoopGroup *, std::set<const FaceLoopGroup *> > grp_graph_fwd, grp_graph_rev; + { + for (FLGroupList::iterator i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) { + FaceLoopGroup *src = &(*i); + for (V2Set::const_iterator k = src->perimeter.begin(); k != src->perimeter.end(); ++k) { + V2 fwd = *k; + V2 rev = std::make_pair(fwd.second, fwd.first); + for (FLGroupList::iterator j = a_loops_grouped.begin(); j != a_loops_grouped.end(); ++j) { + FaceLoopGroup *tgt = &(*j); + if (tgt->perimeter.find(fwd) != tgt->perimeter.end()) { grp_graph_fwd[src].insert(tgt); } + if (tgt->perimeter.find(rev) != tgt->perimeter.end()) { grp_graph_rev[src].insert(tgt); } + } + for (FLGroupList::iterator j = b_loops_grouped.begin(); j != b_loops_grouped.end(); ++j) { + FaceLoopGroup *tgt = &(*j); + if (tgt->perimeter.find(fwd) != tgt->perimeter.end()) { grp_graph_fwd[src].insert(tgt); } + if (tgt->perimeter.find(rev) != tgt->perimeter.end()) { grp_graph_rev[src].insert(tgt); } + } + } + } + for (FLGroupList::iterator i = b_loops_grouped.begin(); i != b_loops_grouped.end(); ++i) { + FaceLoopGroup *src = &(*i); + for (V2Set::const_iterator k = src->perimeter.begin(); k != src->perimeter.end(); ++k) { + V2 fwd = *k; + V2 rev = std::make_pair(fwd.second, fwd.first); + for (FLGroupList::iterator j = a_loops_grouped.begin(); j != a_loops_grouped.end(); ++j) { + FaceLoopGroup *tgt = &(*j); + if (tgt->perimeter.find(fwd) != tgt->perimeter.end()) { grp_graph_fwd[src].insert(tgt); } + if (tgt->perimeter.find(rev) != tgt->perimeter.end()) { grp_graph_rev[src].insert(tgt); } + } + for (FLGroupList::iterator j = b_loops_grouped.begin(); j != b_loops_grouped.end(); ++j) { + FaceLoopGroup *tgt = &(*j); + if (tgt->perimeter.find(fwd) != tgt->perimeter.end()) { grp_graph_fwd[src].insert(tgt); } + if (tgt->perimeter.find(rev) != tgt->perimeter.end()) { grp_graph_rev[src].insert(tgt); } + } + } + } + } +#endif + + generateCandidateOnSets(a_loops_grouped, b_loops_grouped, candidate_on_map, a_classification, b_classification); + + + for (V2Set::const_iterator i = shared_edges.begin(); i != shared_edges.end(); ++i) { + const V2 &edge = (*i); + processOneEdge(edge, a_edge_map, b_edge_map, a_classification, b_classification); + } + + + for (Classification::iterator i = a_classification.begin(), e = a_classification.end(); i != e; ++i) { + if (!(*i).second.class_decided) { + if ((*i).second.c[FACE_IN + 2] == 0) (*i).second.class_bits &= ~ FACE_IN_BIT; + if ((*i).second.c[FACE_ON_ORIENT_IN + 2] == 0) (*i).second.class_bits &= ~ FACE_ON_ORIENT_IN_BIT; + if ((*i).second.c[FACE_ON_ORIENT_OUT + 2] == 0) (*i).second.class_bits &= ~ FACE_ON_ORIENT_OUT_BIT; + if ((*i).second.c[FACE_OUT + 2] == 0) (*i).second.class_bits &= ~ FACE_OUT_BIT; + + // XXX: this is the wrong thing to do. It's intended just as a test. + if ((*i).second.class_bits == (FACE_IN_BIT | FACE_OUT_BIT)) { + if ((*i).second.c[FACE_OUT + 2] > (*i).second.c[FACE_IN + 2]) { + (*i).second.class_bits = FACE_OUT_BIT; + } else { + (*i).second.class_bits = FACE_IN_BIT; + } + } + + if (single_bit_set((*i).second.class_bits)) (*i).second.class_decided = 1; + } + } + + for (Classification::iterator i = b_classification.begin(), e = b_classification.end(); i != e; ++i) { + if (!(*i).second.class_decided) { + if ((*i).second.c[FACE_IN + 2] == 0) (*i).second.class_bits &= ~ FACE_IN_BIT; + if ((*i).second.c[FACE_ON_ORIENT_IN + 2] == 0) (*i).second.class_bits &= ~ FACE_ON_ORIENT_IN_BIT; + if ((*i).second.c[FACE_ON_ORIENT_OUT + 2] == 0) (*i).second.class_bits &= ~ FACE_ON_ORIENT_OUT_BIT; + if ((*i).second.c[FACE_OUT + 2] == 0) (*i).second.class_bits &= ~ FACE_OUT_BIT; + + // XXX: this is the wrong thing to do. It's intended just as a test. + if ((*i).second.class_bits == (FACE_IN_BIT | FACE_OUT_BIT)) { + if ((*i).second.c[FACE_OUT + 2] > (*i).second.c[FACE_IN + 2]) { + (*i).second.class_bits = FACE_OUT_BIT; + } else { + (*i).second.class_bits = FACE_IN_BIT; + } + } + + if (single_bit_set((*i).second.class_bits)) (*i).second.class_decided = 1; + } + } + + +#if defined(CARVE_DEBUG) + std::cerr << "poly a:" << std::endl; + for (Classification::iterator i = a_classification.begin(), e = a_classification.end(); i != e; ++i) { + FaceLoopGroup *grp = ((*i).first.first); + + std::cerr << " group: " << grp << " gid: " << (*i).first.second + << " " + << ((*i).second.class_decided ? "+" : "-") + << " " + << ((*i).second.class_bits & FACE_IN_BIT ? "I" : ".") + << ((*i).second.class_bits & FACE_ON_ORIENT_IN_BIT ? "<" : ".") + << ((*i).second.class_bits & FACE_ON_ORIENT_OUT_BIT ? ">" : ".") + << ((*i).second.class_bits & FACE_OUT_BIT ? "O" : ".") + << " [" + << std::setw(4) << (*i).second.c[0] << " " + << std::setw(4) << (*i).second.c[1] << " " + << std::setw(4) << (*i).second.c[2] << " " + << std::setw(4) << (*i).second.c[3] << " " + << std::setw(4) << (*i).second.c[4] << "]" << std::endl; + } + + std::cerr << "poly b:" << std::endl; + for (Classification::iterator i = b_classification.begin(), e = b_classification.end(); i != e; ++i) { + FaceLoopGroup *grp = ((*i).first.first); + + std::cerr << " group: " << grp << " gid: " << (*i).first.second + << " " + << ((*i).second.class_decided ? "+" : "-") + << " " + << ((*i).second.class_bits & FACE_IN_BIT ? "I" : ".") + << ((*i).second.class_bits & FACE_ON_ORIENT_IN_BIT ? "<" : ".") + << ((*i).second.class_bits & FACE_ON_ORIENT_OUT_BIT ? ">" : ".") + << ((*i).second.class_bits & FACE_OUT_BIT ? "O" : ".") + << " [" + << std::setw(4) << (*i).second.c[0] << " " + << std::setw(4) << (*i).second.c[1] << " " + << std::setw(4) << (*i).second.c[2] << " " + << std::setw(4) << (*i).second.c[3] << " " + << std::setw(4) << (*i).second.c[4] << "]" << std::endl; + } +#endif + + for (Classification::iterator i = a_classification.begin(), e = a_classification.end(); i != e; ++i) { + FaceLoopGroup *grp = ((*i).first.first); + + grp->classification.push_back(ClassificationInfo()); + ClassificationInfo &info = grp->classification.back(); + + info.intersected_mesh = (*i).first.second; + + if ((*i).second.class_decided) { + info.classification = class_bit_to_class((*i).second.class_bits); + } else { + info.classification = FACE_UNCLASSIFIED; + } + } + + for (Classification::iterator i = b_classification.begin(), e = b_classification.end(); i != e; ++i) { + FaceLoopGroup *grp = ((*i).first.first); + + grp->classification.push_back(ClassificationInfo()); + ClassificationInfo &info = grp->classification.back(); + + info.intersected_mesh = (*i).first.second; + + if ((*i).second.class_decided) { + info.classification = class_bit_to_class((*i).second.class_bits); + } else { + info.classification = FACE_UNCLASSIFIED; + } + } + + for (FLGroupList::iterator i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) { + if ((*i).classification.size() == 0) { +#if defined(CARVE_DEBUG) + std::cerr << " non intersecting group (poly a): " << &(*i) << std::endl; +#endif + bool classified = false; + for (FaceLoop *fl = (*i).face_loops.head; !classified && fl != NULL; fl = fl->next) { + for (size_t fli = 0; !classified && fli < fl->vertices.size(); ++fli) { + if (vclass[fl->vertices[fli]].cls[1] == POINT_UNK) { + vclass[fl->vertices[fli]].cls[1] = carve::mesh::classifyPoint(poly_b, poly_b_rtree, fl->vertices[fli]->v); + } + switch (vclass[fl->vertices[fli]].cls[1]) { + case POINT_IN: + (*i).classification.push_back(ClassificationInfo(NULL, FACE_IN)); + classified = true; + break; + case POINT_OUT: + (*i).classification.push_back(ClassificationInfo(NULL, FACE_OUT)); + classified = true; + break; + default: + break; + } + } + } + if (!classified) { + throw carve::exception("non intersecting group is not IN or OUT! (poly_a)"); + } + } + } + + for (FLGroupList::iterator i = b_loops_grouped.begin(); i != b_loops_grouped.end(); ++i) { + if ((*i).classification.size() == 0) { +#if defined(CARVE_DEBUG) + std::cerr << " non intersecting group (poly b): " << &(*i) << std::endl; +#endif + bool classified = false; + for (FaceLoop *fl = (*i).face_loops.head; !classified && fl != NULL; fl = fl->next) { + for (size_t fli = 0; !classified && fli < fl->vertices.size(); ++fli) { + if (vclass[fl->vertices[fli]].cls[0] == POINT_UNK) { + vclass[fl->vertices[fli]].cls[0] = carve::mesh::classifyPoint(poly_a, poly_a_rtree, fl->vertices[fli]->v); + } + switch (vclass[fl->vertices[fli]].cls[0]) { + case POINT_IN: + (*i).classification.push_back(ClassificationInfo(NULL, FACE_IN)); + classified = true; + break; + case POINT_OUT: + (*i).classification.push_back(ClassificationInfo(NULL, FACE_OUT)); + classified = true; + break; + default: + break; + } + } + } + if (!classified) { + throw carve::exception("non intersecting group is not IN or OUT! (poly_b)"); + } + } + } + +#if defined(DISPLAY_GRP_GRAPH) +#define POLY(grp) (std::string((grp)->face_loops.head->orig_face->polyhedron == poly_a ? "[A:" : "[B:") + CODE(grp) + "]") + + for (std::map<const FaceLoopGroup *, std::set<const FaceLoopGroup *> >::iterator i = grp_graph_fwd.begin(); i != grp_graph_fwd.end(); ++i) { + const FaceLoopGroup *grp = (*i).first; + + std::cerr << "GRP: " << grp << POLY(grp) << std::endl; + + std::set<const FaceLoopGroup *> &fwd_set = grp_graph_fwd[grp]; + std::set<const FaceLoopGroup *> &rev_set = grp_graph_rev[grp]; + std::cerr << " FWD: "; + for (std::set<const FaceLoopGroup *>::const_iterator j = fwd_set.begin(); j != fwd_set.end(); ++j) { + std::cerr << " " << (*j) << POLY(*j); + } + std::cerr << std::endl; + std::cerr << " REV: "; + for (std::set<const FaceLoopGroup *>::const_iterator j = rev_set.begin(); j != rev_set.end(); ++j) { + std::cerr << " " << (*j) << POLY(*j); + } + std::cerr << std::endl; + } +#endif + + for (FLGroupList::iterator i = a_loops_grouped.begin(); i != a_loops_grouped.end(); ++i) { + collector.collect(&*i, hooks); + } + + for (FLGroupList::iterator i = b_loops_grouped.begin(); i != b_loops_grouped.end(); ++i) { + collector.collect(&*i, hooks); + } + + // traceIntersectionGraph(shared_edges, a_loops_grouped, b_loops_grouped, a_edge_map, b_edge_map); + } + + } +} diff --git a/extern/carve/lib/intersect_classify_group.cpp b/extern/carve/lib/intersect_classify_group.cpp new file mode 100644 index 00000000000..4251af63f89 --- /dev/null +++ b/extern/carve/lib/intersect_classify_group.cpp @@ -0,0 +1,220 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/csg.hpp> +#include <carve/debug_hooks.hpp> + +#include <list> +#include <set> +#include <iostream> + +#include <algorithm> + +#include "intersect_common.hpp" +#include "intersect_classify_common.hpp" +#include "intersect_classify_common_impl.hpp" + + +namespace carve { + namespace csg { + + namespace { + +#if defined(_MSC_VER) && _MSC_VER < 1300 + // VC++ 6.0 gets an internal compiler when compiling + // the FaceMaker template. Not sure why but for now we just bypass + // the template + class FaceMaker0 { + public: + CSG::Collector &collector; + CSG::Hooks &hooks; + + FaceMaker0(CSG::Collector &c, CSG::Hooks &h) : collector(c), hooks(h) { + } + bool pointOn(VertexClassification &vclass, FaceLoop *f, size_t index) const { + return vclass[f->vertices[index]].cls[1] == POINT_ON; + } + void explain(FaceLoop *f, size_t index, PointClass pc) const { +#if defined(CARVE_DEBUG) + std::cerr << "face loop " << f << " from poly " << "ab"[0] << " is easy because vertex " << index << " (" << *f->vertices[index] << ") is " << ENUM(pc) << std::endl; +#endif + } + }; + class FaceMaker1 { + public: + CSG::Collector &collector; + CSG::Hooks &hooks; + + FaceMaker1(CSG::Collector &c, CSG::Hooks &h) : collector(c), hooks(h) { + } + bool pointOn(VertexClassification &vclass, FaceLoop *f, size_t index) const { + return vclass[f->vertices[index]].cls[0] == POINT_ON; + } + void explain(FaceLoop *f, size_t index, PointClass pc) const { +#if defined(CARVE_DEBUG) + std::cerr << "face loop " << f << " from poly " << "ab"[1] << " is easy because vertex " << index << " (" << *f->vertices[index] << ") is " << ENUM(pc) << std::endl; +#endif + } + }; +#else + template <int poly_num> + class FaceMaker { + FaceMaker &operator=(const FaceMaker &); + + public: + CSG::Collector &collector; + CSG::Hooks &hooks; + + FaceMaker(CSG::Collector &c, CSG::Hooks &h) : collector(c), hooks(h) { + } + + bool pointOn(VertexClassification &vclass, FaceLoop *f, size_t index) const { + return vclass[f->vertices[index]].cls[1 - poly_num] == POINT_ON; + } + + void explain(FaceLoop *f, size_t index, PointClass pc) const { +#if defined(CARVE_DEBUG) + std::cerr << "face loop " << f << " from poly " << "ab"[poly_num] << " is easy because vertex " << index << " (" << f->vertices[index]->v << ") is " << ENUM(pc) << std::endl; +#endif + } + }; + typedef FaceMaker<0> FaceMaker0; + typedef FaceMaker<1> FaceMaker1; +#endif + class ClassifyFaceGroups { + ClassifyFaceGroups &operator=(const ClassifyFaceGroups &); + + public: + CSG::Collector &collector; + CSG::Hooks &hooks; + + ClassifyFaceGroups(CSG::Collector &c, CSG::Hooks &h) : collector(c), hooks(h) { + } + + void classifySimple(FLGroupList &a_loops_grouped, + FLGroupList &b_loops_grouped, + VertexClassification & /* vclass */, + carve::mesh::MeshSet<3> *poly_a, + carve::mesh::MeshSet<3> *poly_b) const { + if (a_loops_grouped.size() < b_loops_grouped.size()) { + performClassifySimpleOnFaceGroups(a_loops_grouped, b_loops_grouped, poly_a, poly_b, collector, hooks); + } else { + performClassifySimpleOnFaceGroups(b_loops_grouped, a_loops_grouped, poly_b, poly_a, collector, hooks); + } +#if defined(CARVE_DEBUG) + std::cerr << "after removal of simple on groups: " << a_loops_grouped.size() << " a groups" << std::endl; + std::cerr << "after removal of simple on groups: " << b_loops_grouped.size() << " b groups" << std::endl; +#endif + } + + void classifyEasy(FLGroupList &a_loops_grouped, + FLGroupList &b_loops_grouped, + VertexClassification &vclass, + carve::mesh::MeshSet<3> *poly_a, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree, + carve::mesh::MeshSet<3> *poly_b, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree) const { + performClassifyEasyFaceGroups(a_loops_grouped, poly_b, poly_b_rtree, vclass, FaceMaker0(collector, hooks), collector, hooks); + performClassifyEasyFaceGroups(b_loops_grouped, poly_a, poly_a_rtree, vclass, FaceMaker1(collector, hooks), collector, hooks); +#if defined(CARVE_DEBUG) + std::cerr << "after removal of easy groups: " << a_loops_grouped.size() << " a groups" << std::endl; + std::cerr << "after removal of easy groups: " << b_loops_grouped.size() << " b groups" << std::endl; +#endif + } + + void classifyHard(FLGroupList &a_loops_grouped, + FLGroupList &b_loops_grouped, + VertexClassification & /* vclass */, + carve::mesh::MeshSet<3> *poly_a, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree, + carve::mesh::MeshSet<3> *poly_b, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree) const { + performClassifyHardFaceGroups(a_loops_grouped, poly_b, poly_b_rtree, FaceMaker0(collector, hooks), collector, hooks); + performClassifyHardFaceGroups(b_loops_grouped, poly_a, poly_a_rtree, FaceMaker1(collector, hooks), collector, hooks); +#if defined(CARVE_DEBUG) + std::cerr << "after removal of hard groups: " << a_loops_grouped.size() << " a groups" << std::endl; + std::cerr << "after removal of hard groups: " << b_loops_grouped.size() << " b groups" << std::endl; +#endif + } + + void faceLoopWork(FLGroupList &a_loops_grouped, + FLGroupList &b_loops_grouped, + VertexClassification & /* vclass */, + carve::mesh::MeshSet<3> *poly_a, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree, + carve::mesh::MeshSet<3> *poly_b, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree) const { + performFaceLoopWork(poly_b, poly_b_rtree, a_loops_grouped, *this, collector, hooks); + performFaceLoopWork(poly_a, poly_a_rtree, b_loops_grouped, *this, collector, hooks); + } + + void postRemovalCheck(FLGroupList &a_loops_grouped, + FLGroupList &b_loops_grouped) const { +#if defined(CARVE_DEBUG) + std::cerr << "after removal of on groups: " << a_loops_grouped.size() << " a groups" << std::endl; + std::cerr << "after removal of on groups: " << b_loops_grouped.size() << " b groups" << std::endl; +#endif + } + + bool faceLoopSanityChecker(FaceLoopGroup &i) const { + return i.face_loops.size() != 1; + } + + void finish(FLGroupList &a_loops_grouped,FLGroupList &b_loops_grouped) const { +#if defined(CARVE_DEBUG) + if (a_loops_grouped.size() || b_loops_grouped.size()) + std::cerr << "UNCLASSIFIED! a=" << a_loops_grouped.size() << ", b=" << b_loops_grouped.size() << std::endl; +#endif + } + }; + } + + void CSG::classifyFaceGroups(const V2Set & /* shared_edges */, + VertexClassification &vclass, + carve::mesh::MeshSet<3> *poly_a, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree, + FLGroupList &a_loops_grouped, + const detail::LoopEdges & /* a_edge_map */, + carve::mesh::MeshSet<3> *poly_b, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree, + FLGroupList &b_loops_grouped, + const detail::LoopEdges & /* b_edge_map */, + CSG::Collector &collector) { + ClassifyFaceGroups classifier(collector, hooks); +#if defined(CARVE_DEBUG) + std::cerr << "initial groups: " << a_loops_grouped.size() << " a groups" << std::endl; + std::cerr << "initial groups: " << b_loops_grouped.size() << " b groups" << std::endl; +#endif + performClassifyFaceGroups( + a_loops_grouped, + b_loops_grouped, + vclass, + poly_a, + poly_a_rtree, + poly_b, + poly_b_rtree, + classifier, + collector, + hooks); + } + + } +} diff --git a/extern/carve/lib/intersect_common.hpp b/extern/carve/lib/intersect_common.hpp new file mode 100644 index 00000000000..06f3cfdd4ec --- /dev/null +++ b/extern/carve/lib/intersect_common.hpp @@ -0,0 +1,83 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#pragma once + + +static inline bool facesAreCoplanar(const carve::mesh::MeshSet<3>::face_t *a, const carve::mesh::MeshSet<3>::face_t *b) { + carve::geom3d::Ray temp; + // XXX: Find a better definition. This may be a source of problems + // if floating point inaccuracies cause an incorrect answer. + return !carve::geom3d::planeIntersection(a->plane, b->plane, temp); +} + +#if defined(CARVE_DEBUG) + +#include <carve/debug_hooks.hpp> + +#endif + +namespace carve { + namespace csg { + + static inline carve::mesh::MeshSet<3>::vertex_t *map_vertex(const VVMap &vmap, carve::mesh::MeshSet<3>::vertex_t *v) { + VVMap::const_iterator i = vmap.find(v); + if (i == vmap.end()) return v; + return (*i).second; + } + +#if defined(CARVE_DEBUG) + + class IntersectDebugHooks; + extern IntersectDebugHooks *g_debug; + +#define HOOK(x) do { if (g_debug) { g_debug->x } } while(0) + + static inline void drawFaceLoopList(const FaceLoopList &ll, + float rF, float gF, float bF, float aF, + float rB, float gB, float bB, float aB, + bool lit) { + for (FaceLoop *flb = ll.head; flb; flb = flb->next) { + const carve::mesh::MeshSet<3>::face_t *f = (flb->orig_face); + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &loop = flb->vertices; + HOOK(drawFaceLoop2(loop, f->plane.N, rF, gF, bF, aF, rB, gB, bB, aB, true, lit);); + HOOK(drawFaceLoopWireframe(loop, f->plane.N, 1, 1, 1, 0.1f);); + } + } + + static inline void drawFaceLoopListWireframe(const FaceLoopList &ll) { + for (FaceLoop *flb = ll.head; flb; flb = flb->next) { + const carve::mesh::MeshSet<3>::face_t *f = (flb->orig_face); + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &loop = flb->vertices; + HOOK(drawFaceLoopWireframe(loop, f->plane.N, 1, 1, 1, 0.1f);); + } + } + + template<typename T> + static inline void drawEdges(T begin, T end, + float rB, float gB, float bB, float aB, + float rE, float gE, float bE, float aE, + float w) { + for (; begin != end; ++begin) { + HOOK(drawEdge((*begin).first, (*begin).second, rB, gB, bB, aB, rE, gE, bE, aE, w);); + } + } + +#endif + + } +} diff --git a/extern/carve/lib/intersect_debug.cpp b/extern/carve/lib/intersect_debug.cpp new file mode 100644 index 00000000000..c16854d5655 --- /dev/null +++ b/extern/carve/lib/intersect_debug.cpp @@ -0,0 +1,65 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/csg.hpp> + +#include <list> +#include <set> +#include <iostream> + +#include <algorithm> + +#include "intersect_debug.hpp" + +namespace carve { + namespace csg { + +#if defined(CARVE_DEBUG) + +#define DEBUG_DRAW_FACE_EDGES +#define DEBUG_DRAW_INTERSECTIONS +// #define DEBUG_DRAW_OCTREE +#define DEBUG_DRAW_INTERSECTION_LINE +// #define DEBUG_DRAW_GROUPS +// #define DEBUG_PRINT_RESULT_FACES + + IntersectDebugHooks *g_debug = NULL; + + IntersectDebugHooks *intersect_installDebugHooks(IntersectDebugHooks *hooks) { + IntersectDebugHooks *h = g_debug; + g_debug = hooks; + return h; + } + + bool intersect_debugEnabled() { return true; } + +#else + + IntersectDebugHooks *intersect_installDebugHooks(IntersectDebugHooks * /* hooks */) { + return NULL; + } + + bool intersect_debugEnabled() { return false; } + +#endif + + } +} diff --git a/extern/carve/lib/intersect_debug.hpp b/extern/carve/lib/intersect_debug.hpp new file mode 100644 index 00000000000..be73a7c3dae --- /dev/null +++ b/extern/carve/lib/intersect_debug.hpp @@ -0,0 +1,29 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#include <carve/debug_hooks.hpp> + +#if defined(CARVE_DEBUG) + +#define DEBUG_DRAW_FACE_EDGES +#define DEBUG_DRAW_INTERSECTIONS +// #define DEBUG_DRAW_OCTREE +#define DEBUG_DRAW_INTERSECTION_LINE +// #define DEBUG_DRAW_GROUPS +// #define DEBUG_PRINT_RESULT_FACES + +#endif diff --git a/extern/carve/lib/intersect_face_division.cpp b/extern/carve/lib/intersect_face_division.cpp new file mode 100644 index 00000000000..6f2aa65ed67 --- /dev/null +++ b/extern/carve/lib/intersect_face_division.cpp @@ -0,0 +1,1709 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/csg.hpp> +#include <carve/polyline.hpp> +#include <carve/debug_hooks.hpp> +#include <carve/timing.hpp> +#include <carve/triangulator.hpp> + +#include <list> +#include <set> +#include <iostream> + +#include <algorithm> + +#include "csg_detail.hpp" +#include "csg_data.hpp" + +#include "intersect_common.hpp" + + + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) +void writePLY(const std::string &out_file, const carve::line::PolylineSet *lines, bool ascii); +#endif + + + +namespace { + + + + template<typename T> + void populateVectorFromList(std::list<T> &l, std::vector<T> &v) { + v.clear(); + v.reserve(l.size()); + for (typename std::list<T>::iterator i = l.begin(); i != l.end(); ++i) { + v.push_back(T()); + std::swap(*i, v.back()); + } + l.clear(); + } + + template<typename T> + void populateListFromVector(std::vector<T> &v, std::list<T> &l) { + l.clear(); + for (size_t i = 0; i < v.size(); ++i) { + l.push_back(T()); + std::swap(v[i], l.back()); + } + v.clear(); + } + + + + struct GraphEdge { + GraphEdge *next; + GraphEdge *prev; + GraphEdge *loop_next; + carve::mesh::MeshSet<3>::vertex_t *src; + carve::mesh::MeshSet<3>::vertex_t *tgt; + double ang; + int visited; + + GraphEdge(carve::mesh::MeshSet<3>::vertex_t *_src, carve::mesh::MeshSet<3>::vertex_t *_tgt) : + next(NULL), prev(NULL), loop_next(NULL), + src(_src), tgt(_tgt), + ang(0.0), visited(-1) { + } + }; + + + + struct GraphEdges { + GraphEdge *edges; + carve::geom2d::P2 proj; + + GraphEdges() : edges(NULL), proj() { + } + }; + + + + struct Graph { + typedef std::unordered_map<carve::mesh::MeshSet<3>::vertex_t *, GraphEdges> graph_t; + + graph_t graph; + + Graph() : graph() { + } + + ~Graph() { + int c = 0; + + GraphEdge *edge; + for (graph_t::iterator i = graph.begin(), e = graph.end(); i != e; ++i) { + edge = (*i).second.edges; + while (edge) { + GraphEdge *temp = edge; + ++c; + edge = edge->next; + delete temp; + } + } + + if (c) { + std::cerr << "warning: " + << c + << " edges should have already been removed at graph destruction time" + << std::endl; + } + } + + const carve::geom2d::P2 &projection(carve::mesh::MeshSet<3>::vertex_t *v) const { + graph_t::const_iterator i = graph.find(v); + CARVE_ASSERT(i != graph.end()); + return (*i).second.proj; + } + + void computeProjection(carve::mesh::MeshSet<3>::face_t *face) { + for (graph_t::iterator i = graph.begin(), e = graph.end(); i != e; ++i) { + (*i).second.proj = face->project((*i).first->v); + } + for (graph_t::iterator i = graph.begin(), e = graph.end(); i != e; ++i) { + for (GraphEdge *e = (*i).second.edges; e; e = e->next) { + e->ang = carve::math::ANG(carve::geom2d::atan2(projection(e->tgt) - projection(e->src))); + } + } + } + + void print(std::ostream &out, const carve::csg::VertexIntersections *vi) const { + for (graph_t::const_iterator i = graph.begin(), e = graph.end(); i != e; ++i) { + out << (*i).first << (*i).first->v << '(' << projection((*i).first).x << ',' << projection((*i).first).y << ") :"; + for (const GraphEdge *e = (*i).second.edges; e; e = e->next) { + out << ' ' << e->tgt << e->tgt->v << '(' << projection(e->tgt).x << ',' << projection(e->tgt).y << ')'; + } + out << std::endl; + if (vi) { + carve::csg::VertexIntersections::const_iterator j = vi->find((*i).first); + if (j != vi->end()) { + out << " (int) "; + for (carve::csg::IObjPairSet::const_iterator + k = (*j).second.begin(), ke = (*j).second.end(); k != ke; ++k) { + if ((*k).first < (*k).second) { + out << (*k).first << ".." << (*k).second << "; "; + } + } + out << std::endl; + } + } + } + } + + void addEdge(carve::mesh::MeshSet<3>::vertex_t *v1, carve::mesh::MeshSet<3>::vertex_t *v2) { + GraphEdges &edges = graph[v1]; + GraphEdge *edge = new GraphEdge(v1, v2); + if (edges.edges) edges.edges->prev = edge; + edge->next = edges.edges; + edges.edges = edge; + } + + void removeEdge(GraphEdge *edge) { + if (edge->prev != NULL) { + edge->prev->next = edge->next; + } else { + if (edge->next != NULL) { + GraphEdges &edges = (graph[edge->src]); + edges.edges = edge->next; + } else { + graph.erase(edge->src); + } + } + if (edge->next != NULL) { + edge->next->prev = edge->prev; + } + delete edge; + } + + bool empty() const { + return graph.size() == 0; + } + + GraphEdge *pickStartEdge() { + // Try and find a vertex from which there is only one outbound edge. Won't always succeed. + for (graph_t::iterator i = graph.begin(); i != graph.end(); ++i) { + GraphEdges &ge = i->second; + if (ge.edges->next == NULL) { + return ge.edges; + } + } + return (*graph.begin()).second.edges; + } + + GraphEdge *outboundEdges(carve::mesh::MeshSet<3>::vertex_t *v) { + return graph[v].edges; + } + }; + + + + /** + * \brief Take a set of new edges and split a face based upon those edges. + * + * @param[in] face The face to be split. + * @param[in] edges + * @param[out] face_loops Output list of face loops + * @param[out] hole_loops Output list of hole loops + * @param vi + */ + static void splitFace(carve::mesh::MeshSet<3>::face_t *face, + const carve::csg::V2Set &edges, + std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &face_loops, + std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &hole_loops, + const carve::csg::VertexIntersections & /* vi */) { + Graph graph; + + for (carve::csg::V2Set::const_iterator + i = edges.begin(), e = edges.end(); + i != e; + ++i) { + carve::mesh::MeshSet<3>::vertex_t *v1 = ((*i).first), *v2 = ((*i).second); + if (carve::geom::equal(v1->v, v2->v)) std::cerr << "WARNING! " << v1->v << "==" << v2->v << std::endl; + graph.addEdge(v1, v2); + } + + graph.computeProjection(face); + + while (!graph.empty()) { + GraphEdge *edge; + GraphEdge *start; + start = edge = graph.pickStartEdge(); + + edge->visited = 0; + + int len = 0; + + for (;;) { + double in_ang = M_PI + edge->ang; + if (in_ang > M_TWOPI) in_ang -= M_TWOPI; + + GraphEdge *opts; + GraphEdge *out = NULL; + double best = M_TWOPI + 1.0; + + for (opts = graph.outboundEdges(edge->tgt); opts; opts = opts->next) { + if (opts->tgt == edge->src) { + if (out == NULL && opts->next == NULL) out = opts; + } else { + double out_ang = carve::math::ANG(in_ang - opts->ang); + + if (out == NULL || out_ang < best) { + out = opts; + best = out_ang; + } + } + } + + CARVE_ASSERT(out != NULL); + + edge->loop_next = out; + + if (out->visited >= 0) { + while (start != out) { + GraphEdge *e = start; + start = start->loop_next; + e->loop_next = NULL; + e->visited = -1; + } + len = edge->visited - out->visited + 1; + break; + } + + out->visited = edge->visited + 1; + edge = out; + } + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> loop(len); + std::vector<carve::geom2d::P2> projected(len); + + edge = start; + for (int i = 0; i < len; ++i) { + GraphEdge *next = edge->loop_next; + loop[i] = edge->src; + projected[i] = graph.projection(edge->src); + graph.removeEdge(edge); + edge = next; + } + + CARVE_ASSERT(edge == start); + + if (carve::geom2d::signedArea(projected) < 0) { + face_loops.push_back(std::vector<carve::mesh::MeshSet<3>::vertex_t *>()); + face_loops.back().swap(loop); + } else { + hole_loops.push_back(std::vector<carve::mesh::MeshSet<3>::vertex_t *>()); + hole_loops.back().swap(loop); + } + } + } + + + + /** + * \brief Determine the relationship between a face loop and a hole loop. + * + * Determine whether a face and hole share an edge, or a vertex, + * or do not touch. Find a hole vertex that is not part of the + * face, and a hole,face vertex pair that are coincident, if such + * a pair exists. + * + * @param[in] f A face loop. + * @param[in] f_sort A vector indexing \a f in address order + * @param[in] h A hole loop. + * @param[in] h_sort A vector indexing \a h in address order + * @param[out] f_idx Index of a face vertex that is shared with the hole. + * @param[out] h_idx Index of the hole vertex corresponding to \a f_idx. + * @param[out] unmatched_h_idx Index of a hole vertex that is not part of the face. + * @param[out] shares_vertex Boolean indicating that the face and the hole share a vertex. + * @param[out] shares_edge Boolean indicating that the face and the hole share an edge. + */ + static void compareFaceLoopAndHoleLoop(const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &f, + const std::vector<unsigned> &f_sort, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &h, + const std::vector<unsigned> &h_sort, + unsigned &f_idx, + unsigned &h_idx, + int &unmatched_h_idx, + bool &shares_vertex, + bool &shares_edge) { + const size_t F = f.size(); + const size_t H = h.size(); + + shares_vertex = shares_edge = false; + unmatched_h_idx = -1; + + unsigned I, J; + for (I = J = 0; I < F && J < H;) { + unsigned i = f_sort[I], j = h_sort[J]; + if (f[i] == h[j]) { + shares_vertex = true; + f_idx = i; + h_idx = j; + if (f[(i + F - 1) % F] == h[(j + 1) % H]) { + shares_edge = true; + } + carve::mesh::MeshSet<3>::vertex_t *t = f[i]; + do { ++I; } while (I < F && f[f_sort[I]] == t); + do { ++J; } while (J < H && h[h_sort[J]] == t); + } else if (f[i] < h[j]) { + ++I; + } else { + unmatched_h_idx = j; + ++J; + } + } + if (J < H) { + unmatched_h_idx = h_sort[J]; + } + } + + + + /** + * \brief Compute an embedding for a set of face loops and hole loops. + * + * Because face and hole loops may be contained within each other, + * it must be determined which hole loops are directly contained + * within a face loop. + * + * @param[in] face The face from which these face and hole loops derive. + * @param[in] face_loops + * @param[in] hole_loops + * @param[out] containing_faces A vector which for each hole loop + * lists the indices of the face + * loops it is containined in. + * @param[out] hole_shared_vertices A map from a face,hole pair to + * a shared vertex pair. + */ + static void computeContainment(carve::mesh::MeshSet<3>::face_t *face, + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &face_loops, + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &hole_loops, + std::vector<std::vector<int> > &containing_faces, + std::map<int, std::map<int, std::pair<unsigned, unsigned> > > &hole_shared_vertices) { +#if defined(CARVE_DEBUG) + std::cerr << "input: " + << face_loops.size() << "faces, " + << hole_loops.size() << "holes." + << std::endl; +#endif + + std::vector<std::vector<carve::geom2d::P2> > face_loops_projected, hole_loops_projected; + std::vector<carve::geom::aabb<2> > face_loop_aabb, hole_loop_aabb; + std::vector<std::vector<unsigned> > face_loops_sorted, hole_loops_sorted; + + std::vector<double> face_loop_areas, hole_loop_areas; + + face_loops_projected.resize(face_loops.size()); + face_loops_sorted.resize(face_loops.size()); + face_loop_aabb.resize(face_loops.size()); + face_loop_areas.resize(face_loops.size()); + + hole_loops_projected.resize(hole_loops.size()); + hole_loops_sorted.resize(hole_loops.size()); + hole_loop_aabb.resize(hole_loops.size()); + hole_loop_areas.resize(hole_loops.size()); + + // produce a projection of each face loop onto a 2D plane, and an + // index vector which sorts vertices by address. + for (size_t m = 0; m < face_loops.size(); ++m) { + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &f_loop = (face_loops[m]); + face_loops_projected[m].reserve(f_loop.size()); + face_loops_sorted[m].reserve(f_loop.size()); + for (size_t n = 0; n < f_loop.size(); ++n) { + face_loops_projected[m].push_back(face->project(f_loop[n]->v)); + face_loops_sorted[m].push_back(n); + } + face_loop_areas.push_back(carve::geom2d::signedArea(face_loops_projected[m])); + std::sort(face_loops_sorted[m].begin(), face_loops_sorted[m].end(), + carve::make_index_sort(face_loops[m].begin())); + face_loop_aabb[m].fit(face_loops_projected[m].begin(), face_loops_projected[m].end()); + } + + // produce a projection of each hole loop onto a 2D plane, and an + // index vector which sorts vertices by address. + for (size_t m = 0; m < hole_loops.size(); ++m) { + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &h_loop = (hole_loops[m]); + hole_loops_projected[m].reserve(h_loop.size()); + hole_loops_projected[m].reserve(h_loop.size()); + for (size_t n = 0; n < h_loop.size(); ++n) { + hole_loops_projected[m].push_back(face->project(h_loop[n]->v)); + hole_loops_sorted[m].push_back(n); + } + hole_loop_areas.push_back(carve::geom2d::signedArea(hole_loops_projected[m])); + std::sort(hole_loops_sorted[m].begin(), hole_loops_sorted[m].end(), + carve::make_index_sort(hole_loops[m].begin())); + hole_loop_aabb[m].fit(hole_loops_projected[m].begin(), hole_loops_projected[m].end()); + } + + containing_faces.resize(hole_loops.size()); + + for (unsigned i = 0; i < hole_loops.size(); ++i) { + + for (unsigned j = 0; j < face_loops.size(); ++j) { + if (!face_loop_aabb[j].completelyContains(hole_loop_aabb[i])) { +#if defined(CARVE_DEBUG) + std::cerr << "face: " << j + << " hole: " << i + << " skipped test (aabb fail)" + << std::endl; +#endif + continue; + } + + unsigned f_idx, h_idx; + int unmatched_h_idx; + bool shares_vertex, shares_edge; + compareFaceLoopAndHoleLoop(face_loops[j], + face_loops_sorted[j], + hole_loops[i], + hole_loops_sorted[i], + f_idx, h_idx, + unmatched_h_idx, + shares_vertex, + shares_edge); + +#if defined(CARVE_DEBUG) + std::cerr << "face: " << j + << " hole: " << i + << " shares_vertex: " << shares_vertex + << " shares_edge: " << shares_edge + << std::endl; +#endif + + carve::geom3d::Vector test = hole_loops[i][0]->v; + carve::geom2d::P2 test_p = face->project(test); + + if (shares_vertex) { + hole_shared_vertices[i][j] = std::make_pair(h_idx, f_idx); + // Hole touches face. Should be able to connect it up + // trivially. Still need to record its containment, so that + // the assignment below works. + if (unmatched_h_idx != -1) { +#if defined(CARVE_DEBUG) + std::cerr << "using unmatched vertex: " << unmatched_h_idx << std::endl; +#endif + test = hole_loops[i][unmatched_h_idx]->v; + test_p = face->project(test); + } else { + // XXX: hole shares ALL vertices with face. Pick a point + // internal to the projected poly. + if (shares_edge) { + // Hole shares edge with face => face can't contain hole. + continue; + } + + // XXX: how is this possible? Doesn't share an edge, but + // also doesn't have any vertices that are not in + // common. Degenerate hole? + + // XXX: come up with a test case for this. + CARVE_FAIL("implement me"); + } + } + + + // XXX: use loop area to avoid some point-in-poly tests? Loop + // area is faster, but not sure which is more robust. + if (carve::geom2d::pointInPolySimple(face_loops_projected[j], test_p)) { +#if defined(CARVE_DEBUG) + std::cerr << "contains: " << i << " - " << j << std::endl; +#endif + containing_faces[i].push_back(j); + } else { +#if defined(CARVE_DEBUG) + std::cerr << "does not contain: " << i << " - " << j << std::endl; +#endif + } + } + +#if defined(CARVE_DEBUG) + if (containing_faces[i].size() == 0) { + //HOOK(drawFaceLoopWireframe(hole_loops[i], face->normal, 1.0, 0.0, 0.0, 1.0);); + std::cerr << "hole loop: "; + for (unsigned j = 0; j < hole_loops[i].size(); ++j) { + std::cerr << " " << hole_loops[i][j] << ":" << hole_loops[i][j]->v; + } + std::cerr << std::endl; + for (unsigned j = 0; j < face_loops.size(); ++j) { + //HOOK(drawFaceLoopWireframe(face_loops[j], face->normal, 0.0, 1.0, 0.0, 1.0);); + } + } +#endif + + // CARVE_ASSERT(containing_faces[i].size() >= 1); + } + } + + + + /** + * \brief Merge face loops and hole loops to produce a set of face loops without holes. + * + * @param[in] face The face from which these face loops derive. + * @param[in,out] f_loops A list of face loops. + * @param[in] h_loops A list of hole loops to be incorporated into face loops. + */ + static void mergeFacesAndHoles(carve::mesh::MeshSet<3>::face_t *face, + std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &f_loops, + std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &h_loops, + carve::csg::CSG::Hooks & /* hooks */) { + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > face_loops; + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > hole_loops; + + std::vector<std::vector<int> > containing_faces; + std::map<int, std::map<int, std::pair<unsigned, unsigned> > > hole_shared_vertices; + + { + // move input face and hole loops to temp vectors. + size_t m; + face_loops.resize(f_loops.size()); + m = 0; + for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::iterator + i = f_loops.begin(), ie = f_loops.end(); + i != ie; + ++i, ++m) { + face_loops[m].swap((*i)); + } + + hole_loops.resize(h_loops.size()); + m = 0; + for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::iterator + i = h_loops.begin(), ie = h_loops.end(); + i != ie; + ++i, ++m) { + hole_loops[m].swap((*i)); + } + f_loops.clear(); + h_loops.clear(); + } + + // work out the embedding of holes and faces. + computeContainment(face, face_loops, hole_loops, containing_faces, hole_shared_vertices); + + int unassigned = (int)hole_loops.size(); + + std::vector<std::vector<int> > face_holes; + face_holes.resize(face_loops.size()); + + for (unsigned i = 0; i < containing_faces.size(); ++i) { + if (containing_faces[i].size() == 0) { + std::map<int, std::map<int, std::pair<unsigned, unsigned> > >::iterator it = hole_shared_vertices.find(i); + if (it != hole_shared_vertices.end()) { + std::map<int, std::pair<unsigned, unsigned> >::iterator it2 = (*it).second.begin(); + int f = (*it2).first; + unsigned h_idx = (*it2).second.first; + unsigned f_idx = (*it2).second.second; + + // patch the hole into the face directly. because + // f_loop[f_idx] == h_loop[h_idx], we don't need to + // duplicate the f_loop vertex. + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &f_loop = face_loops[f]; + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &h_loop = hole_loops[i]; + + f_loop.insert(f_loop.begin() + f_idx + 1, h_loop.size(), NULL); + + unsigned p = f_idx + 1; + for (unsigned a = h_idx + 1; a < h_loop.size(); ++a, ++p) { + f_loop[p] = h_loop[a]; + } + for (unsigned a = 0; a <= h_idx; ++a, ++p) { + f_loop[p] = h_loop[a]; + } + +#if defined(CARVE_DEBUG) + std::cerr << "hook face " << f << " to hole " << i << "(vertex)" << std::endl; +#endif + } else { + std::cerr << "uncontained hole loop does not share vertices with any face loop!" << std::endl; + } + unassigned--; + } + } + + + // work out which holes are directly contained within which faces. + while (unassigned) { + std::set<int> removed; + + for (unsigned i = 0; i < containing_faces.size(); ++i) { + if (containing_faces[i].size() == 1) { + int f = containing_faces[i][0]; + face_holes[f].push_back(i); +#if defined(CARVE_DEBUG) + std::cerr << "hook face " << f << " to hole " << i << std::endl; +#endif + removed.insert(f); + unassigned--; + } + } + for (std::set<int>::iterator f = removed.begin(); f != removed.end(); ++f) { + for (unsigned i = 0; i < containing_faces.size(); ++i) { + containing_faces[i].erase(std::remove(containing_faces[i].begin(), + containing_faces[i].end(), + *f), + containing_faces[i].end()); + } + } + } + +#if 0 + // use old templated projection code to patch holes into faces. + for (unsigned i = 0; i < face_loops.size(); ++i) { + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > face_hole_loops; + face_hole_loops.resize(face_holes[i].size()); + for (unsigned j = 0; j < face_holes[i].size(); ++j) { + face_hole_loops[j].swap(hole_loops[face_holes[i][j]]); + } + if (face_hole_loops.size()) { + + f_loops.push_back(carve::triangulate::incorporateHolesIntoPolygon( + carve::mesh::MeshSet<3>::face_t::projection_mapping(face->project), + face_loops[i], + face_hole_loops)); + } else { + f_loops.push_back(face_loops[i]); + } + } + +#else + // use new 2d-only hole patching code. + for (size_t i = 0; i < face_loops.size(); ++i) { + if (!face_holes[i].size()) { + f_loops.push_back(face_loops[i]); + continue; + } + + std::vector<std::vector<carve::geom2d::P2> > projected_poly; + projected_poly.resize(face_holes[i].size() + 1); + projected_poly[0].reserve(face_loops[i].size()); + for (size_t j = 0; j < face_loops[i].size(); ++j) { + projected_poly[0].push_back(face->project(face_loops[i][j]->v)); + } + for (size_t j = 0; j < face_holes[i].size(); ++j) { + projected_poly[j+1].reserve(hole_loops[face_holes[i][j]].size()); + for (size_t k = 0; k < hole_loops[face_holes[i][j]].size(); ++k) { + projected_poly[j+1].push_back(face->project(hole_loops[face_holes[i][j]][k]->v)); + } + } + + std::vector<std::pair<size_t, size_t> > result = carve::triangulate::incorporateHolesIntoPolygon(projected_poly); + + f_loops.push_back(std::vector<carve::mesh::MeshSet<3>::vertex_t *>()); + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &out = f_loops.back(); + out.reserve(result.size()); + for (size_t j = 0; j < result.size(); ++j) { + if (result[j].first == 0) { + out.push_back(face_loops[i][result[j].second]); + } else { + out.push_back(hole_loops[face_holes[i][result[j].first-1]][result[j].second]); + } + } + } +#endif + } + + + + /** + * \brief Assemble the base loop for a face. + * + * The base loop is the original face loop, including vertices + * created by intersections crossing any of its edges. + * + * @param[in] face The face to process. + * @param[in] vmap + * @param[in] face_split_edges + * @param[in] divided_edges A mapping from edge pointer to sets of + * ordered vertices corrsponding to the intersection points + * on that edge. + * @param[out] base_loop A vector of the vertices of the base loop. + */ + static void assembleBaseLoop(carve::mesh::MeshSet<3>::face_t *face, + const carve::csg::detail::Data &data, + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &base_loop) { + base_loop.clear(); + + // XXX: assumes that face->edges is in the same order as + // face->vertices. (Which it is) + carve::mesh::MeshSet<3>::edge_t *e = face->edge; + do { + base_loop.push_back(carve::csg::map_vertex(data.vmap, e->vert)); + + carve::csg::detail::EVVMap::const_iterator ev = data.divided_edges.find(e); + + if (ev != data.divided_edges.end()) { + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &ev_vec = ((*ev).second); + + for (size_t k = 0, ke = ev_vec.size(); k < ke;) { + base_loop.push_back(ev_vec[k++]); + } + } + e = e->next; + } while (e != face->edge); + } + + + + // the crossing_data structure holds temporary information regarding + // paths, and their relationship to the loop of edges that forms the + // face perimeter. + struct crossing_data { + std::vector<carve::mesh::MeshSet<3>::vertex_t *> *path; + size_t edge_idx[2]; + + crossing_data(std::vector<carve::mesh::MeshSet<3>::vertex_t *> *p, size_t e1, size_t e2) : path(p) { + edge_idx[0] = e1; edge_idx[1] = e2; + } + + bool operator<(const crossing_data &c) const { + // the sort order for paths is in order of increasing initial + // position on the edge loop, but decreasing final position. + return edge_idx[0] < c.edge_idx[0] || (edge_idx[0] == c.edge_idx[0] && edge_idx[1] > c.edge_idx[1]); + } + }; + + + + bool processCrossingEdges(carve::mesh::MeshSet<3>::face_t *face, + const carve::csg::VertexIntersections &vertex_intersections, + carve::csg::CSG::Hooks &hooks, + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &base_loop, + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &paths, + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &loops, + std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &face_loops_out) { + const size_t N = base_loop.size(); + std::vector<crossing_data> endpoint_indices; + + endpoint_indices.reserve(paths.size()); + + for (size_t i = 0; i < paths.size(); ++i) { + endpoint_indices.push_back(crossing_data(&paths[i], N, N)); + } + + // locate endpoints of paths on the base loop. + for (size_t i = 0; i < N; ++i) { + for (size_t j = 0; j < paths.size(); ++j) { + // test beginning of path. + if (paths[j].front() == base_loop[i]) { + if (endpoint_indices[j].edge_idx[0] == N) { + endpoint_indices[j].edge_idx[0] = i; + } else { + // there is a duplicated vertex in the face perimeter. The + // path might attach to either of the duplicate instances + // so we have to work out which is the right one to attach + // to. We assume it's the index currently being examined, + // if the path heads in a direction that's internal to the + // angle made by the prior and next edges of the face + // perimeter. Otherwise, leave it as the currently + // selected index (until another duplicate is found, if it + // exists, and is tested). + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &p = *endpoint_indices[j].path; + const size_t pN = p.size(); + + carve::mesh::MeshSet<3>::vertex_t *a, *b, *c; + a = base_loop[(i+N-1)%N]; + b = base_loop[i]; + c = base_loop[(i+1)%N]; + + carve::mesh::MeshSet<3>::vertex_t *adj = (p[0] == base_loop[i]) ? p[1] : p[pN-2]; + + if (carve::geom2d::internalToAngle(face->project(c->v), + face->project(b->v), + face->project(a->v), + face->project(adj->v))) { + endpoint_indices[j].edge_idx[0] = i; + } + } + } + + // test end of path. + if (paths[j].back() == base_loop[i]) { + if (endpoint_indices[j].edge_idx[1] == N) { + endpoint_indices[j].edge_idx[1] = i; + } else { + // Work out which of the duplicated vertices is the right + // one to attach to, as above. + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &p = *endpoint_indices[j].path; + const size_t pN = p.size(); + + carve::mesh::MeshSet<3>::vertex_t *a, *b, *c; + a = base_loop[(i+N-1)%N]; + b = base_loop[i]; + c = base_loop[(i+1)%N]; + + carve::mesh::MeshSet<3>::vertex_t *adj = (p[0] == base_loop[i]) ? p[1] : p[pN-2]; + + if (carve::geom2d::internalToAngle(face->project(c->v), + face->project(b->v), + face->project(a->v), + face->project(adj->v))) { + endpoint_indices[j].edge_idx[1] = i; + } + } + } + } + } + +#if defined(CARVE_DEBUG) + std::cerr << "### N: " << N << std::endl; + for (size_t i = 0; i < paths.size(); ++i) { + std::cerr << "### path: " << i << " endpoints: " << endpoint_indices[i].edge_idx[0] << " - " << endpoint_indices[i].edge_idx[1] << std::endl; + } +#endif + + + // divide paths up into those that connect to the base loop in two + // places (cross), and those that do not (noncross). + std::vector<crossing_data> cross, noncross; + cross.reserve(endpoint_indices.size() + 1); + noncross.reserve(endpoint_indices.size()); + + for (size_t i = 0; i < endpoint_indices.size(); ++i) { +#if defined(CARVE_DEBUG) + std::cerr << "### orienting path: " << i << " endpoints: " << endpoint_indices[i].edge_idx[0] << " - " << endpoint_indices[i].edge_idx[1] << std::endl; +#endif + if (endpoint_indices[i].edge_idx[0] != N && endpoint_indices[i].edge_idx[1] != N) { + // Orient each path correctly. Paths should progress from + // smaller perimeter index to larger, but if the path starts + // and ends at the same perimeter index, then the decision + // needs to be made based upon area. + if (endpoint_indices[i].edge_idx[0] == endpoint_indices[i].edge_idx[1]) { + // The path forms a loop that starts and ends at the same + // vertex of the perimeter. In this case, we need to orient + // the path so that the constructed loop has the right + // signed area. + double area = carve::geom2d::signedArea(endpoint_indices[i].path->begin() + 1, + endpoint_indices[i].path->end(), + carve::mesh::MeshSet<3>::face_t::projection_mapping(face->project)); + std::cerr << "HITS THIS CODE - area=" << area << std::endl; + if (area < 0) { + // XXX: Create test case to check that this is the correct sign for the area. + std::reverse(endpoint_indices[i].path->begin(), endpoint_indices[i].path->end()); + } + } else { + if (endpoint_indices[i].edge_idx[0] > endpoint_indices[i].edge_idx[1]) { + std::swap(endpoint_indices[i].edge_idx[0], endpoint_indices[i].edge_idx[1]); + std::reverse(endpoint_indices[i].path->begin(), endpoint_indices[i].path->end()); + } + } + } + + if (endpoint_indices[i].edge_idx[0] != N && + endpoint_indices[i].edge_idx[1] != N && + endpoint_indices[i].edge_idx[0] != endpoint_indices[i].edge_idx[1]) { + cross.push_back(endpoint_indices[i]); + } else { + noncross.push_back(endpoint_indices[i]); + } + } + + // add a temporary crossing path that connects the beginning and the + // end of the base loop. this stops us from needing special case + // code to handle the left over loop after all the other crossing + // paths are considered. + std::vector<carve::mesh::MeshSet<3>::vertex_t *> base_loop_temp_path; + base_loop_temp_path.reserve(2); + base_loop_temp_path.push_back(base_loop.front()); + base_loop_temp_path.push_back(base_loop.back()); + + cross.push_back(crossing_data(&base_loop_temp_path, 0, base_loop.size() - 1)); +#if defined(CARVE_DEBUG) + std::cerr << "### crossing edge count (with sentinel): " << cross.size() << std::endl; +#endif + + // sort paths by increasing beginning point and decreasing ending point. + std::sort(cross.begin(), cross.end()); + std::sort(noncross.begin(), noncross.end()); + + // divide up the base loop based upon crossing paths. + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > divided_base_loop; + divided_base_loop.reserve(cross.size()); + std::vector<carve::mesh::MeshSet<3>::vertex_t *> out; + + for (size_t i = 0; i < cross.size(); ++i) { + size_t j; + for (j = i + 1; + j < cross.size() && + cross[i].edge_idx[0] == cross[j].edge_idx[0] && + cross[i].edge_idx[1] == cross[j].edge_idx[1]; + ++j) {} + if (j - i >= 2) { + // when there are multiple paths that begin and end at the + // same point, they need to be ordered so that the constructed + // loops have the right orientation. this means that the loop + // made by taking path(i+1) forward, then path(i) backward + // needs to have negative area. this combined area is equal to + // the area of path(i+1) minus the area of path(i). in turn + // this means that the loop made by path path(i+1) alone has + // to have smaller signed area than loop made by path(i). + // thus, we sort paths in order of decreasing area. + + std::vector<std::pair<double, std::vector<carve::mesh::MeshSet<3>::vertex_t *> *> > order; + order.reserve(j - i); + for (size_t k = i; k < j; ++k) { + double area = carve::geom2d::signedArea(cross[k].path->begin(), + cross[k].path->end(), + carve::mesh::MeshSet<3>::face_t::projection_mapping(face->project)); +#if defined(CARVE_DEBUG) + std::cerr << "### k=" << k << " area=" << area << std::endl; +#endif + order.push_back(std::make_pair(-area, cross[k].path)); + } + std::sort(order.begin(), order.end()); + for (size_t k = i; k < j; ++k) { + cross[k].path = order[k-i].second; +#if defined(CARVE_DEBUG) + std::cerr << "### post-sort k=" << k << " cross[k].path->size()=" << cross[k].path->size() << std::endl; +#endif + } + } + } + + for (size_t i = 0; i < cross.size(); ++i) { +#if defined(CARVE_DEBUG) + std::cerr << "### i=" << i << " working on edge: " << cross[i].edge_idx[0] << " - " << cross[i].edge_idx[1] << std::endl; +#endif + size_t e1_0 = cross[i].edge_idx[0]; + size_t e1_1 = cross[i].edge_idx[1]; + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &p1 = *cross[i].path; +#if defined(CARVE_DEBUG) + std::cerr << "### path size = " << p1.size() << std::endl; +#endif + + out.clear(); + + if (i < cross.size() - 1 && + cross[i+1].edge_idx[1] <= cross[i].edge_idx[1]) { +#if defined(CARVE_DEBUG) + std::cerr << "### complex case" << std::endl; +#endif + // complex case. crossing path with other crossing paths embedded within. + size_t pos = e1_0; + + size_t skip = i+1; + + while (pos != e1_1) { + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &p2 = *cross[skip].path; + size_t e2_0 = cross[skip].edge_idx[0]; + size_t e2_1 = cross[skip].edge_idx[1]; + + // copy up to the beginning of the next path. + std::copy(base_loop.begin() + pos, base_loop.begin() + e2_0, std::back_inserter(out)); + + CARVE_ASSERT(base_loop[e2_0] == p2[0]); + // copy the next path in the right direction. + std::copy(p2.begin(), p2.end() - 1, std::back_inserter(out)); + + // move to the position of the end of the path. + pos = e2_1; + + // advance to the next hit path. + do { + ++skip; + } while(skip != cross.size() && cross[skip].edge_idx[0] < e2_1); + + if (skip == cross.size()) break; + + // if the next hit path is past the start point of the current path, we're done. + if (cross[skip].edge_idx[0] >= e1_1) break; + } + + // copy up to the end of the path. + std::copy(base_loop.begin() + pos, base_loop.begin() + e1_1, std::back_inserter(out)); + + CARVE_ASSERT(base_loop[e1_1] == p1.back()); + std::copy(p1.rbegin(), p1.rend() - 1, std::back_inserter(out)); + } else { + size_t loop_size = (e1_1 - e1_0) + (p1.size() - 1); + out.reserve(loop_size); + + std::copy(base_loop.begin() + e1_0, base_loop.begin() + e1_1, std::back_inserter(out)); + std::copy(p1.rbegin(), p1.rend() - 1, std::back_inserter(out)); + + CARVE_ASSERT(out.size() == loop_size); + } + divided_base_loop.push_back(out); + +#if defined(CARVE_DEBUG) + { + std::vector<carve::geom2d::P2> projected; + projected.reserve(out.size()); + for (size_t n = 0; n < out.size(); ++n) { + projected.push_back(face->project(out[n]->v)); + } + + double A = carve::geom2d::signedArea(projected); + std::cerr << "### out area=" << A << std::endl; + CARVE_ASSERT(A <= 0); + } +#endif + } + + if (!noncross.size() && !loops.size()) { + populateListFromVector(divided_base_loop, face_loops_out); + return true; + } + + // for each divided base loop, work out which noncrossing paths and + // loops are part of it. use the old algorithm to combine these into + // the divided base loop. if none, the divided base loop is just + // output. + std::vector<std::vector<carve::geom2d::P2> > proj; + std::vector<carve::geom::aabb<2> > proj_aabb; + proj.resize(divided_base_loop.size()); + proj_aabb.resize(divided_base_loop.size()); + + // calculate an aabb for each divided base loop, to avoid expensive + // point-in-poly tests. + for (size_t i = 0; i < divided_base_loop.size(); ++i) { + proj[i].reserve(divided_base_loop[i].size()); + for (size_t j = 0; j < divided_base_loop[i].size(); ++j) { + proj[i].push_back(face->project(divided_base_loop[i][j]->v)); + } + proj_aabb[i].fit(proj[i].begin(), proj[i].end()); + } + + for (size_t i = 0; i < divided_base_loop.size(); ++i) { + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> *> inc; + carve::geom2d::P2 test; + + // for each noncrossing path, choose an endpoint that isn't on the + // base loop as a test point. + for (size_t j = 0; j < noncross.size(); ++j) { + if (noncross[j].edge_idx[0] < N) { + if (noncross[j].path->front() == base_loop[noncross[j].edge_idx[0]]) { + // noncrossing paths may be loops that run from the edge, back to the same vertex. + if (noncross[j].path->front() == noncross[j].path->back()) { + CARVE_ASSERT(noncross[j].path->size() > 2); + test = face->project((*noncross[j].path)[1]->v); + } else { + test = face->project(noncross[j].path->back()->v); + } + } else { + test = face->project(noncross[j].path->front()->v); + } + } else { + test = face->project(noncross[j].path->front()->v); + } + + if (proj_aabb[i].intersects(test) && + carve::geom2d::pointInPoly(proj[i], test).iclass != carve::POINT_OUT) { + inc.push_back(noncross[j].path); + } + } + + // for each loop, just test with any point. + for (size_t j = 0; j < loops.size(); ++j) { + test = face->project(loops[j].front()->v); + + if (proj_aabb[i].intersects(test) && + carve::geom2d::pointInPoly(proj[i], test).iclass != carve::POINT_OUT) { + inc.push_back(&loops[j]); + } + } + +#if defined(CARVE_DEBUG) + std::cerr << "### divided base loop:" << i << " inc.size()=" << inc.size() << std::endl; + std::cerr << "### inc = ["; + for (size_t j = 0; j < inc.size(); ++j) { + std::cerr << " " << inc[j]; + } + std::cerr << " ]" << std::endl; +#endif + + if (inc.size()) { + carve::csg::V2Set face_edges; + + for (size_t j = 0; j < divided_base_loop[i].size() - 1; ++j) { + face_edges.insert(std::make_pair(divided_base_loop[i][j], + divided_base_loop[i][j+1])); + } + + face_edges.insert(std::make_pair(divided_base_loop[i].back(), + divided_base_loop[i].front())); + + for (size_t j = 0; j < inc.size(); ++j) { + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &path = *inc[j]; + for (size_t k = 0; k < path.size() - 1; ++k) { + face_edges.insert(std::make_pair(path[k], path[k+1])); + face_edges.insert(std::make_pair(path[k+1], path[k])); + } + } + + std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > face_loops; + std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > hole_loops; + + splitFace(face, face_edges, face_loops, hole_loops, vertex_intersections); + + if (hole_loops.size()) { + mergeFacesAndHoles(face, face_loops, hole_loops, hooks); + } + std::copy(face_loops.begin(), face_loops.end(), std::back_inserter(face_loops_out)); + } else { + face_loops_out.push_back(divided_base_loop[i]); + } + } + return true; + } + + + + void composeEdgesIntoPaths(const carve::csg::V2Set &edges, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &extra_endpoints, + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &paths, + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &loops) { + using namespace carve::csg; + + detail::VVSMap vertex_graph; + detail::VSet endpoints; + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> path; + + std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > temp; + + // build graph from edges. + for (V2Set::const_iterator i = edges.begin(); i != edges.end(); ++i) { +#if defined(CARVE_DEBUG) + std::cerr << "### edge: " << (*i).first << " - " << (*i).second << std::endl; +#endif + vertex_graph[(*i).first].insert((*i).second); + vertex_graph[(*i).second].insert((*i).first); + } + + // find the endpoints in the graph. + // every vertex with number of incident edges != 2 is an endpoint. + for (detail::VVSMap::const_iterator i = vertex_graph.begin(); i != vertex_graph.end(); ++i) { + if ((*i).second.size() != 2) { +#if defined(CARVE_DEBUG) + std::cerr << "### endpoint: " << (*i).first << std::endl; +#endif + endpoints.insert((*i).first); + } + } + + // every vertex on the perimeter of the face is also an endpoint. + for (size_t i = 0; i < extra_endpoints.size(); ++i) { + if (vertex_graph.find(extra_endpoints[i]) != vertex_graph.end()) { +#if defined(CARVE_DEBUG) + std::cerr << "### extra endpoint: " << extra_endpoints[i] << std::endl; +#endif + endpoints.insert(extra_endpoints[i]); + } + } + + while (endpoints.size()) { + carve::mesh::MeshSet<3>::vertex_t *v = *endpoints.begin(); + detail::VVSMap::iterator p = vertex_graph.find(v); + if (p == vertex_graph.end()) { + endpoints.erase(endpoints.begin()); + continue; + } + + path.clear(); + path.push_back(v); + + for (;;) { + CARVE_ASSERT(p != vertex_graph.end()); + + // pick a connected vertex to move to. + if ((*p).second.size() == 0) break; + + carve::mesh::MeshSet<3>::vertex_t *n = *((*p).second.begin()); + detail::VVSMap::iterator q = vertex_graph.find(n); + + // remove the link. + (*p).second.erase(n); + (*q).second.erase(v); + + // move on. + v = n; + path.push_back(v); + + if ((*p).second.size() == 0) vertex_graph.erase(p); + if ((*q).second.size() == 0) { + vertex_graph.erase(q); + q = vertex_graph.end(); + } + + p = q; + + if (v == path[0] || p == vertex_graph.end() || endpoints.find(v) != endpoints.end()) break; + } + CARVE_ASSERT(endpoints.find(path.back()) != endpoints.end()); + + temp.push_back(path); + } + + populateVectorFromList(temp, paths); + temp.clear(); + + // now only loops should remain in the graph. + while (vertex_graph.size()) { + detail::VVSMap::iterator p = vertex_graph.begin(); + carve::mesh::MeshSet<3>::vertex_t *v = (*p).first; + CARVE_ASSERT((*p).second.size() == 2); + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> path; + path.clear(); + path.push_back(v); + + for (;;) { + CARVE_ASSERT(p != vertex_graph.end()); + // pick a connected vertex to move to. + + carve::mesh::MeshSet<3>::vertex_t *n = *((*p).second.begin()); + detail::VVSMap::iterator q = vertex_graph.find(n); + + // remove the link. + (*p).second.erase(n); + (*q).second.erase(v); + + // move on. + v = n; + path.push_back(v); + + if ((*p).second.size() == 0) vertex_graph.erase(p); + if ((*q).second.size() == 0) vertex_graph.erase(q); + + p = q; + + if (v == path[0]) break; + } + + temp.push_back(path); + } + populateVectorFromList(temp, loops); + } + + + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + void dumpFacesAndHoles(const std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &face_loops, + const std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &hole_loops) { + std::map<carve::mesh::MeshSet<3>::vertex_t *, size_t> v_included; + + for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator + i = face_loops.begin(); i != face_loops.end(); ++i) { + for (size_t j = 0; j < (*i).size(); ++j) { + if (v_included.find((*i)[j]) == v_included.end()) { + size_t &p = v_included[(*i)[j]]; + p = v_included.size() - 1; + } + } + } + + for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator + i = hole_loops.begin(); i != hole_loops.end(); ++i) { + for (size_t j = 0; j < (*i).size(); ++j) { + if (v_included.find((*i)[j]) == v_included.end()) { + size_t &p = v_included[(*i)[j]]; + p = v_included.size() - 1; + } + } + } + + carve::line::PolylineSet fh; + fh.vertices.resize(v_included.size()); + for (std::map<carve::mesh::MeshSet<3>::vertex_t *, size_t>::const_iterator + i = v_included.begin(); i != v_included.end(); ++i) { + fh.vertices[(*i).second].v = (*i).first->v; + } + + { + std::vector<size_t> connected; + for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator + i = face_loops.begin(); i != face_loops.end(); ++i) { + connected.clear(); + for (size_t j = 0; j < (*i).size(); ++j) { + connected.push_back(v_included[(*i)[j]]); + } + fh.addPolyline(true, connected.begin(), connected.end()); + } + for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator + i = hole_loops.begin(); i != hole_loops.end(); ++i) { + connected.clear(); + for (size_t j = 0; j < (*i).size(); ++j) { + connected.push_back(v_included[(*i)[j]]); + } + fh.addPolyline(true, connected.begin(), connected.end()); + } + } + + std::string out("/tmp/hole_merge.ply"); + ::writePLY(out, &fh, true); + } +#endif + + + + template<typename T> + std::string ptrstr(const T *ptr) { + std::ostringstream s; + s << ptr; + return s.str().substr(1); + } + + void dumpAsGraph(carve::mesh::MeshSet<3>::face_t *face, + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &base_loop, + const carve::csg::V2Set &face_edges, + const carve::csg::V2Set &split_edges) { + std::map<carve::mesh::MeshSet<3>::vertex_t *, carve::geom2d::P2> proj; + + for (size_t i = 0; i < base_loop.size(); ++i) { + proj[base_loop[i]] = face->project(base_loop[i]->v); + } + for (carve::csg::V2Set::const_iterator i = split_edges.begin(); i != split_edges.end(); ++i) { + proj[(*i).first] = face->project((*i).first->v); + proj[(*i).second] = face->project((*i).second->v); + } + + { + carve::geom2d::P2 lo, hi; + std::map<carve::mesh::MeshSet<3>::vertex_t *, carve::geom2d::P2>::iterator i; + i = proj.begin(); + lo = hi = (*i).second; + for (; i != proj.end(); ++i) { + lo.x = std::min(lo.x, (*i).second.x); lo.y = std::min(lo.y, (*i).second.y); + hi.x = std::max(hi.x, (*i).second.x); hi.y = std::max(hi.y, (*i).second.y); + } + for (i = proj.begin(); i != proj.end(); ++i) { + (*i).second.x = ((*i).second.x - lo.x) / (hi.x - lo.x) * 10; + (*i).second.y = ((*i).second.y - lo.y) / (hi.y - lo.y) * 10; + } + } + + std::cerr << "graph G {\nnode [shape=circle,style=filled,fixedsize=true,width=\".1\",height=\".1\"];\nedge [len=4]\n"; + for (std::map<carve::mesh::MeshSet<3>::vertex_t *, carve::geom2d::P2>::iterator i = proj.begin(); i != proj.end(); ++i) { + std::cerr << " " << ptrstr((*i).first) << " [pos=\"" << (*i).second.x << "," << (*i).second.y << "!\"];\n"; + } + for (carve::csg::V2Set::const_iterator i = face_edges.begin(); i != face_edges.end(); ++i) { + std::cerr << " " << ptrstr((*i).first) << " -- " << ptrstr((*i).second) << ";\n"; + } + for (carve::csg::V2Set::const_iterator i = split_edges.begin(); i != split_edges.end(); ++i) { + std::cerr << " " << ptrstr((*i).first) << " -- " << ptrstr((*i).second) << " [color=\"blue\"];\n"; + } + std::cerr << "};\n"; + } + + void generateOneFaceLoop(carve::mesh::MeshSet<3>::face_t *face, + const carve::csg::detail::Data &data, + const carve::csg::VertexIntersections &vertex_intersections, + carve::csg::CSG::Hooks &hooks, + std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > &face_loops) { + using namespace carve::csg; + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> base_loop; + std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > hole_loops; + + assembleBaseLoop(face, data, base_loop); + + detail::FV2SMap::const_iterator fse_iter = data.face_split_edges.find(face); + + face_loops.clear(); + + if (fse_iter == data.face_split_edges.end()) { + // simple case: input face is output face (possibly with the + // addition of vertices at intersections). + face_loops.push_back(base_loop); + return; + } + + // complex case: input face is split into multiple output faces. + V2Set face_edges; + + for (size_t j = 0, je = base_loop.size() - 1; j < je; ++j) { + face_edges.insert(std::make_pair(base_loop[j], base_loop[j + 1])); + } + face_edges.insert(std::make_pair(base_loop.back(), base_loop[0])); + + // collect the split edges (as long as they're not on the perimeter) + const detail::FV2SMap::mapped_type &fse = ((*fse_iter).second); + + // split_edges contains all of the edges created by intersections + // that aren't part of the perimeter of the face. + V2Set split_edges; + + for (detail::FV2SMap::mapped_type::const_iterator + j = fse.begin(), je = fse.end(); + j != je; + ++j) { + carve::mesh::MeshSet<3>::vertex_t *v1 = ((*j).first), *v2 = ((*j).second); + + if (face_edges.find(std::make_pair(v1, v2)) == face_edges.end() && + face_edges.find(std::make_pair(v2, v1)) == face_edges.end()) { + + split_edges.insert(ordered_edge(v1, v2)); + } + } + + // face is unsplit. + if (!split_edges.size()) { + face_loops.push_back(base_loop); + return; + } + +#if defined(CARVE_DEBUG) + dumpAsGraph(face, base_loop, face_edges, split_edges); +#endif + +#if 0 + // old face splitting method. + for (V2Set::const_iterator i = split_edges.begin(); i != split_edges.end(); ++i) { + face_edges.insert(std::make_pair((*i).first, (*i).second)); + face_edges.insert(std::make_pair((*i).second, (*i).first)); + } + splitFace(face, face_edges, face_loops, hole_loops, vertex_intersections); + + if (hole_loops.size()) { + mergeFacesAndHoles(face, face_loops, hole_loops, hooks); + } + return; +#endif + +#if defined(CARVE_DEBUG) + std::cerr << "### split_edges.size(): " << split_edges.size() << std::endl; +#endif + if (split_edges.size() == 1) { + // handle the common case of a face that's split by a single edge. + carve::mesh::MeshSet<3>::vertex_t *v1 = split_edges.begin()->first; + carve::mesh::MeshSet<3>::vertex_t *v2 = split_edges.begin()->second; + + std::vector<carve::mesh::MeshSet<3>::vertex_t *>::iterator vi1 = std::find(base_loop.begin(), base_loop.end(), v1); + std::vector<carve::mesh::MeshSet<3>::vertex_t *>::iterator vi2 = std::find(base_loop.begin(), base_loop.end(), v2); + + if (vi1 != base_loop.end() && vi2 != base_loop.end()) { + // this is an inserted edge that connects two points on the base loop. nice and simple. + if (vi2 < vi1) std::swap(vi1, vi2); + + size_t loop1_size = vi2 - vi1 + 1; + size_t loop2_size = base_loop.size() + 2 - loop1_size; + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> l1; + std::vector<carve::mesh::MeshSet<3>::vertex_t *> l2; + + l1.reserve(loop1_size); + l2.reserve(loop2_size); + + std::copy(vi1, vi2+1, std::back_inserter(l1)); + std::copy(vi2, base_loop.end(), std::back_inserter(l2)); + std::copy(base_loop.begin(), vi1+1, std::back_inserter(l2)); + + CARVE_ASSERT(l1.size() == loop1_size); + CARVE_ASSERT(l2.size() == loop2_size); + + face_loops.push_back(l1); + face_loops.push_back(l2); + + return; + } + } + + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > paths; + std::vector<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > loops; + + // Take the split edges and compose them into a set of paths and + // loops. Loops are edge paths that do not touch the boundary, or + // any other path or loop - they are holes cut out of the centre + // of the face. Paths are made up of all the other edge segments, + // and start and end at the face perimeter, or where they meet + // another path (sometimes both cases will be true). + composeEdgesIntoPaths(split_edges, base_loop, paths, loops); + +#if defined(CARVE_DEBUG) + std::cerr << "### paths.size(): " << paths.size() << std::endl; + std::cerr << "### loops.size(): " << loops.size() << std::endl; +#endif + + if (!paths.size()) { + // Loops found by composeEdgesIntoPaths() can't touch the + // boundary, or each other, so we can deal with the no paths + // case simply. The hole loops are the loops produced by + // composeEdgesIntoPaths() oriented so that their signed area + // wrt. the face is negative. The face loops are the base loop + // plus the hole loops, reversed. + face_loops.push_back(base_loop); + + for (size_t i = 0; i < loops.size(); ++i) { + hole_loops.push_back(std::vector<carve::mesh::MeshSet<3>::vertex_t *>()); + hole_loops.back().reserve(loops[i].size()-1); + std::copy(loops[i].begin(), loops[i].end()-1, std::back_inserter(hole_loops.back())); + + face_loops.push_back(std::vector<carve::mesh::MeshSet<3>::vertex_t *>()); + face_loops.back().reserve(loops[i].size()-1); + std::copy(loops[i].rbegin()+1, loops[i].rend(), std::back_inserter(face_loops.back())); + + std::vector<carve::geom2d::P2> projected; + projected.reserve(face_loops.back().size()); + for (size_t i = 0; i < face_loops.back().size(); ++i) { + projected.push_back(face->project(face_loops.back()[i]->v)); + } + + if (carve::geom2d::signedArea(projected) > 0.0) { + std::swap(face_loops.back(), hole_loops.back()); + } + } + + // if there are holes, then they need to be merged with faces. + if (hole_loops.size()) { + mergeFacesAndHoles(face, face_loops, hole_loops, hooks); + } + } else { + if (!processCrossingEdges(face, vertex_intersections, hooks, base_loop, paths, loops, face_loops)) { + // complex case - fall back to old edge tracing code. +#if defined(CARVE_DEBUG) + std::cerr << "### processCrossingEdges failed. Falling back to edge tracing code" << std::endl; +#endif + for (V2Set::const_iterator i = split_edges.begin(); i != split_edges.end(); ++i) { + face_edges.insert(std::make_pair((*i).first, (*i).second)); + face_edges.insert(std::make_pair((*i).second, (*i).first)); + } + splitFace(face, face_edges, face_loops, hole_loops, vertex_intersections); + + if (hole_loops.size()) { + mergeFacesAndHoles(face, face_loops, hole_loops, hooks); + } + } + } + } + + + +} + + + +/** + * \brief Build a set of face loops for all (split) faces of a Polyhedron. + * + * @param[in] poly The polyhedron to process + * @param vmap + * @param face_split_edges + * @param divided_edges + * @param[out] face_loops_out The resulting face loops + * + * @return The number of edges generated. + */ +size_t carve::csg::CSG::generateFaceLoops(carve::mesh::MeshSet<3> *poly, + const detail::Data &data, + FaceLoopList &face_loops_out) { + static carve::TimingName FUNC_NAME("CSG::generateFaceLoops()"); + carve::TimingBlock block(FUNC_NAME); + size_t generated_edges = 0; + std::vector<carve::mesh::MeshSet<3>::vertex_t *> base_loop; + std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> > face_loops; + + for (carve::mesh::MeshSet<3>::face_iter i = poly->faceBegin(); i != poly->faceEnd(); ++i) { + carve::mesh::MeshSet<3>::face_t *face = (*i); + +#if defined(CARVE_DEBUG) + double in_area = 0.0, out_area = 0.0; + + { + std::vector<carve::mesh::MeshSet<3>::vertex_t *> base_loop; + assembleBaseLoop(face, data, base_loop); + + { + std::vector<carve::geom2d::P2> projected; + projected.reserve(base_loop.size()); + for (size_t n = 0; n < base_loop.size(); ++n) { + projected.push_back(face->project(base_loop[n]->v)); + } + + in_area = carve::geom2d::signedArea(projected); + std::cerr << "### in_area=" << in_area << std::endl; + } + } +#endif + + generateOneFaceLoop(face, data, vertex_intersections, hooks, face_loops); + +#if defined(CARVE_DEBUG) + { + V2Set face_edges; + + std::vector<carve::mesh::MeshSet<3>::vertex_t *> base_loop; + assembleBaseLoop(face, data, base_loop); + + for (size_t j = 0, je = base_loop.size() - 1; j < je; ++j) { + face_edges.insert(std::make_pair(base_loop[j+1], base_loop[j])); + } + face_edges.insert(std::make_pair(base_loop[0], base_loop.back())); + for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator fli = face_loops.begin(); fli != face_loops.end(); ++ fli) { + + { + std::vector<carve::geom2d::P2> projected; + projected.reserve((*fli).size()); + for (size_t n = 0; n < (*fli).size(); ++n) { + projected.push_back(face->project((*fli)[n]->v)); + } + + double area = carve::geom2d::signedArea(projected); + std::cerr << "### loop_area[" << std::distance((std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator)face_loops.begin(), fli) << "]=" << area << std::endl; + out_area += area; + } + + const std::vector<carve::mesh::MeshSet<3>::vertex_t *> &fl = *fli; + for (size_t j = 0, je = fl.size() - 1; j < je; ++j) { + face_edges.insert(std::make_pair(fl[j], fl[j+1])); + } + face_edges.insert(std::make_pair(fl.back(), fl[0])); + } + for (V2Set::const_iterator j = face_edges.begin(); j != face_edges.end(); ++j) { + if (face_edges.find(std::make_pair((*j).second, (*j).first)) == face_edges.end()) { + std::cerr << "### error: unmatched edge [" << (*j).first << "-" << (*j).second << "]" << std::endl; + } + } + std::cerr << "### out_area=" << out_area << std::endl; + if (out_area != in_area) { + std::cerr << "### error: area does not match. delta = " << (out_area - in_area) << std::endl; + // CARVE_ASSERT(fabs(out_area - in_area) < 1e-5); + } + } +#endif + + // now record all the resulting face loops. +#if defined(CARVE_DEBUG) + std::cerr << "### ======" << std::endl; +#endif + for (std::list<std::vector<carve::mesh::MeshSet<3>::vertex_t *> >::const_iterator + f = face_loops.begin(), fe = face_loops.end(); + f != fe; + ++f) { +#if defined(CARVE_DEBUG) + std::cerr << "### loop:"; + for (size_t i = 0; i < (*f).size(); ++i) { + std::cerr << " " << (*f)[i]; + } + std::cerr << std::endl; +#endif + + face_loops_out.append(new FaceLoop(face, *f)); + generated_edges += (*f).size(); + } +#if defined(CARVE_DEBUG) + std::cerr << "### ======" << std::endl; +#endif + } + return generated_edges; +} diff --git a/extern/carve/lib/intersect_group.cpp b/extern/carve/lib/intersect_group.cpp new file mode 100644 index 00000000000..a1528569c01 --- /dev/null +++ b/extern/carve/lib/intersect_group.cpp @@ -0,0 +1,232 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/csg.hpp> +#include <carve/timing.hpp> + +#include "csg_detail.hpp" +#include "intersect_common.hpp" + +void carve::csg::CSG::makeEdgeMap(const carve::csg::FaceLoopList &loops, + size_t edge_count, + detail::LoopEdges &edge_map) { +#if defined(UNORDERED_COLLECTIONS_SUPPORT_RESIZE) + edge_map.resize(edge_count); +#endif + + for (carve::csg::FaceLoop *i = loops.head; i; i = i->next) { + edge_map.addFaceLoop(i); + i->group = NULL; + } +} + +#include <carve/polyline.hpp> + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) +void writePLY(const std::string &out_file, const carve::mesh::MeshSet<3> *poly, bool ascii); +void writePLY(const std::string &out_file, const carve::line::PolylineSet *lines, bool ascii); +#endif + +void carve::csg::CSG::findSharedEdges(const detail::LoopEdges &edge_map_a, + const detail::LoopEdges &edge_map_b, + V2Set &shared_edges) { + for (detail::LoopEdges::const_iterator + i = edge_map_a.begin(), e = edge_map_a.end(); + i != e; + ++i) { + detail::LoopEdges::const_iterator j = edge_map_b.find((*i).first); + if (j != edge_map_b.end()) { + shared_edges.insert((*i).first); + } + } + +#if defined(CARVE_DEBUG) + detail::VVSMap edge_graph; + + for (V2Set::const_iterator i = shared_edges.begin(); i != shared_edges.end(); ++i) { + edge_graph[(*i).first].insert((*i).second); + edge_graph[(*i).second].insert((*i).first); + } + + std::cerr << "*** testing consistency of edge graph" << std::endl; + for (detail::VVSMap::const_iterator i = edge_graph.begin(); i != edge_graph.end(); ++i) { + if ((*i).second.size() > 2) { + std::cerr << "branch at: " << (*i).first << std::endl; + } + if ((*i).second.size() == 1) { + std::cerr << "endpoint at: " << (*i).first << std::endl; + std::cerr << "coordinate: " << (*i).first->v << std::endl; + } + } + + { + carve::line::PolylineSet intersection_graph; + intersection_graph.vertices.resize(edge_graph.size()); + std::map<const carve::mesh::MeshSet<3>::vertex_t *, size_t> vmap; + + size_t j = 0; + for (detail::VVSMap::const_iterator i = edge_graph.begin(); i != edge_graph.end(); ++i) { + intersection_graph.vertices[j].v = (*i).first->v; + vmap[(*i).first] = j++; + } + + while (edge_graph.size()) { + detail::VVSMap::iterator prior_i = edge_graph.begin(); + carve::mesh::MeshSet<3>::vertex_t *prior = (*prior_i).first; + std::vector<size_t> connected; + connected.push_back(vmap[prior]); + while (prior_i != edge_graph.end() && (*prior_i).second.size()) { + carve::mesh::MeshSet<3>::vertex_t *next = *(*prior_i).second.begin(); + detail::VVSMap::iterator next_i = edge_graph.find(next); + CARVE_ASSERT(next_i != edge_graph.end()); + connected.push_back(vmap[next]); + (*prior_i).second.erase(next); + (*next_i).second.erase(prior); + if (!(*prior_i).second.size()) { edge_graph.erase(prior_i); prior_i = edge_graph.end(); } + if (!(*next_i).second.size()) { edge_graph.erase(next_i); next_i = edge_graph.end(); } + prior_i = next_i; + prior = next; + } + bool closed = connected.front() == connected.back(); + for (size_t k = 0; k < connected.size(); ++k) { + std::cerr << " " << connected[k]; + } + std::cerr << std::endl; + intersection_graph.addPolyline(closed, connected.begin(), connected.end()); + } + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + std::string out("/tmp/intersection.ply"); + ::writePLY(out, &intersection_graph, true); +#endif + } + + std::cerr << "*** edge graph consistency test done" << std::endl; +#endif +} + + + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) +static carve::mesh::MeshSet<3> *groupToPolyhedron(const carve::csg::FaceLoopGroup &grp) { + const carve::csg::FaceLoopList &fl = grp.face_loops; + std::vector<carve::mesh::MeshSet<3>::face_t *> faces; + faces.reserve(fl.size()); + for (carve::csg::FaceLoop *f = fl.head; f; f = f->next) { + faces.push_back(f->orig_face->create(f->vertices.begin(), f->vertices.end(), false)); + } + carve::mesh::MeshSet<3> *poly = new carve::mesh::MeshSet<3>(faces); + + poly->canonicalize(); + return poly; +} +#endif + + + +void carve::csg::CSG::groupFaceLoops(carve::mesh::MeshSet<3> *src, + carve::csg::FaceLoopList &face_loops, + const carve::csg::detail::LoopEdges &loop_edges, + const carve::csg::V2Set &no_cross, + carve::csg::FLGroupList &out_loops) { + // Find all the groups of face loops that are connected by edges + // that are not part of no_cross. + // this could potentially be done with a disjoint set data-structure. +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + static int call_num = 0; + call_num++; +#endif + + static carve::TimingName GROUP_FACE_LOOPS("groupFaceLoops()"); + + carve::TimingBlock block(GROUP_FACE_LOOPS); + + int tag_num = 0; + while (face_loops.size()) { + out_loops.push_back(FaceLoopGroup(src)); + carve::csg::FaceLoopGroup &group = (out_loops.back()); + carve::csg::FaceLoopList &curr = (group.face_loops); + carve::csg::V2Set &perim = (group.perimeter); + + carve::csg::FaceLoop *expand = face_loops.head; + + expand->group = &group; + face_loops.remove(expand); + curr.append(expand); + + while (expand) { + std::vector<carve::mesh::MeshSet<3>::vertex_t *> &loop = (expand->vertices); + carve::mesh::MeshSet<3>::vertex_t *v1, *v2; + + v1 = loop.back(); + for (size_t i = 0; i < loop.size(); ++i) { + v2 = loop[i]; + + carve::csg::V2Set::const_iterator nc = no_cross.find(std::make_pair(v1, v2)); + if (nc == no_cross.end()) { + carve::csg::detail::LoopEdges::const_iterator j; + + j = loop_edges.find(std::make_pair(v1, v2)); + if (j != loop_edges.end()) { + for (std::list<carve::csg::FaceLoop *>::const_iterator + k = (*j).second.begin(), ke = (*j).second.end(); + k != ke; ++k) { + if ((*k)->group != NULL || + (*k)->orig_face->mesh != expand->orig_face->mesh) continue; + face_loops.remove((*k)); + curr.append((*k)); + (*k)->group = &group; + } + } + + j = loop_edges.find(std::make_pair(v2, v1)); + if (j != loop_edges.end()) { + for (std::list<carve::csg::FaceLoop *>::const_iterator + k = (*j).second.begin(), ke = (*j).second.end(); + k != ke; ++k) { + if ((*k)->group != NULL || + (*k)->orig_face->mesh != expand->orig_face->mesh) continue; + face_loops.remove((*k)); + curr.append((*k)); + (*k)->group = &group; + } + } + } else { + perim.insert(std::make_pair(v1, v2)); + } + v1 = v2; + } + expand = expand->next; + } + tag_num++; + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + { + carve::mesh::MeshSet<3> *poly = groupToPolyhedron(group); + char buf[128]; + sprintf(buf, "/tmp/group-%d-%p.ply", call_num, &curr); + std::string out(buf); + ::writePLY(out, poly, false); + delete poly; + } +#endif + } +} diff --git a/extern/carve/lib/intersect_half_classify_group.cpp b/extern/carve/lib/intersect_half_classify_group.cpp new file mode 100644 index 00000000000..97915c784a0 --- /dev/null +++ b/extern/carve/lib/intersect_half_classify_group.cpp @@ -0,0 +1,199 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/csg.hpp> +#include <carve/debug_hooks.hpp> + +#include <list> +#include <set> +#include <iostream> + +#include <algorithm> + +#include "intersect_common.hpp" +#include "intersect_classify_common.hpp" +#include "intersect_classify_common_impl.hpp" + +namespace carve { + namespace csg { + + namespace { + struct GroupPoly : public CSG::Collector { + carve::mesh::MeshSet<3> *want_groups_from; + std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> > &out; + + GroupPoly(carve::mesh::MeshSet<3> *poly, + std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> > &_out) : CSG::Collector(), want_groups_from(poly), out(_out) { + } + + virtual ~GroupPoly() { + } + + virtual void collect(FaceLoopGroup *grp, CSG::Hooks & /* hooks */) { + if (grp->face_loops.head->orig_face->mesh->meshset != want_groups_from) return; + + std::list<ClassificationInfo> &cinfo = (grp->classification); + if (cinfo.size() == 0) { + std::cerr << "WARNING! group " << grp << " has no classification info!" << std::endl; + return; + } + // XXX: check all the cinfo elements for consistency. + FaceClass fc = cinfo.front().classification; + + std::vector<carve::mesh::MeshSet<3>::face_t *> faces; + faces.reserve(grp->face_loops.size()); + for (FaceLoop *loop = grp->face_loops.head; loop != NULL; loop = loop->next) { + faces.push_back(loop->orig_face->create(loop->vertices.begin(), loop->vertices.end(), false)); + } + + out.push_back(std::make_pair(fc, new carve::mesh::MeshSet<3>(faces))); + } + + virtual carve::mesh::MeshSet<3> *done(CSG::Hooks & /* hooks */) { + return NULL; + } + }; + + class FaceMaker { + public: + + bool pointOn(VertexClassification &vclass, FaceLoop *f, size_t index) const { + return vclass[f->vertices[index]].cls[0] == POINT_ON; + } + + void explain(FaceLoop *f, size_t index, PointClass pc) const { +#if defined(CARVE_DEBUG) + std::cerr << "face loop " << f << " from poly b is easy because vertex " << index << " (" << f->vertices[index]->v << ") is " << ENUM(pc) << std::endl; +#endif + } + }; + + class HalfClassifyFaceGroups { + HalfClassifyFaceGroups &operator=(const HalfClassifyFaceGroups &); + + public: + std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> > &b_out; + CSG::Hooks &hooks; + + HalfClassifyFaceGroups(std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> > &c, CSG::Hooks &h) : b_out(c), hooks(h) { + } + + void classifySimple(FLGroupList &a_loops_grouped, + FLGroupList &b_loops_grouped, + VertexClassification & /* vclass */, + carve::mesh::MeshSet<3> *poly_a, + carve::mesh::MeshSet<3> *poly_b) const { + GroupPoly group_poly(poly_b, b_out); + performClassifySimpleOnFaceGroups(a_loops_grouped, b_loops_grouped, poly_a, poly_b, group_poly, hooks); +#if defined(CARVE_DEBUG) + std::cerr << "after removal of simple on groups: " << b_loops_grouped.size() << " b groups" << std::endl; +#endif + } + + void classifyEasy(FLGroupList & /* a_loops_grouped */, + FLGroupList &b_loops_grouped, + VertexClassification & vclass, + carve::mesh::MeshSet<3> *poly_a, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree, + carve::mesh::MeshSet<3> *poly_b, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree) const { + GroupPoly group_poly(poly_b, b_out); + performClassifyEasyFaceGroups(b_loops_grouped, poly_a, poly_a_rtree, vclass, FaceMaker(), group_poly, hooks); +#if defined(CARVE_DEBUG) + std::cerr << "after removal of easy groups: " << b_loops_grouped.size() << " b groups" << std::endl; +#endif + } + + void classifyHard(FLGroupList & /* a_loops_grouped */, + FLGroupList &b_loops_grouped, + VertexClassification & /* vclass */, + carve::mesh::MeshSet<3> *poly_a, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree, + carve::mesh::MeshSet<3> *poly_b, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree) const { + GroupPoly group_poly(poly_b, b_out); + performClassifyHardFaceGroups(b_loops_grouped, poly_a, poly_a_rtree, FaceMaker(), group_poly, hooks); +#if defined(CARVE_DEBUG) + std::cerr << "after removal of hard groups: " << b_loops_grouped.size() << " b groups" << std::endl; +#endif + + } + + void faceLoopWork(FLGroupList & /* a_loops_grouped */, + FLGroupList &b_loops_grouped, + VertexClassification & /* vclass */, + carve::mesh::MeshSet<3> *poly_a, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree, + carve::mesh::MeshSet<3> *poly_b, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree) const { + GroupPoly group_poly(poly_b, b_out); + performFaceLoopWork(poly_a, poly_a_rtree, b_loops_grouped, *this, group_poly, hooks); + } + + void postRemovalCheck(FLGroupList & /* a_loops_grouped */, + FLGroupList &b_loops_grouped) const { +#if defined(CARVE_DEBUG) + std::cerr << "after removal of on groups: " << b_loops_grouped.size() << " b groups" << std::endl; +#endif + } + + bool faceLoopSanityChecker(FaceLoopGroup &i) const { + return false; + return i.face_loops.size() != 1; + } + + void finish(FLGroupList &a_loops_grouped,FLGroupList &b_loops_grouped) const { +#if defined(CARVE_DEBUG) + if (a_loops_grouped.size() || b_loops_grouped.size()) + std::cerr << "UNCLASSIFIED! a=" << a_loops_grouped.size() << ", b=" << b_loops_grouped.size() << std::endl; +#endif + } + }; + } + + void CSG::halfClassifyFaceGroups(const V2Set & /* shared_edges */, + VertexClassification &vclass, + carve::mesh::MeshSet<3> *poly_a, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_a_rtree, + FLGroupList &a_loops_grouped, + const detail::LoopEdges & /* a_edge_map */, + carve::mesh::MeshSet<3> *poly_b, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *poly_b_rtree, + FLGroupList &b_loops_grouped, + const detail::LoopEdges & /* b_edge_map */, + std::list<std::pair<FaceClass, carve::mesh::MeshSet<3> *> > &b_out) { + HalfClassifyFaceGroups classifier(b_out, hooks); + GroupPoly group_poly(poly_b, b_out); + performClassifyFaceGroups( + a_loops_grouped, + b_loops_grouped, + vclass, + poly_a, + poly_a_rtree, + poly_b, + poly_b_rtree, + classifier, + group_poly, + hooks); + } + + } +} diff --git a/extern/carve/lib/intersection.cpp b/extern/carve/lib/intersection.cpp new file mode 100644 index 00000000000..2aa97131f7f --- /dev/null +++ b/extern/carve/lib/intersection.cpp @@ -0,0 +1,92 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <algorithm> + +#include <carve/carve.hpp> +#include <carve/poly.hpp> +#include <carve/timing.hpp> +#include <carve/intersection.hpp> + + + +void carve::csg::Intersections::collect(const IObj &obj, + std::vector<carve::mesh::MeshSet<3>::vertex_t *> *collect_v, + std::vector<carve::mesh::MeshSet<3>::edge_t *> *collect_e, + std::vector<carve::mesh::MeshSet<3>::face_t *> *collect_f) const { + carve::csg::Intersections::const_iterator i = find(obj); + if (i != end()) { + Intersections::mapped_type::const_iterator a, b; + for (a = (*i).second.begin(), b = (*i).second.end(); a != b; ++a) { + switch ((*a).first.obtype) { + case carve::csg::IObj::OBTYPE_VERTEX: + if (collect_v) collect_v->push_back((*a).first.vertex); + break; + case carve::csg::IObj::OBTYPE_EDGE: + if (collect_e) collect_e->push_back((*a).first.edge); + break; + case carve::csg::IObj::OBTYPE_FACE: + if (collect_f) collect_f->push_back((*a).first.face); + break; + default: + throw carve::exception("should not happen " __FILE__ ":" XSTR(__LINE__)); + } + } + } +} + + + +bool carve::csg::Intersections::intersectsFace(carve::mesh::MeshSet<3>::vertex_t *v, + carve::mesh::MeshSet<3>::face_t *f) const { + const_iterator i = find(v); + if (i != end()) { + mapped_type::const_iterator a, b; + + for (a = (*i).second.begin(), b = (*i).second.end(); a != b; ++a) { + switch ((*a).first.obtype) { + case IObj::OBTYPE_VERTEX: { + const carve::mesh::MeshSet<3>::edge_t *edge = f->edge; + do { + if (edge->vert == (*a).first.vertex) return true; + edge = edge->next; + } while (edge != f->edge); + break; + } + case carve::csg::IObj::OBTYPE_EDGE: { + const carve::mesh::MeshSet<3>::edge_t *edge = f->edge; + do { + if (edge == (*a).first.edge) return true; + edge = edge->next; + } while (edge != f->edge); + break; + } + case carve::csg::IObj::OBTYPE_FACE: { + if ((*a).first.face == f) return true; + break; + } + default: + throw carve::exception("should not happen " __FILE__ ":" XSTR(__LINE__)); + } + } + } + return false; +} diff --git a/extern/carve/lib/math.cpp b/extern/carve/lib/math.cpp new file mode 100644 index 00000000000..811312c313e --- /dev/null +++ b/extern/carve/lib/math.cpp @@ -0,0 +1,347 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/math.hpp> +#include <carve/matrix.hpp> + +#include <iostream> +#include <limits> + +#include <stdio.h> + +#define M_2PI_3 2.0943951023931953 +#define M_SQRT_3_4 0.8660254037844386 +#define EPS std::numeric_limits<double>::epsilon() + +namespace carve { + namespace math { + + struct Root { + double root; + int multiplicity; + + Root(double r) : root(r), multiplicity(1) {} + Root(double r, int m) : root(r), multiplicity(m) {} + }; + + void cplx_sqrt(double re, double im, + double &re_1, double &im_1, + double &re_2, double &im_2) { + if (re == 0.0 && im == 0.0) { + re_1 = re_2 = re; + im_1 = im_2 = im; + } else { + double d = sqrt(re * re + im * im); + re_1 = sqrt((d + re) / 2.0); + re_2 = re_1; + im_1 = fabs(sqrt((d - re) / 2.0)); + im_2 = -im_1; + } + } + + void cplx_cbrt(double re, double im, + double &re_1, double &im_1, + double &re_2, double &im_2, + double &re_3, double &im_3) { + if (re == 0.0 && im == 0.0) { + re_1 = re_2 = re_3 = re; + im_1 = im_2 = im_3 = im; + } else { + double r = cbrt(sqrt(re * re + im * im)); + double t = atan2(im, re) / 3.0; + re_1 = r * cos(t); + im_1 = r * sin(t); + re_2 = r * cos(t + M_TWOPI / 3.0); + im_2 = r * sin(t + M_TWOPI / 3.0); + re_3 = r * cos(t + M_TWOPI * 2.0 / 3.0); + im_3 = r * sin(t + M_TWOPI * 2.0 / 3.0); + } + } + + void add_root(std::vector<Root> &roots, double root) { + for (size_t i = 0; i < roots.size(); ++i) { + if (roots[i].root == root) { + roots[i].multiplicity++; + return; + } + } + roots.push_back(Root(root)); + } + + void linear_roots(double c1, double c0, std::vector<Root> &roots) { + roots.push_back(Root(c0 / c1)); + } + + void quadratic_roots(double c2, double c1, double c0, std::vector<Root> &roots) { + if (fabs(c2) < EPS) { + linear_roots(c1, c0, roots); + return; + } + + double p = 0.5 * c1 / c2; + double dis = p * p - c0 / c2; + + if (dis > 0.0) { + dis = sqrt(dis); + if (-p - dis != -p + dis) { + roots.push_back(Root(-p - dis)); + roots.push_back(Root(-p + dis)); + } else { + roots.push_back(Root(-p, 2)); + } + } + } + + void cubic_roots(double c3, double c2, double c1, double c0, std::vector<Root> &roots) { + int n_sol = 0; + double _r[3]; + + if (fabs(c3) < EPS) { + quadratic_roots(c2, c1, c0, roots); + return; + } + + if (fabs(c0) < EPS) { + quadratic_roots(c3, c2, c1, roots); + add_root(roots, 0.0); + return; + } + + double xN = -c2 / (3.0 * c3); + double yN = c0 + xN * (c1 + xN * (c2 + c3 * xN)); + + double delta_sq = (c2 * c2 - 3.0 * c3 * c1) / (9.0 * c3 * c3); + double h_sq = 4.0 / 9.0 * (c2 * c2 - 3.0 * c3 * c1) * (delta_sq * delta_sq); + double dis = yN * yN - h_sq; + + if (dis > EPS) { + // One real root, two complex roots. + + double dis_sqrt = sqrt(dis); + double r_p = yN - dis_sqrt; + double r_q = yN + dis_sqrt; + double p = cbrt(fabs(r_p)/(2.0 * c3)); + double q = cbrt(fabs(r_q)/(2.0 * c3)); + + if (r_p > 0.0) p = -p; + if (r_q > 0.0) q = -q; + + _r[0] = xN + p + q; + n_sol = 1; + + double re = xN - p * .5 - q * .5; + double im = p * M_SQRT_3_4 - q * M_SQRT_3_4; + + // root 2: xN + p * exp(M_2PI_3.i) + q * exp(-M_2PI_3.i); + // root 3: complex conjugate of root 2 + + if (im < EPS) { + _r[1] = _r[2] = re; + n_sol += 2; + } + } else if (dis < -EPS) { + // Three distinct real roots. + double theta = acos(-yN / sqrt(h_sq)) / 3.0; + double delta = sqrt(c2 * c2 - 3.0 * c3 * c1) / (3.0 * c3); + + _r[0] = xN + (2.0 * delta) * cos(theta); + _r[1] = xN + (2.0 * delta) * cos(M_2PI_3 - theta); + _r[2] = xN + (2.0 * delta) * cos(M_2PI_3 + theta); + n_sol = 3; + } else { + // Three real roots (two or three equal). + double r = yN / (2.0 * c3); + double delta = cbrt(r); + + _r[0] = xN + delta; + _r[1] = xN + delta; + _r[2] = xN - 2.0 * delta; + n_sol = 3; + } + + for (int i=0; i < n_sol; i++) { + add_root(roots, _r[i]); + } + } + + static void U(const Matrix3 &m, + double l, + double u[6], + double &u_max, + int &u_argmax) { + u[0] = (m._22 - l) * (m._33 - l) - m._23 * m._23; + u[1] = m._13 * m._23 - m._12 * (m._33 - l); + u[2] = m._12 * m._23 - m._13 * (m._22 - l); + u[3] = (m._11 - l) * (m._33 - l) - m._13 * m._13; + u[4] = m._12 * m._13 - m._23 * (m._11 - l); + u[5] = (m._11 - l) * (m._22 - l) - m._12 * m._12; + + u_max = -1.0; + u_argmax = -1; + + for (int i = 0; i < 6; ++i) { + if (u_max < fabs(u[i])) { u_max = fabs(u[i]); u_argmax = i; } + } + } + + static void eig1(const Matrix3 &m, double l, carve::geom::vector<3> &e) { + double u[6]; + double u_max; + int u_argmax; + + U(m, l, u, u_max, u_argmax); + + switch(u_argmax) { + case 0: + e.x = u[0]; e.y = u[1]; e.z = u[2]; break; + case 1: case 3: + e.x = u[1]; e.y = u[3]; e.z = u[4]; break; + case 2: case 4: case 5: + e.x = u[2]; e.y = u[4]; e.z = u[5]; break; + } + e.normalize(); + } + + static void eig2(const Matrix3 &m, double l, carve::geom::vector<3> &e1, carve::geom::vector<3> &e2) { + double u[6]; + double u_max; + int u_argmax; + + U(m, l, u, u_max, u_argmax); + + switch(u_argmax) { + case 0: case 1: + e1.x = -m._12; e1.y = m._11; e1.z = 0.0; + e2.x = -m._13 * m._11; e2.y = -m._13 * m._12; e2.z = m._11 * m._11 + m._12 * m._12; + break; + case 2: + e1.x = m._12; e1.y = 0.0; e1.z = -m._11; + e2.x = -m._12 * m._11; e2.y = m._11 * m._11 + m._13 * m._13; e2.z = -m._12 * m._13; + break; + case 3: case 4: + e1.x = 0.0; e1.y = -m._23; e1.z = -m._22; + e2.x = m._22 * m._22 + m._23 * m._23; e2.y = -m._12 * m._22; e2.z = -m._12 * m._23; + break; + case 5: + e1.x = 0.0; e1.y = -m._33; e1.z = m._23; + e2.x = m._23 * m._23 + m._33 * m._33; e2.y = -m._13 * m._23; e2.z = -m._13 * m._33; + } + e1.normalize(); + e2.normalize(); + } + + static void eig3(const Matrix3 &m, + double l, + carve::geom::vector<3> &e1, + carve::geom::vector<3> &e2, + carve::geom::vector<3> &e3) { + e1.x = 1.0; e1.y = 0.0; e1.z = 0.0; + e2.x = 0.0; e2.y = 1.0; e2.z = 0.0; + e3.x = 0.0; e3.y = 0.0; e3.z = 1.0; + } + + void eigSolveSymmetric(const Matrix3 &m, + double &l1, carve::geom::vector<3> &e1, + double &l2, carve::geom::vector<3> &e2, + double &l3, carve::geom::vector<3> &e3) { + double c0 = + m._11 * m._22 * m._33 + + 2.0 * m._12 * m._13 * m._23 - + m._11 * m._23 * m._23 - + m._22 * m._13 * m._13 - + m._33 * m._12 * m._12; + double c1 = + m._11 * m._22 - + m._12 * m._12 + + m._11 * m._33 - + m._13 * m._13 + + m._22 * m._33 - + m._23 * m._23; + double c2 = + m._11 + + m._22 + + m._33; + + double a = (3.0 * c1 - c2 * c2) / 3.0; + double b = (-2.0 * c2 * c2 * c2 + 9.0 * c1 * c2 - 27.0 * c0) / 27.0; + + double Q = b * b / 4.0 + a * a * a / 27.0; + + if (fabs(Q) < 1e-16) { + l1 = m._11; e1.x = 1.0; e1.y = 0.0; e1.z = 0.0; + l2 = m._22; e2.x = 0.0; e2.y = 1.0; e2.z = 0.0; + l3 = m._33; e3.x = 0.0; e3.y = 0.0; e3.z = 1.0; + } else if (Q > 0) { + l1 = l2 = c2 / 3.0 + cbrt(b / 2.0); + l3 = c2 / 3.0 - 2.0 * cbrt(b / 2.0); + + eig2(m, l1, e1, e2); + eig1(m, l3, e3); + } else if (Q < 0) { + double t = atan2(sqrt(-Q), -b / 2.0); + double cos_t3 = cos(t / 3.0); + double sin_t3 = sin(t / 3.0); + double r = cbrt(sqrt(b * b / 4.0 - Q)); + + l1 = c2 / 3.0 + 2 * r * cos_t3; + l2 = c2 / 3.0 - r * (cos_t3 + M_SQRT_3 * sin_t3); + l3 = c2 / 3.0 - r * (cos_t3 - M_SQRT_3 * sin_t3); + + eig1(m, l1, e1); + eig1(m, l2, e2); + eig1(m, l3, e3); + } + } + + void eigSolve(const Matrix3 &m, double &l1, double &l2, double &l3) { + double c3, c2, c1, c0; + std::vector<Root> roots; + + c3 = -1.0; + c2 = m._11 + m._22 + m._33; + c1 = + -(m._22 * m._33 + m._11 * m._22 + m._11 * m._33) + +(m._23 * m._32 + m._13 * m._31 + m._12 * m._21); + c0 = + +(m._11 * m._22 - m._12 * m._21) * m._33 + -(m._11 * m._23 - m._13 * m._21) * m._32 + +(m._12 * m._23 - m._13 * m._22) * m._31; + + cubic_roots(c3, c2, c1, c0, roots); + + for (size_t i = 0; i < roots.size(); i++) { + Matrix3 M(m); + M._11 -= roots[i].root; + M._22 -= roots[i].root; + M._33 -= roots[i].root; + // solve M.v = 0 + } + + std::cerr << "n_roots=" << roots.size() << std::endl; + for (size_t i = 0; i < roots.size(); i++) { + fprintf(stderr, " %.24f(%d)", roots[i].root, roots[i].multiplicity); + } + std::cerr << std::endl; + } + + } +} + diff --git a/extern/carve/lib/mesh.cpp b/extern/carve/lib/mesh.cpp new file mode 100644 index 00000000000..55ab893c10a --- /dev/null +++ b/extern/carve/lib/mesh.cpp @@ -0,0 +1,1203 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/mesh.hpp> +#include <carve/mesh_impl.hpp> +#include <carve/rtree.hpp> + +#include <carve/poly.hpp> + +namespace { + inline double CALC_X(const carve::geom::plane<3> &p, double y, double z) { return -(p.d + p.N.y * y + p.N.z * z) / p.N.x; } + inline double CALC_Y(const carve::geom::plane<3> &p, double x, double z) { return -(p.d + p.N.x * x + p.N.z * z) / p.N.y; } + inline double CALC_Z(const carve::geom::plane<3> &p, double x, double y) { return -(p.d + p.N.x * x + p.N.y * y) / p.N.z; } + + carve::geom::vector<2> _project_1(const carve::geom::vector<3> &v) { + return carve::geom::VECTOR(v.z, v.y); + } + + carve::geom::vector<2> _project_2(const carve::geom::vector<3> &v) { + return carve::geom::VECTOR(v.x, v.z); + } + + carve::geom::vector<2> _project_3(const carve::geom::vector<3> &v) { + return carve::geom::VECTOR(v.y, v.x); + } + + carve::geom::vector<2> _project_4(const carve::geom::vector<3> &v) { + return carve::geom::VECTOR(v.y, v.z); + } + + carve::geom::vector<2> _project_5(const carve::geom::vector<3> &v) { + return carve::geom::VECTOR(v.z, v.x); + } + + carve::geom::vector<2> _project_6(const carve::geom::vector<3> &v) { + return carve::geom::VECTOR(v.x, v.y); + } + + carve::geom::vector<3> _unproject_1(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) { + return carve::geom::VECTOR(CALC_X(plane, p.y, p.x), p.y, p.x); + } + + carve::geom::vector<3> _unproject_2(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) { + return carve::geom::VECTOR(p.x, CALC_Y(plane, p.x, p.y), p.y); + } + + carve::geom::vector<3> _unproject_3(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) { + return carve::geom::VECTOR(p.y, p.x, CALC_Z(plane, p.y, p.x)); + } + + carve::geom::vector<3> _unproject_4(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) { + return carve::geom::VECTOR(CALC_X(plane, p.x, p.y), p.x, p.y); + } + + carve::geom::vector<3> _unproject_5(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) { + return carve::geom::VECTOR(p.y, CALC_Y(plane, p.y, p.x), p.x); + } + + carve::geom::vector<3> _unproject_6(const carve::geom::vector<2> &p, const carve::geom3d::Plane &plane) { + return carve::geom::VECTOR(p.x, p.y, CALC_Z(plane, p.x, p.y)); + } + + static carve::geom::vector<2> (*project_tab[2][3])(const carve::geom::vector<3> &) = { + { &_project_1, &_project_2, &_project_3 }, + { &_project_4, &_project_5, &_project_6 } + }; + + static carve::geom::vector<3> (*unproject_tab[2][3])(const carve::geom::vector<2> &, const carve::geom3d::Plane &) = { + { &_unproject_1, &_unproject_2, &_unproject_3 }, + { &_unproject_4, &_unproject_5, &_unproject_6 } + }; + +} + +namespace carve { + namespace mesh { + + + + template<unsigned ndim> + typename Face<ndim>::project_t Face<ndim>::getProjector(bool positive_facing, int axis) const { + return NULL; + } + + + + template<> + Face<3>::project_t Face<3>::getProjector(bool positive_facing, int axis) const { + return project_tab[positive_facing ? 1 : 0][axis]; + } + + + + template<unsigned ndim> + typename Face<ndim>::unproject_t Face<ndim>::getUnprojector(bool positive_facing, int axis) const { + return NULL; + } + + + + template<> + Face<3>::unproject_t Face<3>::getUnprojector(bool positive_facing, int axis) const { + return unproject_tab[positive_facing ? 1 : 0][axis]; + } + + + + template<unsigned ndim> + bool Face<ndim>::containsPoint(const vector_t &p) const { + if (!carve::math::ZERO(carve::geom::distance(plane, p))) return false; + // return pointInPolySimple(vertices, projector(), (this->*project)(p)); + std::vector<carve::geom::vector<2> > verts; + getProjectedVertices(verts); + return carve::geom2d::pointInPoly(verts, project(p)).iclass != carve::POINT_OUT; + } + + + + template<unsigned ndim> + bool Face<ndim>::containsPointInProjection(const vector_t &p) const { + std::vector<carve::geom::vector<2> > verts; + getProjectedVertices(verts); + return carve::geom2d::pointInPoly(verts, project(p)).iclass != carve::POINT_OUT; + } + + + + template<unsigned ndim> + bool Face<ndim>::simpleLineSegmentIntersection( + const carve::geom::linesegment<ndim> &line, + vector_t &intersection) const { + if (!line.OK()) return false; + + carve::mesh::MeshSet<3>::vertex_t::vector_t p; + carve::IntersectionClass intersects = + carve::geom3d::lineSegmentPlaneIntersection(plane, line, p); + if (intersects == carve::INTERSECT_NONE || intersects == carve::INTERSECT_BAD) { + return false; + } + + std::vector<carve::geom::vector<2> > verts; + getProjectedVertices(verts); + if (carve::geom2d::pointInPolySimple(verts, project(p))) { + intersection = p; + return true; + } + return false; + } + + + + template<unsigned ndim> + IntersectionClass Face<ndim>::lineSegmentIntersection(const carve::geom::linesegment<ndim> &line, + vector_t &intersection) const { + if (!line.OK()) return INTERSECT_NONE; + + + vector_t p; + IntersectionClass intersects = carve::geom3d::lineSegmentPlaneIntersection(plane, line, p); + if (intersects == INTERSECT_NONE || intersects == INTERSECT_BAD) { + return intersects; + } + + std::vector<carve::geom::vector<2> > verts; + getProjectedVertices(verts); + carve::geom2d::PolyInclusionInfo pi = carve::geom2d::pointInPoly(verts, project(p)); + switch (pi.iclass) { + case POINT_VERTEX: + intersection = p; + return INTERSECT_VERTEX; + + case POINT_EDGE: + intersection = p; + return INTERSECT_EDGE; + + case POINT_IN: + intersection = p; + return INTERSECT_FACE; + + case POINT_OUT: + return INTERSECT_NONE; + + default: + break; + } + return INTERSECT_NONE; + } + + + + template<unsigned ndim> + Face<ndim> *Face<ndim>::closeLoop(typename Face<ndim>::edge_t *start) { + edge_t *e = start; + std::vector<edge_t *> loop_edges; + do { + CARVE_ASSERT(e->rev == NULL); + loop_edges.push_back(e); + e = e->perimNext(); + } while (e != start); + + const size_t N = loop_edges.size(); + for (size_t i = 0; i < N; ++i) { + loop_edges[i]->rev = new edge_t(loop_edges[i]->v2(), NULL); + } + + for (size_t i = 0; i < N; ++i) { + edge_t *e1 = loop_edges[i]->rev; + edge_t *e2 = loop_edges[(i+1)%N]->rev; + e1->prev = e2; + e2->next = e1; + } + + Face *f = new Face(start->rev); + + CARVE_ASSERT(f->n_edges == N); + + return f; + } + + + + namespace detail { + + + + bool FaceStitcher::EdgeOrderData::Cmp::operator()(const EdgeOrderData &a, const EdgeOrderData &b) const { + int v = carve::geom3d::compareAngles(edge_dir, base_dir, a.face_dir, b.face_dir); + double da = carve::geom3d::antiClockwiseAngle(base_dir, a.face_dir, edge_dir); + double db = carve::geom3d::antiClockwiseAngle(base_dir, b.face_dir, edge_dir); + int v0 = v; + v = 0; + if (da < db) v = -1; + if (db < da) v = +1; + if (v0 != v) { + std::cerr << "v0= " << v0 << " v= " << v << " da= " << da << " db= " << db << " " << edge_dir << " " << base_dir << " " << a.face_dir << b.face_dir << std::endl; + } + if (v < 0) return true; + if (v == 0) { + if (a.is_reversed && !b.is_reversed) return true; + if (a.is_reversed == b.is_reversed) { + return a.group_id < b.group_id; + } + } + return false; + } + + + + void FaceStitcher::matchSimpleEdges() { + // join faces that share an edge, where no other faces are incident. + for (edge_map_t::iterator i = edges.begin(); i != edges.end(); ++i) { + const vpair_t &ev = (*i).first; + edge_map_t::iterator j = edges.find(vpair_t(ev.second, ev.first)); + if (j == edges.end()) { + for (edgelist_t::iterator k = (*i).second.begin(); k != (*i).second.end(); ++k) { + is_open[ (*k)->face->id] = true; + } + } else if ((*i).second.size() != 1 || (*j).second.size() != 1) { + std::swap(complex_edges[(*i).first], (*i).second); + } else { + // simple edge. + edge_t *a = (*i).second.front(); + edge_t *b = (*j).second.front(); + if (a < b) { + // every simple edge pair is encountered twice. only merge once. + a->rev = b; + b->rev = a; + face_groups.merge_sets(a->face->id, b->face->id); + } + } + } + } + + + + size_t FaceStitcher::faceGroupID(const Face<3> *face) { + return face_groups.find_set_head(face->id); + } + + + + size_t FaceStitcher::faceGroupID(const Edge<3> *edge) { + return face_groups.find_set_head(edge->face->id); + } + + + + void FaceStitcher::orderForwardAndReverseEdges(std::vector<std::vector<Edge<3> *> > &efwd, + std::vector<std::vector<Edge<3> *> > &erev, + std::vector<std::vector<EdgeOrderData> > &result) { + const size_t Nfwd = efwd.size(); + const size_t Nrev = erev.size(); + const size_t N = efwd[0].size(); + + result.resize(N); + + for (size_t i = 0; i < N; ++i) { + Edge<3> *base = efwd[0][i]; + + result[i].reserve(Nfwd + Nrev); + for (size_t j = 0; j < Nfwd; ++j) { + result[i].push_back(EdgeOrderData(efwd[j][i], j, false)); + CARVE_ASSERT(efwd[0][i]->v1() == efwd[j][i]->v1()); + CARVE_ASSERT(efwd[0][i]->v2() == efwd[j][i]->v2()); + } + for (size_t j = 0; j < Nrev; ++j) { + result[i].push_back(EdgeOrderData(erev[j][i], j, true)); + CARVE_ASSERT(erev[0][i]->v1() == erev[j][i]->v1()); + CARVE_ASSERT(erev[0][i]->v2() == erev[j][i]->v2()); + } + + std::sort(result[i].begin(), + result[i].end(), + EdgeOrderData::Cmp(base->v2()->v - base->v1()->v, result[i][0].face_dir)); + } + } + + + + void FaceStitcher::edgeIncidentGroups(const vpair_t &e, + const edge_map_t &all_edges, + std::pair<std::set<size_t>, std::set<size_t> > &groups) { + groups.first.clear(); + groups.second.clear(); + edge_map_t::const_iterator i; + + i = all_edges.find(e); + if (i != all_edges.end()) { + for (edgelist_t::const_iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) { + groups.first.insert(faceGroupID(*j)); + } + } + + i = all_edges.find(vpair_t(e.second, e.first)); + if (i != all_edges.end()) { + for (edgelist_t::const_iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) { + groups.second.insert(faceGroupID(*j)); + } + } + } + + + + void FaceStitcher::buildEdgeGraph(const edge_map_t &all_edges) { + for (edge_map_t::const_iterator i = all_edges.begin(); + i != all_edges.end(); + ++i) { + edge_graph[(*i).first.first].insert((*i).first.second); + } + } + + + + void FaceStitcher::extractPath(std::vector<const vertex_t *> &path) { + path.clear(); + + edge_graph_t::iterator iter = edge_graph.begin(); + + + const vertex_t *init = (*iter).first; + const vertex_t *next = *(*iter).second.begin(); + const vertex_t *prev = NULL; + const vertex_t *vert = init; + + while ((*iter).second.size() == 2) { + prev = *std::find_if((*iter).second.begin(), + (*iter).second.end(), + std::bind2nd(std::not_equal_to<const vertex_t *>(), next)); + next = vert; + vert = prev; + iter = edge_graph.find(vert); + CARVE_ASSERT(iter != edge_graph.end()); + if (vert == init) break; + } + init = vert; + + std::vector<const edge_t *> efwd; + std::vector<const edge_t *> erev; + + edge_map_t::iterator edgeiter; + edgeiter = complex_edges.find(vpair_t(vert, next)); + std::copy((*edgeiter).second.begin(), (*edgeiter).second.end(), std::back_inserter(efwd)); + + edgeiter = complex_edges.find(vpair_t(next, vert)); + std::copy((*edgeiter).second.begin(), (*edgeiter).second.end(), std::back_inserter(erev)); + + path.push_back(vert); + + prev = vert; + vert = next; + path.push_back(vert); + iter = edge_graph.find(vert); + CARVE_ASSERT(iter != edge_graph.end()); + + while (vert != init && (*iter).second.size() == 2) { + next = *std::find_if((*iter).second.begin(), + (*iter).second.end(), + std::bind2nd(std::not_equal_to<const vertex_t *>(), prev)); + + edgeiter = complex_edges.find(vpair_t(vert, next)); + if ((*edgeiter).second.size() != efwd.size()) goto done; + + for (size_t i = 0; i < efwd.size(); ++i) { + Edge<3> *e_next = efwd[i]->perimNext(); + if (e_next->v2() != next) goto done; + efwd[i] = e_next; + } + + edgeiter = complex_edges.find(vpair_t(next, vert)); + if ((*edgeiter).second.size() != erev.size()) goto done; + + for (size_t i = 0; i < erev.size(); ++i) { + Edge<3> *e_prev = erev[i]->perimPrev(); + if (e_prev->v1() != next) goto done; + erev[i] = e_prev; + } + + prev = vert; + vert = next; + path.push_back(vert); + iter = edge_graph.find(vert); + CARVE_ASSERT(iter != edge_graph.end()); + } + done:; + } + + + + void FaceStitcher::removePath(const std::vector<const vertex_t *> &path) { + for (size_t i = 1; i < path.size() - 1; ++i) { + edge_graph.erase(path[i]); + } + + edge_graph[path[0]].erase(path[1]); + if (edge_graph[path[0]].size() == 0) { + edge_graph.erase(path[0]); + } + + edge_graph[path[path.size()-1]].erase(path[path.size()-2]); + if (edge_graph[path[path.size()-1]].size() == 0) { + edge_graph.erase(path[path.size()-1]); + } + } + + + + void FaceStitcher::reorder(std::vector<EdgeOrderData> &ordering, + size_t grp) { + if (!ordering[0].is_reversed && ordering[0].group_id == grp) return; + for (size_t i = 1; i < ordering.size(); ++i) { + if (!ordering[i].is_reversed && ordering[i].group_id == grp) { + std::vector<EdgeOrderData> temp; + temp.reserve(ordering.size()); + std::copy(ordering.begin() + i, ordering.end(), std::back_inserter(temp)); + std::copy(ordering.begin(), ordering.begin() + i, std::back_inserter(temp)); + std::copy(temp.begin(), temp.end(), ordering.begin()); + return; + } + } + } + + + + struct lt_second { + template<typename pair_t> + bool operator()(const pair_t &a, const pair_t &b) const { + return a.second < b.second; + } + }; + + + + void FaceStitcher::fuseEdges(std::vector<Edge<3> *> &fwd, + std::vector<Edge<3> *> &rev) { + for (size_t i = 0; i < fwd.size(); ++i) { + fwd[i]->rev = rev[i]; + rev[i]->rev = fwd[i]; + face_groups.merge_sets(fwd[i]->face->id, rev[i]->face->id); + } + } + + + + void FaceStitcher::joinGroups(std::vector<std::vector<Edge<3> *> > &efwd, + std::vector<std::vector<Edge<3> *> > &erev, + size_t fwd_grp, + size_t rev_grp) { + fuseEdges(efwd[fwd_grp], erev[rev_grp]); + } + + + + void FaceStitcher::matchOrderedEdges(const std::vector<std::vector<EdgeOrderData> >::iterator begin, + const std::vector<std::vector<EdgeOrderData> >::iterator end, + std::vector<std::vector<Edge<3> *> > &efwd, + std::vector<std::vector<Edge<3> *> > &erev) { + typedef std::unordered_map<std::pair<size_t, size_t>, size_t> pair_counts_t; + for (;;) { + pair_counts_t pair_counts; + + for (std::vector<std::vector<EdgeOrderData> >::iterator i = begin; i != end; ++i) { + std::vector<EdgeOrderData> &e = *i; + for (size_t j = 0; j < e.size(); ++j) { + if (!e[j].is_reversed && e[(j+1)%e.size()].is_reversed) { + pair_counts[std::make_pair(e[j].group_id, + e[(j+1)%e.size()].group_id)]++; + } + } + } + + if (!pair_counts.size()) break; + + std::vector<std::pair<size_t, std::pair<size_t, size_t> > > counts; + counts.reserve(pair_counts.size()); + for (pair_counts_t::iterator iter = pair_counts.begin(); iter != pair_counts.end(); ++iter) { + counts.push_back(std::make_pair((*iter).second, (*iter).first)); + } + std::make_heap(counts.begin(), counts.end()); + + std::set<size_t> rem_fwd, rem_rev; + + while (counts.size()) { + std::pair<size_t, size_t> join = counts.front().second; + std::pop_heap(counts.begin(), counts.end()); + counts.pop_back(); + if (rem_fwd.find(join.first) != rem_fwd.end()) continue; + if (rem_rev.find(join.second) != rem_rev.end()) continue; + + size_t g1 = join.first; + size_t g2 = join.second; + + joinGroups(efwd, erev, g1, g2); + + for (std::vector<std::vector<EdgeOrderData> >::iterator i = begin; i != end; ++i) { + (*i).erase(std::remove_if((*i).begin(), (*i).end(), EdgeOrderData::TestGroups(g1, g2)), (*i).end()); + } + + rem_fwd.insert(g1); + rem_rev.insert(g2); + } + } + } + + + + void FaceStitcher::resolveOpenEdges() { + // Remove open regions of mesh. Doing this may make additional + // edges simple (for example, removing a fin from the edge of + // a cube), and may also expose more open mesh regions. In the + // latter case, the process must be repeated to deal with the + // newly uncovered regions. + std::unordered_set<size_t> open_groups; + + for (size_t i = 0; i < is_open.size(); ++i) { + if (is_open[i]) open_groups.insert(face_groups.find_set_head(i)); + } + + while (!open_groups.empty()) { + std::list<vpair_t> edge_0, edge_1; + + for (edge_map_t::iterator i = complex_edges.begin(); i != complex_edges.end(); ++i) { + bool was_modified = false; + for(edgelist_t::iterator j = (*i).second.begin(); j != (*i).second.end(); ) { + if (open_groups.find(faceGroupID(*j)) != open_groups.end()) { + j = (*i).second.erase(j); + was_modified = true; + } else { + ++j; + } + } + if (was_modified) { + if ((*i).second.empty()) { + edge_0.push_back((*i).first); + } else if ((*i).second.size() == 1) { + edge_1.push_back((*i).first); + } + } + } + + for (std::list<vpair_t>::iterator i = edge_1.begin(); i != edge_1.end(); ++i) { + vpair_t e1 = *i; + edge_map_t::iterator e1i = complex_edges.find(e1); + if (e1i == complex_edges.end()) continue; + vpair_t e2 = vpair_t(e1.second, e1.first); + edge_map_t::iterator e2i = complex_edges.find(e2); + CARVE_ASSERT(e2i != complex_edges.end()); // each complex edge should have a mate. + + if ((*e2i).second.size() == 1) { + // merge newly simple edges, delete both from complex_edges. + edge_t *a = (*e1i).second.front(); + edge_t *b = (*e2i).second.front(); + a->rev = b; + b->rev = a; + face_groups.merge_sets(a->face->id, b->face->id); + complex_edges.erase(e1i); + complex_edges.erase(e2i); + } + } + + open_groups.clear(); + + for (std::list<vpair_t>::iterator i = edge_0.begin(); i != edge_0.end(); ++i) { + vpair_t e1 = *i; + edge_map_t::iterator e1i = complex_edges.find(e1); + vpair_t e2 = vpair_t(e1.second, e1.first); + edge_map_t::iterator e2i = complex_edges.find(e2); + if (e2i == complex_edges.end()) { + // This could occur, for example, when two faces share + // an edge in the same direction, but are both not + // touching anything else. Both get removed by the open + // group removal code, leaving an edge map with zero + // edges. The edge in the opposite direction does not + // exist, because there's no face that adjoins either of + // the two open faces. + continue; + } + + for (edgelist_t::iterator j = (*e2i).second.begin(); j != (*e2i).second.end(); ++j) { + open_groups.insert(faceGroupID(*j)); + } + complex_edges.erase(e1i); + complex_edges.erase(e2i); + } + } + } + + + + void FaceStitcher::extractConnectedEdges(std::vector<const vertex_t *>::iterator begin, + std::vector<const vertex_t *>::iterator end, + std::vector<std::vector<Edge<3> *> > &efwd, + std::vector<std::vector<Edge<3> *> > &erev) { + const size_t N = std::distance(begin, end) - 1; + + std::vector<const vertex_t *>::iterator e1, e2; + e1 = e2 = begin; ++e2; + vpair_t start_f = vpair_t(*e1, *e2); + vpair_t start_r = vpair_t(*e2, *e1); + + const size_t Nfwd = complex_edges[start_f].size(); + const size_t Nrev = complex_edges[start_r].size(); + + size_t j; + edgelist_t::iterator ji; + + efwd.clear(); efwd.resize(Nfwd); + erev.clear(); erev.resize(Nrev); + + for (j = 0, ji = complex_edges[start_f].begin(); + ji != complex_edges[start_f].end(); + ++j, ++ji) { + efwd[j].reserve(N); + efwd[j].push_back(*ji); + } + + for (j = 0, ji = complex_edges[start_r].begin(); + ji != complex_edges[start_r].end(); + ++j, ++ji) { + erev[j].reserve(N); + erev[j].push_back(*ji); + } + + std::vector<Edge<3> *> temp_f, temp_r; + temp_f.resize(Nfwd); + temp_r.resize(Nrev); + + for (j = 1; j < N; ++j) { + ++e1; ++e2; + vpair_t ef = vpair_t(*e1, *e2); + vpair_t er = vpair_t(*e2, *e1); + + if (complex_edges[ef].size() != Nfwd || complex_edges[ef].size() != Nrev) break; + + for (size_t k = 0; k < Nfwd; ++k) { + Edge<3> *e_next = efwd[k].back()->perimNext(); + CARVE_ASSERT(e_next == NULL || e_next->rev == NULL); + if (e_next == NULL || e_next->v2() != *e2) goto done; + CARVE_ASSERT(e_next->v1() == *e1); + CARVE_ASSERT(std::find(complex_edges[ef].begin(), complex_edges[ef].end(), e_next) != complex_edges[ef].end()); + temp_f[k] = e_next; + } + + for (size_t k = 0; k < Nrev; ++k) { + Edge<3> *e_next = erev[k].back()->perimPrev(); + if (e_next == NULL || e_next->v1() != *e2) goto done; + CARVE_ASSERT(e_next->v2() == *e1); + CARVE_ASSERT(std::find(complex_edges[er].begin(), complex_edges[er].end(), e_next) != complex_edges[er].end()); + temp_r[k] = e_next; + } + + for (size_t k = 0; k < Nfwd; ++k) { + efwd[k].push_back(temp_f[k]); + } + + for (size_t k = 0; k < Nrev; ++k) { + erev[k].push_back(temp_r[k]); + } + } + done:; + } + + + + void FaceStitcher::construct() { + matchSimpleEdges(); + if (!complex_edges.size()) return; + + resolveOpenEdges(); + if (!complex_edges.size()) return; + + buildEdgeGraph(complex_edges); + + std::list<std::vector<const vertex_t *> > paths; + + while (edge_graph.size()) { + paths.push_back(std::vector<const vertex_t *>()); + extractPath(paths.back()); + removePath(paths.back()); + }; + + + for (std::list<std::vector<const vertex_t *> >::iterator path = paths.begin(); path != paths.end(); ++path) { + for (size_t i = 0; i < (*path).size() - 1;) { + std::vector<std::vector<Edge<3> *> > efwd, erev; + + extractConnectedEdges((*path).begin() + i, (*path).end(), efwd, erev); + + std::vector<std::vector<EdgeOrderData> > orderings; + orderForwardAndReverseEdges(efwd, erev, orderings); + + matchOrderedEdges(orderings.begin(), orderings.end(), efwd, erev); + i += efwd[0].size(); + } + } + } + } + } + + + + // construct a MeshSet from a Polyhedron, maintaining on the + // connectivity information in the Polyhedron. + mesh::MeshSet<3> *meshFromPolyhedron(const poly::Polyhedron *poly, int manifold_id) { + typedef mesh::Vertex<3> vertex_t; + typedef mesh::Vertex<3>::vector_t vector_t; + typedef mesh::Edge<3> edge_t; + typedef mesh::Face<3> face_t; + typedef mesh::Mesh<3> mesh_t; + typedef mesh::MeshSet<3> meshset_t; + + std::vector<vertex_t> vertex_storage; + vertex_storage.reserve(poly->vertices.size()); + for (size_t i = 0; i < poly->vertices.size(); ++i) { + vertex_storage.push_back(vertex_t(poly->vertices[i].v)); + } + + std::vector<std::vector<face_t *> > faces; + faces.resize(poly->manifold_is_closed.size()); + + std::unordered_map<std::pair<size_t, size_t>, std::list<edge_t *> > vertex_to_edge; + + std::vector<vertex_t *> vert_ptrs; + for (size_t i = 0; i < poly->faces.size(); ++i) { + const poly::Polyhedron::face_t &src = poly->faces[i]; + if (manifold_id != -1 && src.manifold_id != manifold_id) continue; + vert_ptrs.clear(); + vert_ptrs.reserve(src.nVertices()); + for (size_t j = 0; j < src.nVertices(); ++j) { + size_t vi = poly->vertexToIndex_fast(src.vertex(j)); + vert_ptrs.push_back(&vertex_storage[vi]); + } + face_t *face = new face_t(vert_ptrs.begin(), vert_ptrs.end()); + face->id = src.manifold_id; + faces[src.manifold_id].push_back(face); + + edge_t *edge = face->edge; + do { + vertex_to_edge[std::make_pair(size_t(edge->v1() - &vertex_storage[0]), + size_t(edge->v2() - &vertex_storage[0]))].push_back(edge); + edge = edge->next; + } while (edge != face->edge); + } + + // copy connectivity from Polyhedron. + for (size_t i = 0; i < poly->edges.size(); ++i) { + const poly::Polyhedron::edge_t &src = poly->edges[i]; + size_t v1i = poly->vertexToIndex_fast(src.v1); + size_t v2i = poly->vertexToIndex_fast(src.v2); + + std::list<edge_t *> &efwd = vertex_to_edge[std::make_pair(v1i, v2i)]; + std::list<edge_t *> &erev = vertex_to_edge[std::make_pair(v2i, v1i)]; + + const std::vector<const poly::Polyhedron::face_t *> &facepairs = poly->connectivity.edge_to_face[i]; + for (size_t j = 0; j < facepairs.size(); j += 2) { + const poly::Polyhedron::face_t *fa, *fb; + fa = facepairs[j]; + fb = facepairs[j+1]; + if (!fa || !fb) continue; + CARVE_ASSERT(fa->manifold_id == fb->manifold_id); + if (manifold_id != -1 && fa->manifold_id != manifold_id) continue; + + std::list<edge_t *>::iterator efwdi, erevi; + for (efwdi = efwd.begin(); efwdi != efwd.end() && (*efwdi)->face->id != (size_t)fa->manifold_id; ++efwdi); + for (erevi = erev.begin(); erevi != erev.end() && (*erevi)->face->id != (size_t)fa->manifold_id; ++erevi); + CARVE_ASSERT(efwdi != efwd.end() && erevi != erev.end()); + + (*efwdi)->rev = (*erevi); + (*erevi)->rev = (*efwdi); + } + } + + std::vector<mesh_t *> meshes; + meshes.reserve(faces.size()); + for (size_t i = 0; i < faces.size(); ++i) { + if (faces[i].size()) { + meshes.push_back(new mesh_t(faces[i])); + } + } + + return new meshset_t(vertex_storage, meshes); + } + + + + static void copyMeshFaces(const mesh::Mesh<3> *mesh, + size_t manifold_id, + const mesh::Vertex<3> *Vbase, + poly::Polyhedron *poly, + std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> > &edges, + std::unordered_map<const mesh::Face<3> *, size_t> &face_map) { + std::vector<const poly::Polyhedron::vertex_t *> vert_ptr; + for (size_t f = 0; f < mesh->faces.size(); ++f) { + mesh::Face<3> *src = mesh->faces[f]; + vert_ptr.clear(); + vert_ptr.reserve(src->nVertices()); + mesh::Edge<3> *e = src->edge; + do { + vert_ptr.push_back(&poly->vertices[e->vert - Vbase]); + edges[std::make_pair(e->v1() - Vbase, e->v2() - Vbase)].push_back(e); + e = e->next; + } while (e != src->edge); + + face_map[src] = poly->faces.size();; + + poly->faces.push_back(poly::Polyhedron::face_t(vert_ptr)); + poly->faces.back().manifold_id = manifold_id; + poly->faces.back().owner = poly; + } + } + + + + // construct a Polyhedron from a MeshSet + poly::Polyhedron *polyhedronFromMesh(const mesh::MeshSet<3> *mesh, int manifold_id) { + typedef poly::Polyhedron poly_t; + typedef poly::Polyhedron::vertex_t vertex_t; + typedef poly::Polyhedron::edge_t edge_t; + typedef poly::Polyhedron::face_t face_t; + + poly::Polyhedron *poly = new poly::Polyhedron(); + const mesh::Vertex<3> *Vbase = &mesh->vertex_storage[0]; + + poly->vertices.reserve(mesh->vertex_storage.size()); + for (size_t i = 0; i < mesh->vertex_storage.size(); ++i) { + poly->vertices.push_back(vertex_t(mesh->vertex_storage[i].v)); + poly->vertices.back().owner = poly; + } + + size_t n_faces = 0; + if (manifold_id == -1) { + poly->manifold_is_closed.resize(mesh->meshes.size()); + poly->manifold_is_negative.resize(mesh->meshes.size()); + for (size_t m = 0; m < mesh->meshes.size(); ++m) { + n_faces += mesh->meshes[m]->faces.size(); + poly->manifold_is_closed[m] = mesh->meshes[m]->isClosed(); + poly->manifold_is_negative[m] = mesh->meshes[m]->isNegative(); + } + } else { + poly->manifold_is_closed.resize(1); + poly->manifold_is_negative.resize(1); + n_faces = mesh->meshes[manifold_id]->faces.size(); + poly->manifold_is_closed[manifold_id] = mesh->meshes[manifold_id]->isClosed(); + poly->manifold_is_negative[manifold_id] = mesh->meshes[manifold_id]->isNegative(); + } + + std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> > edges; + std::unordered_map<const mesh::Face<3> *, size_t> face_map; + poly->faces.reserve(n_faces); + + if (manifold_id == -1) { + for (size_t m = 0; m < mesh->meshes.size(); ++m) { + copyMeshFaces(mesh->meshes[m], m, Vbase, poly, edges, face_map); + } + } else { + copyMeshFaces(mesh->meshes[manifold_id], 0, Vbase, poly, edges, face_map); + } + + size_t n_edges = 0; + for (std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> >::iterator i = edges.begin(); i != edges.end(); ++i) { + if ((*i).first.first < (*i).first.second || edges.find(std::make_pair((*i).first.second, (*i).first.first)) == edges.end()) { + n_edges++; + } + } + + poly->edges.reserve(n_edges); + for (std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> >::iterator i = edges.begin(); i != edges.end(); ++i) { + if ((*i).first.first < (*i).first.second || + edges.find(std::make_pair((*i).first.second, (*i).first.first)) == edges.end()) { + poly->edges.push_back(edge_t(&poly->vertices[(*i).first.first], + &poly->vertices[(*i).first.second], + poly)); + } + } + + poly->initVertexConnectivity(); + + // build edge entries for face. + for (size_t f = 0; f < poly->faces.size(); ++f) { + face_t &face = poly->faces[f]; + size_t N = face.nVertices(); + for (size_t v = 0; v < N; ++v) { + size_t v1i = poly->vertexToIndex_fast(face.vertex(v)); + size_t v2i = poly->vertexToIndex_fast(face.vertex((v+1)%N)); + std::vector<const edge_t *> found_edge; + std::set_intersection(poly->connectivity.vertex_to_edge[v1i].begin(), poly->connectivity.vertex_to_edge[v1i].end(), + poly->connectivity.vertex_to_edge[v2i].begin(), poly->connectivity.vertex_to_edge[v2i].end(), + std::back_inserter(found_edge)); + CARVE_ASSERT(found_edge.size() == 1); + face.edge(v) = found_edge[0]; + } + } + + poly->connectivity.edge_to_face.resize(poly->edges.size()); + + for (size_t i = 0; i < poly->edges.size(); ++i) { + size_t v1i = poly->vertexToIndex_fast(poly->edges[i].v1); + size_t v2i = poly->vertexToIndex_fast(poly->edges[i].v2); + std::list<mesh::Edge<3> *> &efwd = edges[std::make_pair(v1i, v2i)]; + std::list<mesh::Edge<3> *> &erev = edges[std::make_pair(v1i, v2i)]; + + for (std::list<mesh::Edge<3> *>::iterator j = efwd.begin(); j != efwd.end(); ++j) { + mesh::Edge<3> *edge = *j; + if (face_map.find(edge->face) != face_map.end()) { + poly->connectivity.edge_to_face[i].push_back(&poly->faces[face_map[edge->face]]); + if (edge->rev == NULL) { + poly->connectivity.edge_to_face[i].push_back(NULL); + } else { + poly->connectivity.edge_to_face[i].push_back(&poly->faces[face_map[edge->rev->face]]); + } + } + } + for (std::list<mesh::Edge<3> *>::iterator j = erev.begin(); j != erev.end(); ++j) { + mesh::Edge<3> *edge = *j; + if (face_map.find(edge->face) != face_map.end()) { + if (edge->rev == NULL) { + poly->connectivity.edge_to_face[i].push_back(NULL); + poly->connectivity.edge_to_face[i].push_back(&poly->faces[face_map[edge->face]]); + } + } + } + + } + + poly->initSpatialIndex(); + + // XXX: at this point, manifold_is_negative is not set up. This + // info should be computed/stored in Mesh instances. + + return poly; + } + + + +} + + + +// explicit instantiation for 2D case. +// XXX: do not compile because of a missing definition for fitPlane in the 2d case. + +// template class carve::mesh::Vertex<2>; +// template class carve::mesh::Edge<2>; +// template class carve::mesh::Face<2>; +// template class carve::mesh::Mesh<2>; +// template class carve::mesh::MeshSet<2>; + +// explicit instantiation for 3D case. +template class carve::mesh::Vertex<3>; +template class carve::mesh::Edge<3>; +template class carve::mesh::Face<3>; +template class carve::mesh::Mesh<3>; +template class carve::mesh::MeshSet<3>; + + + +carve::PointClass carve::mesh::classifyPoint( + const carve::mesh::MeshSet<3> *meshset, + const carve::geom::RTreeNode<3, carve::mesh::Face<3> *> *face_rtree, + const carve::geom::vector<3> &v, + bool even_odd, + const carve::mesh::Mesh<3> *mesh, + const carve::mesh::Face<3> **hit_face) { + + if (hit_face) *hit_face = NULL; + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{containsVertex " << v << "}" << std::endl; +#endif + + if (!face_rtree->bbox.containsPoint(v)) { +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{final:OUT(aabb short circuit)}" << std::endl; +#endif + // XXX: if the top level manifolds are negative, this should be POINT_IN. + // for the moment, this only works for a single manifold. + if (meshset->meshes.size() == 1 && meshset->meshes[0]->isNegative()) { + return POINT_IN; + } + return POINT_OUT; + } + + std::vector<carve::mesh::Face<3> *> near_faces; + face_rtree->search(v, std::back_inserter(near_faces)); + + for (size_t i = 0; i < near_faces.size(); i++) { + if (mesh != NULL && mesh != near_faces[i]->mesh) continue; + + // XXX: Do allow the tested vertex to be ON an open + // manifold. This was here originally because of the + // possibility of an open manifold contained within a closed + // manifold. + + // if (!near_faces[i]->mesh->isClosed()) continue; + + if (near_faces[i]->containsPoint(v)) { +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{final:ON(hits face " << near_faces[i] << ")}" << std::endl; +#endif + if (hit_face) *hit_face = near_faces[i]; + return POINT_ON; + } + } + + double ray_len = face_rtree->bbox.extent.length() * 2; + + + std::vector<std::pair<const carve::mesh::Face<3> *, carve::geom::vector<3> > > manifold_intersections; + + for (;;) { + double a1 = random() / double(RAND_MAX) * M_TWOPI; + double a2 = random() / double(RAND_MAX) * M_TWOPI; + + carve::geom3d::Vector ray_dir = carve::geom::VECTOR(sin(a1) * sin(a2), cos(a1) * sin(a2), cos(a2)); + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{testing ray: " << ray_dir << "}" << std::endl; +#endif + + carve::geom::vector<3> v2 = v + ray_dir * ray_len; + + bool failed = false; + carve::geom::linesegment<3> line(v, v2); + carve::geom::vector<3> intersection; + + near_faces.clear(); + manifold_intersections.clear(); + face_rtree->search(line, std::back_inserter(near_faces)); + + for (unsigned i = 0; !failed && i < near_faces.size(); i++) { + if (mesh != NULL && mesh != near_faces[i]->mesh) continue; + + if (!near_faces[i]->mesh->isClosed()) continue; + + switch (near_faces[i]->lineSegmentIntersection(line, intersection)) { + case INTERSECT_FACE: { + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{intersects face: " << near_faces[i] + << " dp: " << dot(ray_dir, near_faces[i]->plane.N) << "}" << std::endl; +#endif + + if (!even_odd && fabs(dot(ray_dir, near_faces[i]->plane.N)) < EPSILON) { + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{failing(small dot product)}" << std::endl; +#endif + + failed = true; + break; + } + manifold_intersections.push_back(std::make_pair(near_faces[i], intersection)); + break; + } + case INTERSECT_NONE: { + break; + } + default: { + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{failing(degenerate intersection)}" << std::endl; +#endif + failed = true; + break; + } + } + } + + if (!failed) { + if (even_odd) { + return (manifold_intersections.size() & 1) ? POINT_IN : POINT_OUT; + } + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{intersections ok [count:" + << manifold_intersections.size() + << "], sorting}" + << std::endl; +#endif + + carve::geom3d::sortInDirectionOfRay(ray_dir, + manifold_intersections.begin(), + manifold_intersections.end(), + carve::geom3d::vec_adapt_pair_second()); + + std::map<const carve::mesh::Mesh<3> *, int> crossings; + + for (size_t i = 0; i < manifold_intersections.size(); ++i) { + const carve::mesh::Face<3> *f = manifold_intersections[i].first; + if (dot(ray_dir, f->plane.N) < 0.0) { + crossings[f->mesh]++; + } else { + crossings[f->mesh]--; + } + } + +#if defined(DEBUG_CONTAINS_VERTEX) + for (std::map<const carve::mesh::Mesh<3> *, int>::const_iterator i = crossings.begin(); i != crossings.end(); ++i) { + std::cerr << "{mesh " << (*i).first << " crossing count: " << (*i).second << "}" << std::endl; + } +#endif + + for (size_t i = 0; i < manifold_intersections.size(); ++i) { + const carve::mesh::Face<3> *f = manifold_intersections[i].first; + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{intersection at " + << manifold_intersections[i].second + << " mesh: " + << f->mesh + << " count: " + << crossings[f->mesh] + << "}" + << std::endl; +#endif + + if (crossings[f->mesh] < 0) { + // inside this manifold. + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{final:IN}" << std::endl; +#endif + + return POINT_IN; + } else if (crossings[f->mesh] > 0) { + // outside this manifold, but it's an infinite manifold. (for instance, an inverted cube) + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{final:OUT}" << std::endl; +#endif + + return POINT_OUT; + } + } + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{final:OUT(default)}" << std::endl; +#endif + + return POINT_OUT; + } + } +} + + + diff --git a/extern/carve/lib/octree.cpp b/extern/carve/lib/octree.cpp new file mode 100644 index 00000000000..900a9614f47 --- /dev/null +++ b/extern/carve/lib/octree.cpp @@ -0,0 +1,399 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/octree_decl.hpp> +#include <carve/octree_impl.hpp> + +#include <carve/poly_decl.hpp> + +namespace carve { + namespace csg { + + Octree::Node::Node(const carve::geom3d::Vector &newMin, const carve::geom3d::Vector &newMax) : + parent(NULL), is_leaf(true), min(newMin), max(newMax) { + for (int i = 0; i < 8; ++i) children[i] = NULL; + aabb = Octree::makeAABB(this); + } + + Octree::Node::Node(Node *p, double x1, double y1, double z1, double x2, double y2, double z2) : + parent(p), is_leaf(true), min(carve::geom::VECTOR(x1, y1, z1)), max(carve::geom::VECTOR(x2, y2, z2)) { + for (int i = 0; i < 8; ++i) children[i] = NULL; + aabb = Octree::makeAABB(this); + } + + Octree::Node::~Node() { + for (int i = 0; i < 8; ++i) { + if (children[i] != NULL) { + (*children[i]).~Node(); + } + } + if (children[0] != NULL) { + char *ptr = (char*)children[0]; + delete[] ptr; + } + } + + bool Octree::Node::mightContain(const carve::poly::Face<3> &face) { + if (face.nVertices() == 3) { + return aabb.intersects(carve::geom::tri<3>(face.vertex(0)->v, face.vertex(1)->v, face.vertex(2)->v)); + } else { + return aabb.intersects(face.aabb) && aabb.intersects(face.plane_eqn); + } + } + + bool Octree::Node::mightContain(const carve::poly::Edge<3> &edge) { + return aabb.intersectsLineSegment(edge.v1->v, edge.v2->v); + } + + bool Octree::Node::mightContain(const carve::poly::Vertex<3> &p) { + return aabb.containsPoint(p.v); + } + + bool Octree::Node::hasChildren() { + return !is_leaf; + } + + bool Octree::Node::split() { + if (is_leaf && hasGeometry()) { + + carve::geom3d::Vector mid = 0.5 * (min + max); + char *ptr = new char[sizeof(Node)*8]; + children[0] = new (ptr + sizeof(Node) * 0) Node(this, min.x, min.y, min.z, mid.x, mid.y, mid.z); + children[1] = new (ptr + sizeof(Node) * 1) Node(this, mid.x, min.y, min.z, max.x, mid.y, mid.z); + children[2] = new (ptr + sizeof(Node) * 2) Node(this, min.x, mid.y, min.z, mid.x, max.y, mid.z); + children[3] = new (ptr + sizeof(Node) * 3) Node(this, mid.x, mid.y, min.z, max.x, max.y, mid.z); + children[4] = new (ptr + sizeof(Node) * 4) Node(this, min.x, min.y, mid.z, mid.x, mid.y, max.z); + children[5] = new (ptr + sizeof(Node) * 5) Node(this, mid.x, min.y, mid.z, max.x, mid.y, max.z); + children[6] = new (ptr + sizeof(Node) * 6) Node(this, min.x, mid.y, mid.z, mid.x, max.y, max.z); + children[7] = new (ptr + sizeof(Node) * 7) Node(this, mid.x, mid.y, mid.z, max.x, max.y, max.z); + + for (int i = 0; i < 8; ++i) { + putInside(faces, children[i], children[i]->faces); + putInside(edges, children[i], children[i]->edges); + putInside(vertices, children[i], children[i]->vertices); + } + + faces.clear(); + edges.clear(); + vertices.clear(); + is_leaf = false; + } + return is_leaf; + } + + template <class T> + void Octree::Node::putInside(const T &input, Node *child, T &output) { + for (typename T::const_iterator it = input.begin(), e = input.end(); it != e; ++it) { + if (child->mightContain(**it)) { + output.push_back(*it); + } + } + } + + bool Octree::Node::hasGeometry() { + return faces.size() > 0 || edges.size() > 0 || vertices.size() > 0; + } + + Octree::Octree() { + root = NULL; + } + + Octree::~Octree() { + if (root) delete root; + } + + void Octree::setBounds(const carve::geom3d::Vector &min, const carve::geom3d::Vector &max) { + if (root) delete root; + root = new Node(min, max); + } + + void Octree::setBounds(carve::geom3d::AABB aabb) { + if (root) delete root; + aabb.extent = 1.1 * aabb.extent; + root = new Node(aabb.min(), aabb.max()); + } + + void Octree::addEdges(const std::vector<carve::poly::Edge<3> > &e) { + root->edges.reserve(root->edges.size() + e.size()); + for (size_t i = 0; i < e.size(); ++i) { + root->edges.push_back(&e[i]); + } + } + + void Octree::addFaces(const std::vector<carve::poly::Face<3> > &f) { + root->faces.reserve(root->faces.size() + f.size()); + for (size_t i = 0; i < f.size(); ++i) { + root->faces.push_back(&f[i]); + } + } + + void Octree::addVertices(const std::vector<const carve::poly::Vertex<3> *> &p) { + root->vertices.insert(root->vertices.end(), p.begin(), p.end()); + } + + carve::geom3d::AABB Octree::makeAABB(const Node *node) { + carve::geom3d::Vector centre = 0.5 * (node->min + node->max); + carve::geom3d::Vector size = SLACK_FACTOR * 0.5 * (node->max - node->min); + return carve::geom3d::AABB(centre, size); + } + + void Octree::doFindEdges(const carve::geom::aabb<3> &aabb, + Node *node, + std::vector<const carve::poly::Edge<3> *> &out, + unsigned depth) const { + if (node == NULL) { + return; + } + + if (node->aabb.intersects(aabb)) { + if (node->hasChildren()) { + for (int i = 0; i < 8; ++i) { + doFindEdges(aabb, node->children[i], out, depth + 1); + } + } else { + if (depth < MAX_SPLIT_DEPTH && node->edges.size() > EDGE_SPLIT_THRESHOLD) { + if (!node->split()) { + for (int i = 0; i < 8; ++i) { + doFindEdges(aabb, node->children[i], out, depth + 1); + } + return; + } + } + for (std::vector<const carve::poly::Edge<3>*>::const_iterator it = node->edges.begin(), e = node->edges.end(); it != e; ++it) { + if ((*it)->tag_once()) { + out.push_back(*it); + } + } + } + } + } + + void Octree::doFindEdges(const carve::geom3d::LineSegment &l, + Node *node, + std::vector<const carve::poly::Edge<3> *> &out, + unsigned depth) const { + if (node == NULL) { + return; + } + + if (node->aabb.intersectsLineSegment(l.v1, l.v2)) { + if (node->hasChildren()) { + for (int i = 0; i < 8; ++i) { + doFindEdges(l, node->children[i], out, depth + 1); + } + } else { + if (depth < MAX_SPLIT_DEPTH && node->edges.size() > EDGE_SPLIT_THRESHOLD) { + if (!node->split()) { + for (int i = 0; i < 8; ++i) { + doFindEdges(l, node->children[i], out, depth + 1); + } + return; + } + } + for (std::vector<const carve::poly::Edge<3>*>::const_iterator it = node->edges.begin(), e = node->edges.end(); it != e; ++it) { + if ((*it)->tag_once()) { + out.push_back(*it); + } + } + } + } + } + + void Octree::doFindEdges(const carve::geom3d::Vector &v, + Node *node, + std::vector<const carve::poly::Edge<3> *> &out, + unsigned depth) const { + if (node == NULL) { + return; + } + + if (node->aabb.containsPoint(v)) { + if (node->hasChildren()) { + for (int i = 0; i < 8; ++i) { + doFindEdges(v, node->children[i], out, depth + 1); + } + } else { + if (depth < MAX_SPLIT_DEPTH && node->edges.size() > EDGE_SPLIT_THRESHOLD) { + if (!node->split()) { + for (int i = 0; i < 8; ++i) { + doFindEdges(v, node->children[i], out, depth + 1); + } + return; + } + } + for (std::vector<const carve::poly::Edge<3>*>::const_iterator + it = node->edges.begin(), e = node->edges.end(); it != e; ++it) { + if ((*it)->tag_once()) { + out.push_back(*it); + } + } + } + } + } + + void Octree::doFindFaces(const carve::geom::aabb<3> &aabb, + Node *node, + std::vector<const carve::poly::Face<3>*> &out, + unsigned depth) const { + if (node == NULL) { + return; + } + + if (node->aabb.intersects(aabb)) { + if (node->hasChildren()) { + for (int i = 0; i < 8; ++i) { + doFindFaces(aabb, node->children[i], out, depth + 1); + } + } else { + if (depth < MAX_SPLIT_DEPTH && node->faces.size() > FACE_SPLIT_THRESHOLD) { + if (!node->split()) { + for (int i = 0; i < 8; ++i) { + doFindFaces(aabb, node->children[i], out, depth + 1); + } + return; + } + } + for (std::vector<const carve::poly::Face<3>*>::const_iterator it = node->faces.begin(), e = node->faces.end(); it != e; ++it) { + if ((*it)->tag_once()) { + out.push_back(*it); + } + } + } + } + } + + void Octree::doFindFaces(const carve::geom3d::LineSegment &l, + Node *node, + std::vector<const carve::poly::Face<3>*> &out, + unsigned depth) const { + if (node == NULL) { + return; + } + + if (node->aabb.intersectsLineSegment(l.v1, l.v2)) { + if (node->hasChildren()) { + for (int i = 0; i < 8; ++i) { + doFindFaces(l, node->children[i], out, depth + 1); + } + } else { + if (depth < MAX_SPLIT_DEPTH && node->faces.size() > FACE_SPLIT_THRESHOLD) { + if (!node->split()) { + for (int i = 0; i < 8; ++i) { + doFindFaces(l, node->children[i], out, depth + 1); + } + return; + } + } + for (std::vector<const carve::poly::Face<3>*>::const_iterator it = node->faces.begin(), e = node->faces.end(); it != e; ++it) { + if ((*it)->tag_once()) { + out.push_back(*it); + } + } + } + } + } + + void Octree::doFindVerticesAllowDupes(const carve::geom3d::Vector &v, Node *node, std::vector<const carve::poly::Vertex<3> *> &out, unsigned depth) const { + if (node == NULL) { + return; + } + + if (node->aabb.containsPoint(v)) { + if (node->hasChildren()) { + for (int i = 0; i < 8; ++i) { + doFindVerticesAllowDupes(v, node->children[i], out, depth + 1); + } + } else { + if (depth < MAX_SPLIT_DEPTH && node->vertices.size() > POINT_SPLIT_THRESHOLD) { + if (!node->split()) { + for (int i = 0; i < 8; ++i) { + doFindVerticesAllowDupes(v, node->children[i], out, depth + 1); + } + return; + } + } + for (std::vector<const carve::poly::Vertex<3> *>::const_iterator it = node->vertices.begin(), e = node->vertices.end(); it != e; ++it) { + out.push_back(*it); + } + } + } + } + + void Octree::findEdgesNear(const carve::geom::aabb<3> &aabb, std::vector<const carve::poly::Edge<3>*> &out) const { + tagable::tag_begin(); + doFindEdges(aabb, root, out, 0); + } + + void Octree::findEdgesNear(const carve::geom3d::LineSegment &l, std::vector<const carve::poly::Edge<3>*> &out) const { + tagable::tag_begin(); + doFindEdges(l, root, out, 0); + } + + void Octree::findEdgesNear(const carve::poly::Edge<3> &e, std::vector<const carve::poly::Edge<3>*> &out) const { + tagable::tag_begin(); + doFindEdges(carve::geom3d::LineSegment(e.v1->v, e.v2->v), root, out, 0); + } + + void Octree::findEdgesNear(const carve::geom3d::Vector &v, std::vector<const carve::poly::Edge<3>*> &out) const { + tagable::tag_begin(); + doFindEdges(v, root, out, 0); + } + + void Octree::findFacesNear(const carve::geom::aabb<3> &aabb, std::vector<const carve::poly::Face<3>*> &out) const { + tagable::tag_begin(); + doFindFaces(aabb, root, out, 0); + } + + void Octree::findFacesNear(const carve::geom3d::LineSegment &l, std::vector<const carve::poly::Face<3>*> &out) const { + tagable::tag_begin(); + doFindFaces(l, root, out, 0); + } + + void Octree::findFacesNear(const carve::poly::Edge<3> &e, std::vector<const carve::poly::Face<3>*> &out) const { + tagable::tag_begin(); + doFindFaces(carve::geom3d::LineSegment(e.v1->v, e.v2->v), root, out, 0); + } + + void Octree::findVerticesNearAllowDupes(const carve::geom3d::Vector &v, std::vector<const carve::poly::Vertex<3> *> &out) const { + tagable::tag_begin(); + doFindVerticesAllowDupes(v, root, out, 0); + } + + void Octree::doSplit(int maxSplit, Node *node) { + // Don't split down any further than 4 levels. + if (maxSplit <= 0 || (node->edges.size() < 5 && node->faces.size() < 5)) { + return; + } + + if (!node->split()) { + for (int i = 0; i < 8; ++i) { + doSplit(maxSplit - 1, node->children[i]); + } + } + } + + void Octree::splitTree() { + // initially split 4 levels + doSplit(0, root); + } + + } +} diff --git a/extern/carve/lib/pointset.cpp b/extern/carve/lib/pointset.cpp new file mode 100644 index 00000000000..7ecf0074c69 --- /dev/null +++ b/extern/carve/lib/pointset.cpp @@ -0,0 +1,59 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/geom.hpp> +#include <carve/pointset.hpp> + +namespace carve { + namespace point { + + PointSet::PointSet(const std::vector<carve::geom3d::Vector> &points) { + vertices.resize(points.size()); + for (size_t i = 0; i < points.size(); ++i) { + vertices[i].v = points[i]; + } + aabb.fit(points.begin(), points.end()); + } + + void PointSet::sortVertices(const carve::geom3d::Vector &axis) { + std::vector<std::pair<double, size_t> > temp; + temp.reserve(vertices.size()); + for (size_t i = 0; i < vertices.size(); ++i) { + temp.push_back(std::make_pair(dot(axis, vertices[i].v), i)); + } + std::sort(temp.begin(), temp.end()); + + std::vector<Vertex> vnew; + vnew.reserve(vertices.size()); + + // std::vector<int> revmap; + // revmap.resize(vertices.size()); + + for (size_t i = 0; i < vertices.size(); ++i) { + vnew.push_back(vertices[temp[i].second]); + // revmap[temp[i].second] = i; + } + + vertices.swap(vnew); + } + + } +} diff --git a/extern/carve/lib/polyhedron.cpp b/extern/carve/lib/polyhedron.cpp new file mode 100644 index 00000000000..93e667ffaf7 --- /dev/null +++ b/extern/carve/lib/polyhedron.cpp @@ -0,0 +1,1103 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#if defined(CARVE_DEBUG) +#define DEBUG_CONTAINS_VERTEX +#endif + +#include <carve/djset.hpp> + +#include <carve/geom.hpp> +#include <carve/poly.hpp> + +#include <carve/octree_impl.hpp> + +#include <carve/timing.hpp> + +#include <algorithm> + +#include <carve/mesh.hpp> + +#include BOOST_INCLUDE(random.hpp) + +namespace { + bool emb_test(carve::poly::Polyhedron *poly, + std::map<int, std::set<int> > &embedding, + carve::geom3d::Vector v, + int m_id) { + + std::map<int, carve::PointClass> result; +#if defined(CARVE_DEBUG) + std::cerr << "test " << v << " (m_id:" << m_id << ")" << std::endl; +#endif + poly->testVertexAgainstClosedManifolds(v, result, true); + std::set<int> inside; + for (std::map<int, carve::PointClass>::iterator j = result.begin(); + j != result.end(); + ++j) { + if ((*j).first == m_id) continue; + if ((*j).second == carve::POINT_IN) inside.insert((*j).first); + else if ((*j).second == carve::POINT_ON) { +#if defined(CARVE_DEBUG) + std::cerr << " FAIL" << std::endl; +#endif + return false; + } + } +#if defined(CARVE_DEBUG) + std::cerr << " OK (inside.size()==" << inside.size() << ")" << std::endl; +#endif + embedding[m_id] = inside; + return true; + } + + + + struct order_faces { + bool operator()(const carve::poly::Polyhedron::face_t * const &a, + const carve::poly::Polyhedron::face_t * const &b) const { + return std::lexicographical_compare(a->vbegin(), a->vend(), b->vbegin(), b->vend()); + } + }; + + + +} + + + +namespace carve { + namespace poly { + + + + bool Polyhedron::initSpatialIndex() { + static carve::TimingName FUNC_NAME("Polyhedron::initSpatialIndex()"); + carve::TimingBlock block(FUNC_NAME); + + octree.setBounds(aabb); + octree.addFaces(faces); + octree.addEdges(edges); + octree.splitTree(); + + return true; + } + + + + void Polyhedron::invertAll() { + for (size_t i = 0; i < faces.size(); ++i) { + faces[i].invert(); + } + + for (size_t i = 0; i < edges.size(); ++i) { + std::vector<const face_t *> &f = connectivity.edge_to_face[i]; + for (size_t j = 0; j < (f.size() & ~1U); j += 2) { + std::swap(f[j], f[j+1]); + } + } + + for (size_t i = 0; i < manifold_is_negative.size(); ++i) { + manifold_is_negative[i] = !manifold_is_negative[i]; + } + } + + + + void Polyhedron::invert(const std::vector<bool> &selected_manifolds) { + bool altered = false; + for (size_t i = 0; i < faces.size(); ++i) { + if (faces[i].manifold_id >= 0 && + (unsigned)faces[i].manifold_id < selected_manifolds.size() && + selected_manifolds[faces[i].manifold_id]) { + altered = true; + faces[i].invert(); + } + } + + if (altered) { + for (size_t i = 0; i < edges.size(); ++i) { + std::vector<const face_t *> &f = connectivity.edge_to_face[i]; + for (size_t j = 0; j < (f.size() & ~1U); j += 2) { + int m_id = -1; + if (f[j]) m_id = f[j]->manifold_id; + if (f[j+1]) m_id = f[j+1]->manifold_id; + if (m_id >= 0 && (unsigned)m_id < selected_manifolds.size() && selected_manifolds[m_id]) { + std::swap(f[j], f[j+1]); + } + } + } + + for (size_t i = 0; i < std::min(selected_manifolds.size(), manifold_is_negative.size()); ++i) { + manifold_is_negative[i] = !manifold_is_negative[i]; + } + } + } + + + + void Polyhedron::initVertexConnectivity() { + static carve::TimingName FUNC_NAME("static Polyhedron initVertexConnectivity()"); + carve::TimingBlock block(FUNC_NAME); + + // allocate space for connectivity info. + connectivity.vertex_to_edge.resize(vertices.size()); + connectivity.vertex_to_face.resize(vertices.size()); + + std::vector<size_t> vertex_face_count; + + vertex_face_count.resize(vertices.size()); + + // work out how many faces/edges each vertex is connected to, in + // order to save on array reallocs. + for (unsigned i = 0; i < faces.size(); ++i) { + face_t &f = faces[i]; + for (unsigned j = 0; j < f.nVertices(); j++) { + vertex_face_count[vertexToIndex_fast(f.vertex(j))]++; + } + } + + for (size_t i = 0; i < vertices.size(); ++i) { + connectivity.vertex_to_edge[i].reserve(vertex_face_count[i]); + connectivity.vertex_to_face[i].reserve(vertex_face_count[i]); + } + + // record connectivity from vertex to edges. + for (size_t i = 0; i < edges.size(); ++i) { + size_t v1i = vertexToIndex_fast(edges[i].v1); + size_t v2i = vertexToIndex_fast(edges[i].v2); + + connectivity.vertex_to_edge[v1i].push_back(&edges[i]); + connectivity.vertex_to_edge[v2i].push_back(&edges[i]); + } + + // record connectivity from vertex to faces. + for (size_t i = 0; i < faces.size(); ++i) { + face_t &f = faces[i]; + for (unsigned j = 0; j < f.nVertices(); j++) { + size_t vi = vertexToIndex_fast(f.vertex(j)); + connectivity.vertex_to_face[vi].push_back(&f); + } + } + } + + + + bool Polyhedron::initConnectivity() { + static carve::TimingName FUNC_NAME("Polyhedron::initConnectivity()"); + carve::TimingBlock block(FUNC_NAME); + + // temporary measure: initialize connectivity by creating a + // half-edge mesh, and then converting back. + + std::vector<mesh::Vertex<3> > vertex_storage; + vertex_storage.reserve(vertices.size()); + for (size_t i = 0; i < vertices.size(); ++i) { + vertex_storage.push_back(mesh::Vertex<3>(vertices[i].v)); + } + + std::vector<mesh::Face<3> *> mesh_faces; + std::unordered_map<const mesh::Face<3> *, size_t> face_map; + { + std::vector<mesh::Vertex<3> *> vert_ptrs; + for (size_t i = 0; i < faces.size(); ++i) { + const face_t &src = faces[i]; + vert_ptrs.clear(); + vert_ptrs.reserve(src.nVertices()); + for (size_t j = 0; j < src.nVertices(); ++j) { + size_t vi = vertexToIndex_fast(src.vertex(j)); + vert_ptrs.push_back(&vertex_storage[vi]); + } + mesh::Face<3> *face = new mesh::Face<3>(vert_ptrs.begin(), vert_ptrs.end()); + mesh_faces.push_back(face); + face_map[face] = i; + } + } + + std::vector<mesh::Mesh<3> *> meshes; + mesh::Mesh<3>::create(mesh_faces.begin(), mesh_faces.end(), meshes); + mesh::MeshSet<3> *meshset = new mesh::MeshSet<3>(vertex_storage, meshes); + + manifold_is_closed.resize(meshset->meshes.size()); + manifold_is_negative.resize(meshset->meshes.size()); + + std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> > edge_map; + + if (meshset->vertex_storage.size()) { + mesh::Vertex<3> *Vbase = &meshset->vertex_storage[0]; + for (size_t m = 0; m < meshset->meshes.size(); ++m) { + mesh::Mesh<3> *mesh = meshset->meshes[m]; + manifold_is_closed[m] = mesh->isClosed(); + for (size_t f = 0; f < mesh->faces.size(); ++f) { + mesh::Face<3> *src = mesh->faces[f]; + mesh::Edge<3> *e = src->edge; + faces[face_map[src]].manifold_id = m; + do { + edge_map[std::make_pair(e->v1() - Vbase, e->v2() - Vbase)].push_back(e); + e = e->next; + } while (e != src->edge); + } + } + } + + size_t n_edges = 0; + for (std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> >::iterator i = edge_map.begin(); i != edge_map.end(); ++i) { + if ((*i).first.first < (*i).first.second || edge_map.find(std::make_pair((*i).first.second, (*i).first.first)) == edge_map.end()) { + n_edges++; + } + } + + edges.clear(); + edges.reserve(n_edges); + for (std::unordered_map<std::pair<size_t, size_t>, std::list<mesh::Edge<3> *> >::iterator i = edge_map.begin(); i != edge_map.end(); ++i) { + if ((*i).first.first < (*i).first.second || edge_map.find(std::make_pair((*i).first.second, (*i).first.first)) == edge_map.end()) { + edges.push_back(edge_t(&vertices[(*i).first.first], &vertices[(*i).first.second], this)); + } + } + + initVertexConnectivity(); + + for (size_t f = 0; f < faces.size(); ++f) { + face_t &face = faces[f]; + size_t N = face.nVertices(); + for (size_t v = 0; v < N; ++v) { + size_t v1i = vertexToIndex_fast(face.vertex(v)); + size_t v2i = vertexToIndex_fast(face.vertex((v+1)%N)); + std::vector<const edge_t *> found_edge; + + CARVE_ASSERT(carve::is_sorted(connectivity.vertex_to_edge[v1i].begin(), connectivity.vertex_to_edge[v1i].end())); + CARVE_ASSERT(carve::is_sorted(connectivity.vertex_to_edge[v2i].begin(), connectivity.vertex_to_edge[v2i].end())); + + std::set_intersection(connectivity.vertex_to_edge[v1i].begin(), connectivity.vertex_to_edge[v1i].end(), + connectivity.vertex_to_edge[v2i].begin(), connectivity.vertex_to_edge[v2i].end(), + std::back_inserter(found_edge)); + + CARVE_ASSERT(found_edge.size() == 1); + + face.edge(v) = found_edge[0]; + } + } + + connectivity.edge_to_face.resize(edges.size()); + + for (size_t i = 0; i < edges.size(); ++i) { + size_t v1i = vertexToIndex_fast(edges[i].v1); + size_t v2i = vertexToIndex_fast(edges[i].v2); + std::list<mesh::Edge<3> *> &efwd = edge_map[std::make_pair(v1i, v2i)]; + std::list<mesh::Edge<3> *> &erev = edge_map[std::make_pair(v1i, v2i)]; + + for (std::list<mesh::Edge<3> *>::iterator j = efwd.begin(); j != efwd.end(); ++j) { + mesh::Edge<3> *edge = *j; + if (face_map.find(edge->face) != face_map.end()) { + connectivity.edge_to_face[i].push_back(&faces[face_map[edge->face]]); + if (edge->rev == NULL) { + connectivity.edge_to_face[i].push_back(NULL); + } else { + connectivity.edge_to_face[i].push_back(&faces[face_map[edge->rev->face]]); + } + } + } + for (std::list<mesh::Edge<3> *>::iterator j = erev.begin(); j != erev.end(); ++j) { + mesh::Edge<3> *edge = *j; + if (face_map.find(edge->face) != face_map.end()) { + if (edge->rev == NULL) { + connectivity.edge_to_face[i].push_back(NULL); + connectivity.edge_to_face[i].push_back(&faces[face_map[edge->face]]); + } + } + } + } + + delete meshset; + + return true; + } + + + + bool Polyhedron::calcManifoldEmbedding() { + // this could be significantly sped up using bounding box tests + // to work out what pairs of manifolds are embedding candidates. + // A per-manifold AABB could also be used to speed up + // testVertexAgainstClosedManifolds(). + + static carve::TimingName FUNC_NAME("Polyhedron::calcManifoldEmbedding()"); + static carve::TimingName CME_V("Polyhedron::calcManifoldEmbedding() (vertices)"); + static carve::TimingName CME_E("Polyhedron::calcManifoldEmbedding() (edges)"); + static carve::TimingName CME_F("Polyhedron::calcManifoldEmbedding() (faces)"); + + carve::TimingBlock block(FUNC_NAME); + + const unsigned MCOUNT = manifoldCount(); + if (MCOUNT < 2) return true; + + std::set<int> vertex_manifolds; + std::map<int, std::set<int> > embedding; + + carve::Timing::start(CME_V); + for (size_t i = 0; i < vertices.size(); ++i) { + vertex_manifolds.clear(); + if (vertexManifolds(&vertices[i], set_inserter(vertex_manifolds)) != 1) continue; + int m_id = *vertex_manifolds.begin(); + if (embedding.find(m_id) == embedding.end()) { + if (emb_test(this, embedding, vertices[i].v, m_id) && embedding.size() == MCOUNT) { + carve::Timing::stop(); + goto done; + } + } + } + carve::Timing::stop(); + + carve::Timing::start(CME_E); + for (size_t i = 0; i < edges.size(); ++i) { + if (connectivity.edge_to_face[i].size() == 2) { + int m_id; + const face_t *f1 = connectivity.edge_to_face[i][0]; + const face_t *f2 = connectivity.edge_to_face[i][1]; + if (f1) m_id = f1->manifold_id; + if (f2) m_id = f2->manifold_id; + if (embedding.find(m_id) == embedding.end()) { + if (emb_test(this, embedding, (edges[i].v1->v + edges[i].v2->v) / 2, m_id) && embedding.size() == MCOUNT) { + carve::Timing::stop(); + goto done; + } + } + } + } + carve::Timing::stop(); + + carve::Timing::start(CME_F); + for (size_t i = 0; i < faces.size(); ++i) { + int m_id = faces[i].manifold_id; + if (embedding.find(m_id) == embedding.end()) { + carve::geom2d::P2 pv; + if (!carve::geom2d::pickContainedPoint(faces[i].projectedVertices(), pv)) continue; + carve::geom3d::Vector v = carve::poly::face::unproject(faces[i], pv); + if (emb_test(this, embedding, v, m_id) && embedding.size() == MCOUNT) { + carve::Timing::stop(); + goto done; + } + } + } + carve::Timing::stop(); + + CARVE_FAIL("could not find test points"); + + // std::cerr << "could not find test points!!!" << std::endl; + // return true; + done:; + for (std::map<int, std::set<int> >::iterator i = embedding.begin(); i != embedding.end(); ++i) { +#if defined(CARVE_DEBUG) + std::cerr << (*i).first << " : "; + std::copy((*i).second.begin(), (*i).second.end(), std::ostream_iterator<int>(std::cerr, ",")); + std::cerr << std::endl; +#endif + (*i).second.insert(-1); + } + std::set<int> parents, new_parents; + parents.insert(-1); + + while (embedding.size()) { + new_parents.clear(); + for (std::map<int, std::set<int> >::iterator i = embedding.begin(); i != embedding.end(); ++i) { + if ((*i).second.size() == 1) { + if (parents.find(*(*i).second.begin()) != parents.end()) { + new_parents.insert((*i).first); +#if defined(CARVE_DEBUG) + std::cerr << "parent(" << (*i).first << "): " << *(*i).second.begin() << std::endl; +#endif + } else { +#if defined(CARVE_DEBUG) + std::cerr << "no parent: " << (*i).first << " (looking for: " << *(*i).second.begin() << ")" << std::endl; +#endif + } + } + } + for (std::set<int>::const_iterator i = new_parents.begin(); i != new_parents.end(); ++i) { + embedding.erase(*i); + } + for (std::map<int, std::set<int> >::iterator i = embedding.begin(); i != embedding.end(); ++i) { + size_t n = 0; + for (std::set<int>::const_iterator j = parents.begin(); j != parents.end(); ++j) { + n += (*i).second.erase((*j)); + } + CARVE_ASSERT(n != 0); + } + parents.swap(new_parents); + } + + return true; + } + + + + bool Polyhedron::init() { + static carve::TimingName FUNC_NAME("Polyhedron::init()"); + carve::TimingBlock block(FUNC_NAME); + + aabb.fit(vertices.begin(), vertices.end(), vec_adapt_vertex_ref()); + + connectivity.vertex_to_edge.clear(); + connectivity.vertex_to_face.clear(); + connectivity.edge_to_face.clear(); + + if (!initConnectivity()) return false; + if (!initSpatialIndex()) return false; + + return true; + } + + + + void Polyhedron::faceRecalc() { + for (size_t i = 0; i < faces.size(); ++i) { + if (!faces[i].recalc()) { + std::ostringstream out; + out << "face " << i << " recalc failed"; + throw carve::exception(out.str()); + } + } + } + + + + Polyhedron::Polyhedron(const Polyhedron &poly) { + faces.reserve(poly.faces.size()); + + for (size_t i = 0; i < poly.faces.size(); ++i) { + const face_t &src = poly.faces[i]; + faces.push_back(src); + } + commonFaceInit(false); // calls setFaceAndVertexOwner() and init() + } + + + + Polyhedron::Polyhedron(const Polyhedron &poly, const std::vector<bool> &selected_manifolds) { + size_t n_faces = 0; + + for (size_t i = 0; i < poly.faces.size(); ++i) { + const face_t &src = poly.faces[i]; + if (src.manifold_id >= 0 && + (unsigned)src.manifold_id < selected_manifolds.size() && + selected_manifolds[src.manifold_id]) { + n_faces++; + } + } + + faces.reserve(n_faces); + + for (size_t i = 0; i < poly.faces.size(); ++i) { + const face_t &src = poly.faces[i]; + if (src.manifold_id >= 0 && + (unsigned)src.manifold_id < selected_manifolds.size() && + selected_manifolds[src.manifold_id]) { + faces.push_back(src); + } + } + + commonFaceInit(false); // calls setFaceAndVertexOwner() and init() + } + + + + Polyhedron::Polyhedron(const Polyhedron &poly, int m_id) { + size_t n_faces = 0; + + for (size_t i = 0; i < poly.faces.size(); ++i) { + const face_t &src = poly.faces[i]; + if (src.manifold_id == m_id) n_faces++; + } + + faces.reserve(n_faces); + + for (size_t i = 0; i < poly.faces.size(); ++i) { + const face_t &src = poly.faces[i]; + if (src.manifold_id == m_id) faces.push_back(src); + } + + commonFaceInit(false); // calls setFaceAndVertexOwner() and init() + } + + + + Polyhedron::Polyhedron(const std::vector<carve::geom3d::Vector> &_vertices, + int n_faces, + const std::vector<int> &face_indices) { + // The polyhedron is defined by a vector of vertices, which we + // want to copy, and a face index list, from which we need to + // generate a set of Faces. + + vertices.clear(); + vertices.resize(_vertices.size()); + for (size_t i = 0; i < _vertices.size(); ++i) { + vertices[i].v = _vertices[i]; + } + + faces.reserve(n_faces); + + std::vector<int>::const_iterator iter = face_indices.begin(); + std::vector<const vertex_t *> v; + for (int i = 0; i < n_faces; ++i) { + int vertexCount = *iter++; + + v.clear(); + + while (vertexCount--) { + CARVE_ASSERT(*iter >= 0); + CARVE_ASSERT((unsigned)*iter < vertices.size()); + v.push_back(&vertices[*iter++]); + } + faces.push_back(face_t(v)); + } + + setFaceAndVertexOwner(); + + if (!init()) { + throw carve::exception("polyhedron creation failed"); + } + } + + + + Polyhedron::Polyhedron(std::vector<face_t> &_faces, + std::vector<vertex_t> &_vertices, + bool _recalc) { + faces.swap(_faces); + vertices.swap(_vertices); + + setFaceAndVertexOwner(); + + if (_recalc) faceRecalc(); + + if (!init()) { + throw carve::exception("polyhedron creation failed"); + } + } + + + + Polyhedron::Polyhedron(std::vector<face_t> &_faces, + bool _recalc) { + faces.swap(_faces); + commonFaceInit(_recalc); // calls setFaceAndVertexOwner() and init() + } + + + + Polyhedron::Polyhedron(std::list<face_t> &_faces, + bool _recalc) { + faces.reserve(_faces.size()); + std::copy(_faces.begin(), _faces.end(), std::back_inserter(faces)); + commonFaceInit(_recalc); // calls setFaceAndVertexOwner() and init() + } + + + + void Polyhedron::collectFaceVertices(std::vector<face_t> &faces, + std::vector<vertex_t> &vertices, + std::unordered_map<const vertex_t *, const vertex_t *> &vmap) { + // Given a set of faces, copy all referenced vertices into a + // single vertex array and update the faces to point into that + // array. On exit, vmap contains a mapping from old pointer to + // new pointer. + + vertices.clear(); + vmap.clear(); + + for (size_t i = 0, il = faces.size(); i != il; ++i) { + face_t &f = faces[i]; + + for (size_t j = 0, jl = f.nVertices(); j != jl; ++j) { + vmap[f.vertex(j)] = NULL; + } + } + + vertices.reserve(vmap.size()); + + for (std::unordered_map<const vertex_t *, const vertex_t *>::iterator i = vmap.begin(), + e = vmap.end(); + i != e; + ++i) { + vertices.push_back(*(*i).first); + (*i).second = &vertices.back(); + } + + for (size_t i = 0, il = faces.size(); i != il; ++i) { + face_t &f = faces[i]; + + for (size_t j = 0, jl = f.nVertices(); j != jl; ++j) { + f.vertex(j) = vmap[f.vertex(j)]; + } + } + } + + + + void Polyhedron::collectFaceVertices(std::vector<face_t> &faces, + std::vector<vertex_t> &vertices) { + std::unordered_map<const vertex_t *, const vertex_t *> vmap; + collectFaceVertices(faces, vertices, vmap); + } + + + + void Polyhedron::setFaceAndVertexOwner() { + for (size_t i = 0; i < vertices.size(); ++i) vertices[i].owner = this; + for (size_t i = 0; i < faces.size(); ++i) faces[i].owner = this; + } + + + + void Polyhedron::commonFaceInit(bool _recalc) { + collectFaceVertices(faces, vertices); + setFaceAndVertexOwner(); + if (_recalc) faceRecalc(); + + if (!init()) { + throw carve::exception("polyhedron creation failed"); + } + } + + + + Polyhedron::~Polyhedron() { + } + + + + void Polyhedron::testVertexAgainstClosedManifolds(const carve::geom3d::Vector &v, + std::map<int, PointClass> &result, + bool ignore_orientation) const { + + for (size_t i = 0; i < faces.size(); i++) { + if (!manifold_is_closed[faces[i].manifold_id]) continue; // skip open manifolds + if (faces[i].containsPoint(v)) { + result[faces[i].manifold_id] = POINT_ON; + } + } + + double ray_len = aabb.extent.length() * 2; + + std::vector<const face_t *> possible_faces; + + std::vector<std::pair<const face_t *, carve::geom3d::Vector> > manifold_intersections; + + boost::mt19937 rng; + boost::uniform_on_sphere<double> distrib(3); + boost::variate_generator<boost::mt19937 &, boost::uniform_on_sphere<double> > gen(rng, distrib); + + for (;;) { + carve::geom3d::Vector ray_dir; + ray_dir = gen(); + + carve::geom3d::Vector v2 = v + ray_dir * ray_len; + + bool failed = false; + carve::geom3d::LineSegment line(v, v2); + carve::geom3d::Vector intersection; + + possible_faces.clear(); + manifold_intersections.clear(); + octree.findFacesNear(line, possible_faces); + + for (unsigned i = 0; !failed && i < possible_faces.size(); i++) { + if (!manifold_is_closed[possible_faces[i]->manifold_id]) continue; // skip open manifolds + if (result.find(possible_faces[i]->manifold_id) != result.end()) continue; // already ON + + switch (possible_faces[i]->lineSegmentIntersection(line, intersection)) { + case INTERSECT_FACE: { + manifold_intersections.push_back(std::make_pair(possible_faces[i], intersection)); + break; + } + case INTERSECT_NONE: { + break; + } + default: { + failed = true; + break; + } + } + } + + if (!failed) break; + } + + std::vector<int> crossings(manifold_is_closed.size(), 0); + + for (size_t i = 0; i < manifold_intersections.size(); ++i) { + const face_t *f = manifold_intersections[i].first; + crossings[f->manifold_id]++; + } + + for (size_t i = 0; i < crossings.size(); ++i) { +#if defined(CARVE_DEBUG) + std::cerr << "crossing: " << i << " = " << crossings[i] << " is_negative = " << manifold_is_negative[i] << std::endl; +#endif + if (!manifold_is_closed[i]) continue; + if (result.find(i) != result.end()) continue; + PointClass pc = (crossings[i] & 1) ? POINT_IN : POINT_OUT; + if (!ignore_orientation && manifold_is_negative[i]) pc = (PointClass)-pc; + result[i] = pc; + } + } + + + + PointClass Polyhedron::containsVertex(const carve::geom3d::Vector &v, + const face_t **hit_face, + bool even_odd, + int manifold_id) const { + if (hit_face) *hit_face = NULL; + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{containsVertex " << v << "}" << std::endl; +#endif + + if (!aabb.containsPoint(v)) { +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{final:OUT(aabb short circuit)}" << std::endl; +#endif + // XXX: if the top level manifolds are negative, this should be POINT_IN. + // for the moment, this only works for a single manifold. + if (manifold_is_negative.size() == 1 && manifold_is_negative[0]) return POINT_IN; + return POINT_OUT; + } + + for (size_t i = 0; i < faces.size(); i++) { + if (manifold_id != -1 && manifold_id != faces[i].manifold_id) continue; + + // XXX: Do allow the tested vertex to be ON an open + // manifold. This was here originally because of the + // possibility of an open manifold contained within a closed + // manifold. + + // if (!manifold_is_closed[faces[i].manifold_id]) continue; + + if (faces[i].containsPoint(v)) { +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{final:ON(hits face " << &faces[i] << ")}" << std::endl; +#endif + if (hit_face) *hit_face = &faces[i]; + return POINT_ON; + } + } + + double ray_len = aabb.extent.length() * 2; + + std::vector<const face_t *> possible_faces; + + std::vector<std::pair<const face_t *, carve::geom3d::Vector> > manifold_intersections; + + for (;;) { + double a1 = random() / double(RAND_MAX) * M_TWOPI; + double a2 = random() / double(RAND_MAX) * M_TWOPI; + + carve::geom3d::Vector ray_dir = carve::geom::VECTOR(sin(a1) * sin(a2), cos(a1) * sin(a2), cos(a2)); + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{testing ray: " << ray_dir << "}" << std::endl; +#endif + + carve::geom3d::Vector v2 = v + ray_dir * ray_len; + + bool failed = false; + carve::geom3d::LineSegment line(v, v2); + carve::geom3d::Vector intersection; + + possible_faces.clear(); + manifold_intersections.clear(); + octree.findFacesNear(line, possible_faces); + + for (unsigned i = 0; !failed && i < possible_faces.size(); i++) { + if (manifold_id != -1 && manifold_id != faces[i].manifold_id) continue; + + if (!manifold_is_closed[possible_faces[i]->manifold_id]) continue; + + switch (possible_faces[i]->lineSegmentIntersection(line, intersection)) { + case INTERSECT_FACE: { + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{intersects face: " << possible_faces[i] + << " dp: " << dot(ray_dir, possible_faces[i]->plane_eqn.N) << "}" << std::endl; +#endif + + if (!even_odd && fabs(dot(ray_dir, possible_faces[i]->plane_eqn.N)) < EPSILON) { + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{failing(small dot product)}" << std::endl; +#endif + + failed = true; + break; + } + manifold_intersections.push_back(std::make_pair(possible_faces[i], intersection)); + break; + } + case INTERSECT_NONE: { + break; + } + default: { + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{failing(degenerate intersection)}" << std::endl; +#endif + failed = true; + break; + } + } + } + + if (!failed) { + if (even_odd) { + return (manifold_intersections.size() & 1) ? POINT_IN : POINT_OUT; + } + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{intersections ok [count:" + << manifold_intersections.size() + << "], sorting}" + << std::endl; +#endif + + carve::geom3d::sortInDirectionOfRay(ray_dir, + manifold_intersections.begin(), + manifold_intersections.end(), + carve::geom3d::vec_adapt_pair_second()); + + std::vector<int> crossings(manifold_is_closed.size(), 0); + + for (size_t i = 0; i < manifold_intersections.size(); ++i) { + const face_t *f = manifold_intersections[i].first; + if (dot(ray_dir, f->plane_eqn.N) < 0.0) { + crossings[f->manifold_id]++; + } else { + crossings[f->manifold_id]--; + } + } + +#if defined(DEBUG_CONTAINS_VERTEX) + for (size_t i = 0; i < crossings.size(); ++i) { + std::cerr << "{manifold " << i << " crossing count: " << crossings[i] << "}" << std::endl; + } +#endif + + for (size_t i = 0; i < manifold_intersections.size(); ++i) { + const face_t *f = manifold_intersections[i].first; + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{intersection at " + << manifold_intersections[i].second + << " id: " + << f->manifold_id + << " count: " + << crossings[f->manifold_id] + << "}" + << std::endl; +#endif + + if (crossings[f->manifold_id] < 0) { + // inside this manifold. + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{final:IN}" << std::endl; +#endif + + return POINT_IN; + } else if (crossings[f->manifold_id] > 0) { + // outside this manifold, but it's an infinite manifold. (for instance, an inverted cube) + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{final:OUT}" << std::endl; +#endif + + return POINT_OUT; + } + } + +#if defined(DEBUG_CONTAINS_VERTEX) + std::cerr << "{final:OUT(default)}" << std::endl; +#endif + + return POINT_OUT; + } + } + } + + + + void Polyhedron::findEdgesNear(const carve::geom::aabb<3> &aabb, + std::vector<const edge_t *> &outEdges) const { + outEdges.clear(); + octree.findEdgesNear(aabb, outEdges); + } + + + + void Polyhedron::findEdgesNear(const carve::geom3d::LineSegment &line, + std::vector<const edge_t *> &outEdges) const { + outEdges.clear(); + octree.findEdgesNear(line, outEdges); + } + + + + void Polyhedron::findEdgesNear(const carve::geom3d::Vector &v, + std::vector<const edge_t *> &outEdges) const { + outEdges.clear(); + octree.findEdgesNear(v, outEdges); + } + + + + void Polyhedron::findEdgesNear(const face_t &face, + std::vector<const edge_t *> &edges) const { + edges.clear(); + octree.findEdgesNear(face, edges); + } + + + + void Polyhedron::findEdgesNear(const edge_t &edge, + std::vector<const edge_t *> &outEdges) const { + outEdges.clear(); + octree.findEdgesNear(edge, outEdges); + } + + + + void Polyhedron::findFacesNear(const carve::geom3d::LineSegment &line, + std::vector<const face_t *> &outFaces) const { + outFaces.clear(); + octree.findFacesNear(line, outFaces); + } + + + + void Polyhedron::findFacesNear(const carve::geom::aabb<3> &aabb, + std::vector<const face_t *> &outFaces) const { + outFaces.clear(); + octree.findFacesNear(aabb, outFaces); + } + + + + void Polyhedron::findFacesNear(const edge_t &edge, + std::vector<const face_t *> &outFaces) const { + outFaces.clear(); + octree.findFacesNear(edge, outFaces); + } + + + + void Polyhedron::transform(const carve::math::Matrix &xform) { + for (size_t i = 0; i < vertices.size(); i++) { + vertices[i].v = xform * vertices[i].v; + } + for (size_t i = 0; i < faces.size(); i++) { + faces[i].recalc(); + } + init(); + } + + + + void Polyhedron::print(std::ostream &o) const { + o << "Polyhedron@" << this << " {" << std::endl; + for (std::vector<vertex_t >::const_iterator + i = vertices.begin(), e = vertices.end(); i != e; ++i) { + o << " V@" << &(*i) << " " << (*i).v << std::endl; + } + for (std::vector<edge_t >::const_iterator + i = edges.begin(), e = edges.end(); i != e; ++i) { + o << " E@" << &(*i) << " {" << std::endl; + o << " V@" << (*i).v1 << " - " << "V@" << (*i).v2 << std::endl; + const std::vector<const face_t *> &faces = connectivity.edge_to_face[edgeToIndex_fast(&(*i))]; + for (size_t j = 0; j < (faces.size() & ~1U); j += 2) { + o << " fp: F@" << faces[j] << ", F@" << faces[j+1] << std::endl; + } + o << " }" << std::endl; + } + for (std::vector<face_t >::const_iterator + i = faces.begin(), e = faces.end(); i != e; ++i) { + o << " F@" << &(*i) << " {" << std::endl; + o << " vertices {" << std::endl; + for (face_t::const_vertex_iter_t j = (*i).vbegin(), je = (*i).vend(); j != je; ++j) { + o << " V@" << (*j) << std::endl; + } + o << " }" << std::endl; + o << " edges {" << std::endl; + for (face_t::const_edge_iter_t j = (*i).ebegin(), je = (*i).eend(); j != je; ++j) { + o << " E@" << (*j) << std::endl; + } + carve::geom::plane<3> p = (*i).plane_eqn; + o << " }" << std::endl; + o << " normal " << (*i).plane_eqn.N << std::endl; + o << " aabb " << (*i).aabb << std::endl; + o << " plane_eqn "; + carve::geom::operator<< <3>(o, p); + o << std::endl; + o << " }" << std::endl; + } + + o << "}" << std::endl; + } + + + + void Polyhedron::canonicalize() { + orderVertices(); + for (size_t i = 0; i < faces.size(); i++) { + face_t &f = faces[i]; + size_t j = std::distance(f.vbegin(), + std::min_element(f.vbegin(), + f.vend())); + if (j) { + { + std::vector<const vertex_t *> temp; + temp.reserve(f.nVertices()); + std::copy(f.vbegin() + j, f.vend(), std::back_inserter(temp)); + std::copy(f.vbegin(), f.vbegin() + j, std::back_inserter(temp)); + std::copy(temp.begin(), temp.end(), f.vbegin()); + } + { + std::vector<const edge_t *> temp; + temp.reserve(f.nEdges()); + std::copy(f.ebegin() + j, f.eend(), std::back_inserter(temp)); + std::copy(f.ebegin(), f.ebegin() + j, std::back_inserter(temp)); + std::copy(temp.begin(), temp.end(), f.ebegin()); + } + } + } + + std::vector<face_t *> face_ptrs; + face_ptrs.reserve(faces.size()); + for (size_t i = 0; i < faces.size(); ++i) face_ptrs.push_back(&faces[i]); + std::sort(face_ptrs.begin(), face_ptrs.end(), order_faces()); + std::vector<face_t> sorted_faces; + sorted_faces.reserve(faces.size()); + for (size_t i = 0; i < faces.size(); ++i) sorted_faces.push_back(*face_ptrs[i]); + std::swap(faces, sorted_faces); + } + + } +} + diff --git a/extern/carve/lib/polyline.cpp b/extern/carve/lib/polyline.cpp new file mode 100644 index 00000000000..d9681c76a5c --- /dev/null +++ b/extern/carve/lib/polyline.cpp @@ -0,0 +1,67 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/geom.hpp> +#include <carve/vector.hpp> +#include <carve/polyline.hpp> + +namespace carve { + namespace line { + carve::geom3d::AABB Polyline::aabb() const { + return carve::geom3d::AABB(vbegin(), vend(), vec_adapt_vertex_ptr()); + } + + PolylineSet::PolylineSet(const std::vector<carve::geom3d::Vector> &points) { + vertices.resize(points.size()); + for (size_t i = 0; i < points.size(); ++i) vertices[i].v = points[i]; + aabb.fit(points.begin(), points.end(), carve::geom3d::vec_adapt_ident()); + } + + void PolylineSet::sortVertices(const carve::geom3d::Vector &axis) { + std::vector<std::pair<double, size_t> > temp; + temp.reserve(vertices.size()); + for (size_t i = 0; i < vertices.size(); ++i) { + temp.push_back(std::make_pair(dot(axis, vertices[i].v), i)); + } + std::sort(temp.begin(), temp.end()); + std::vector<Vertex> vnew; + std::vector<int> revmap; + vnew.reserve(vertices.size()); + revmap.resize(vertices.size()); + + for (size_t i = 0; i < vertices.size(); ++i) { + vnew.push_back(vertices[temp[i].second]); + revmap[temp[i].second] = i; + } + + for (line_iter i = lines.begin(); i != lines.end(); ++i) { + Polyline &l = *(*i); + for (size_t j = 0; j < l.edges.size(); ++j) { + PolylineEdge &e = *l.edges[j]; + if (e.v1) e.v1 = &vnew[revmap[vertexToIndex_fast(e.v1)]]; + if (e.v2) e.v2 = &vnew[revmap[vertexToIndex_fast(e.v2)]]; + } + } + vertices.swap(vnew); + } + + } +} diff --git a/extern/carve/lib/tag.cpp b/extern/carve/lib/tag.cpp new file mode 100644 index 00000000000..449eb555346 --- /dev/null +++ b/extern/carve/lib/tag.cpp @@ -0,0 +1,24 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/tag.hpp> + +int carve::tagable::s_count = 0; diff --git a/extern/carve/lib/timing.cpp b/extern/carve/lib/timing.cpp new file mode 100644 index 00000000000..a98796cd8a7 --- /dev/null +++ b/extern/carve/lib/timing.cpp @@ -0,0 +1,436 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if CARVE_USE_TIMINGS + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/timing.hpp> + +#include <cstring> +#include <list> +#include <stack> +#include <vector> +#include <map> +#include <iostream> +#include <string> +#include <algorithm> + +#ifdef WIN32 +#include <windows.h> +#else +#include <time.h> +#include <sys/time.h> +#endif + +#ifndef CARVE_USE_GLOBAL_NEW_DELETE +#define CARVE_USE_GLOBAL_NEW_DELETE 0 +#endif + +namespace carve { + static uint64_t memoryCurr = 0; + static uint64_t memoryTotal = 0; + unsigned blkCntCurr[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + unsigned blkCntTotal[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + + void addBlk(unsigned size) { + unsigned i = 0; + while (i < 31 && (1U<<i) < size) ++i; + blkCntCurr[i]++; + blkCntTotal[i]++; + } + void remBlk(unsigned size) { + unsigned i = 0; + while (i < 31 && (1<<i) < size) ++i; + blkCntCurr[i]--; + } +} + +// lets provide a global new and delete as well + +#if CARVE_USE_GLOBAL_NEW_DELETE + +#if defined(__APPLE__) + +#include <stdlib.h> +#include <malloc/malloc.h> + +void* carve_alloc(size_t size) { + void *p = malloc(size); + if (p == 0) throw std::bad_alloc(); // ANSI/ISO compliant behavior + + unsigned sz = malloc_size(p); + carve::memoryCurr += sz; + carve::memoryTotal += sz; + carve::addBlk(sz); + return p; +} + +void carve_free(void *p) { + unsigned sz = malloc_size(p); + carve::memoryCurr -= sz; + carve::remBlk(sz); + free(p); +} + +#else + +void* carve_alloc(size_t size) { + void *p = malloc(size + 4); + if (p == 0) throw std::bad_alloc(); // ANSI/ISO compliant behavior + + int *sizePtr = (int*)p; + *sizePtr = size; + ++sizePtr; + carve::memoryCurr += size; + carve::memoryTotal += size; + carve::addBlk(size); + return sizePtr; +} + +void carve_free(void *p) { + // our memory block is actually a size of an int behind this pointer. + int *sizePtr = (int*)p; + + --sizePtr; + + carve::memoryCurr -= *sizePtr; + int size = *sizePtr; + carve::remBlk(size); + free(sizePtr); +} + +#endif + + +void* operator new (size_t size) { + return carve_alloc(size); +} + +void* operator new[](size_t size) { + return carve_alloc(size); +} + + +void operator delete (void *p) { + carve_free(p); +} + +void operator delete[](void *p) { + carve_free(p); +} + +#endif + +namespace carve { + + + +#ifdef WIN32 + + typedef __int64 precise_time_t; + + precise_time_t g_frequency; + + void initTime() { + ::QueryPerformanceFrequency((LARGE_INTEGER*)&g_frequency); + } + + void getTime(precise_time_t &t) { + ::QueryPerformanceCounter((LARGE_INTEGER*)&t); + } + + double diffTime(precise_time_t from, precise_time_t to) { + return (double)(to - from) / (double)g_frequency; + } + +#else + + typedef double precise_time_t; + + void initTime() { + } + + void getTime(precise_time_t &t) { + struct timeval tv; + gettimeofday(&tv, NULL); + t = tv.tv_sec + tv.tv_usec / 1000000.0; + } + + double diffTime(precise_time_t from, precise_time_t to) { + return to - from; + } + +#endif + + struct Entry { + Entry(int _id) { + id = _id; + time = 0; + parent = NULL; + } + int id; + double time; + int64_t memoryDiff; + int64_t allocTotal; + int delta_blk_cnt_curr[32]; + int delta_blk_cnt_total[32]; + Entry *parent; + std::vector<Entry *> children; + }; + + struct Timer { + struct cmp { + bool operator()(const std::pair<int, double> &a, const std::pair<int, double> &b) const { + return b.second < a.second; + } + bool operator()(const Entry * const &a, const Entry * const &b) const { + return b->time < a->time; + } + }; + + Timer() { + initTime(); + } + + struct Snapshot { + precise_time_t time; + uint64_t memory_curr; + uint64_t memory_total; + unsigned blk_cnt_curr[32]; + unsigned blk_cnt_total[32]; + }; + + static void getSnapshot(Snapshot &snapshot) { + getTime(snapshot.time); + snapshot.memory_curr = carve::memoryCurr; + snapshot.memory_total = carve::memoryTotal; + std::memcpy(snapshot.blk_cnt_curr, carve::blkCntCurr, sizeof(carve::blkCntCurr)); + std::memcpy(snapshot.blk_cnt_total, carve::blkCntTotal, sizeof(carve::blkCntTotal)); + } + + static void compareSnapshot(const Snapshot &from, const Snapshot &to, Entry *entry) { + entry->time = diffTime(from.time, to.time); + entry->memoryDiff = to.memory_curr - from.memory_curr; + entry->allocTotal = to.memory_total - from.memory_total; + for (int i = 0; i < 32; i++) { + entry->delta_blk_cnt_curr[i] = to.blk_cnt_curr[i] - from.blk_cnt_curr[i]; + entry->delta_blk_cnt_total[i] = to.blk_cnt_total[i] - from.blk_cnt_total[i]; + } + } + + std::stack<std::pair<Entry*, Snapshot> > currentTimers; + + void startTiming(int id) { + entries.push_back(Entry(id)); + currentTimers.push(std::make_pair(&entries.back(), Snapshot())); + getSnapshot(currentTimers.top().second); + } + + double endTiming() { + Snapshot end; + getSnapshot(end); + + Entry *entry = currentTimers.top().first; + compareSnapshot(currentTimers.top().second, end, entry); + + currentTimers.pop(); + if (!currentTimers.empty()) { + entry->parent = currentTimers.top().first; + entry->parent->children.push_back(entry); + } else { + root_entries.push_back(entry); + } + //std::sort(entry->children.begin(), entry->children.end(), cmp()); + return entry->time; + } + + typedef std::list<Entry> EntryList; + EntryList entries; + std::vector<Entry *> root_entries; + + std::map<int, std::string> names; + + static std::string formatMemory(int64_t value) { + + std::ostringstream result; + + result << (value >= 0 ? "+" : "-"); + if (value < 0) { + value = -value; + } + + int power = 1; + while (value > pow(10.0, power)) { + power++; + } + + for (power--; power >= 0; power--) { + int64_t base = pow(10.0, power); + int64_t amount = value / base; + result << +#if defined(_MSC_VER) && _MSC_VER < 1300 + (long) +#endif + amount; + if (power > 0 && (power % 3) == 0) { + result << ","; + } + value -= amount * base; + } + + result << " bytes"; + + return result.str(); + } + + void printEntries(std::ostream &o, const std::vector<Entry *> &entries, const std::string &indent, double parent_time) { + if (parent_time <= 0.0) { + parent_time = 0.0; + for (size_t i = 0; i < entries.size(); ++i) { + parent_time += entries[i]->time; + } + } + double t_tot = 0.0; + for (size_t i = 0; i < entries.size(); ++i) { + const Entry *entry = entries[i]; + + std::ostringstream r; + r << indent; + std::string str = names[entry->id]; + if (str.empty()) { + r << "(" << entry->id << ")"; + } else { + r << str; + } + r << " "; + std::string pad(r.str().size(), ' '); + r << " - exectime: " << entry->time << "s (" << (entry->time * 100.0 / parent_time) << "%)" << std::endl; + if (entry->allocTotal || entry->memoryDiff) { + r << pad << " - alloc: " << formatMemory(entry->allocTotal) << " delta: " << formatMemory(entry->memoryDiff) << std::endl; + r << pad << " - alloc blks:"; + for (int i = 0; i < 32; i++) { if (entry->delta_blk_cnt_total[i]) r << ' ' << ((1 << (i - 1)) + 1) << '-' << (1 << i) << ':' << entry->delta_blk_cnt_total[i]; } + r << std::endl; + r << pad << " - delta blks:"; + for (int i = 0; i < 32; i++) { if (entry->delta_blk_cnt_curr[i]) r << ' ' << ((1 << (i - 1)) + 1) << '-' << (1 << i) << ':' << entry->delta_blk_cnt_curr[i]; } + r << std::endl; + } + o << r.str(); + t_tot += entry->time; + if (entry->children.size()) printEntries(o, entry->children, indent + " ", entry->time); + } + if (t_tot < parent_time) { + o << indent << "*** unaccounted: " << (parent_time - t_tot) << "s (" << (100.0 - t_tot * 100.0 / parent_time) << "%)" << std::endl; + } + } + + void print() { + std::map<int, double> totals; + std::cerr << "Timings: " << std::endl; + // print out all the entries. + + //std::sort(root_entries.begin(), root_entries.end(), cmp()); + + printEntries(std::cerr, root_entries, " ", -1.0); + + for (EntryList::const_iterator it = entries.begin(); it != entries.end(); ++it) { + totals[(*it).id] += (*it).time; + } + + std::cerr << std::endl; + std::cerr << "Totals: " << std::endl; + + std::vector<std::pair<int, double> > sorted_totals; + sorted_totals.reserve(totals.size()); + for (std::map<int,double>::iterator it = totals.begin(); it != totals.end(); ++it) { + sorted_totals.push_back(*it); + } + + std::sort(sorted_totals.begin(), sorted_totals.end(), cmp()); + + for (std::vector<std::pair<int,double> >::iterator it = sorted_totals.begin(); it != sorted_totals.end(); ++it) { + std::cerr << " "; + std::string str = names[it->first]; + if (str.empty()) { + std::cerr << "(" << it->first << ")"; + } else { + std::cerr << str; + } + std::cerr << " - " << it->second << "s " << std::endl; + } + } + void registerID(int id, const char *name) { + names[id] = name; + } + int registerID(const char *name) { + int id = names.size() + 1; + names[id] = name; + return id; + } + + }; + + Timer timer; + + + TimingBlock::TimingBlock(int id) { +#if CARVE_USE_TIMINGS + timer.startTiming(id); +#endif + } + + TimingBlock::TimingBlock(const TimingName &name) { +#if CARVE_USE_TIMINGS + timer.startTiming(name.id); +#endif + } + + + TimingBlock::~TimingBlock() { +#if CARVE_USE_TIMINGS + timer.endTiming(); +#endif + } + void Timing::start(int id) { +#if CARVE_USE_TIMINGS + timer.startTiming(id); +#endif + } + + double Timing::stop() { +#if CARVE_USE_TIMINGS + return timer.endTiming(); +#endif + } + + void Timing::printTimings() { + timer.print(); + } + + void Timing::registerID(int id, const char *name) { + timer.registerID(id, name); + } + + TimingName::TimingName(const char *name) { + id = timer.registerID(name); + } + +} + +#endif diff --git a/extern/carve/lib/triangulator.cpp b/extern/carve/lib/triangulator.cpp new file mode 100644 index 00000000000..b36aecf98be --- /dev/null +++ b/extern/carve/lib/triangulator.cpp @@ -0,0 +1,1211 @@ +// Begin License: +// Copyright (C) 2006-2011 Tobias Sargeant (tobias.sargeant@gmail.com). +// All rights reserved. +// +// This file is part of the Carve CSG Library (http://carve-csg.com/) +// +// This file may be used under the terms of the GNU General Public +// License version 2.0 as published by the Free Software Foundation +// and appearing in the file LICENSE.GPL2 included in the packaging of +// this file. +// +// This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE. +// End: + + +#if defined(HAVE_CONFIG_H) +# include <carve_config.h> +#endif + +#include <carve/csg.hpp> +#include <carve/triangulator.hpp> + +#include <fstream> +#include <sstream> + +#include <algorithm> + + +namespace { + // private code related to hole patching. + + class order_h_loops_2d { + order_h_loops_2d &operator=(const order_h_loops_2d &); + + const std::vector<std::vector<carve::geom2d::P2> > &poly; + int axis; + public: + + order_h_loops_2d(const std::vector<std::vector<carve::geom2d::P2> > &_poly, int _axis) : + poly(_poly), axis(_axis) { + } + + bool operator()(const std::pair<size_t, size_t> &a, + const std::pair<size_t, size_t> &b) const { + return carve::triangulate::detail::axisOrdering(poly[a.first][a.second], poly[b.first][b.second], axis); + } + }; + + class heap_ordering_2d { + heap_ordering_2d &operator=(const heap_ordering_2d &); + + const std::vector<std::vector<carve::geom2d::P2> > &poly; + const std::vector<std::pair<size_t, size_t> > &loop; + const carve::geom2d::P2 p; + int axis; + + public: + + heap_ordering_2d(const std::vector<std::vector<carve::geom2d::P2> > &_poly, + const std::vector<std::pair<size_t, size_t> > &_loop, + const carve::geom2d::P2 _p, + int _axis) : poly(_poly), loop(_loop), p(_p), axis(_axis) { + } + + bool operator()(size_t a, size_t b) const { + double da = carve::geom::distance2(p, poly[loop[a].first][loop[a].second]); + double db = carve::geom::distance2(p, poly[loop[b].first][loop[b].second]); + if (da > db) return true; + if (da < db) return false; + return carve::triangulate::detail::axisOrdering(poly[loop[a].first][loop[a].second], poly[loop[b].first][loop[b].second], axis); + } + }; + + static inline void patchHoleIntoPolygon_2d(std::vector<std::pair<size_t, size_t> > &f_loop, + size_t f_loop_attach, + size_t h_loop, + size_t h_loop_attach, + size_t h_loop_size) { + f_loop.insert(f_loop.begin() + f_loop_attach + 1, h_loop_size + 2, std::make_pair(h_loop, 0)); + size_t f = f_loop_attach + 1; + + for (size_t h = h_loop_attach; h != h_loop_size; ++h) { + f_loop[f++].second = h; + } + + for (size_t h = 0; h <= h_loop_attach; ++h) { + f_loop[f++].second = h; + } + + f_loop[f] = f_loop[f_loop_attach]; + } + + static inline const carve::geom2d::P2 &pvert(const std::vector<std::vector<carve::geom2d::P2> > &poly, const std::pair<size_t, size_t> &idx) { + return poly[idx.first][idx.second]; + } +} + + +namespace { + // private code related to triangulation. + + using carve::triangulate::detail::vertex_info; + + struct vertex_info_ordering { + bool operator()(const vertex_info *a, const vertex_info *b) const { + return a->score < b->score; + } + }; + + struct vertex_info_l2norm_inc_ordering { + const vertex_info *v; + vertex_info_l2norm_inc_ordering(const vertex_info *_v) : v(_v) { + } + bool operator()(const vertex_info *a, const vertex_info *b) const { + return carve::geom::distance2(v->p, a->p) > carve::geom::distance2(v->p, b->p); + } + }; + + class EarQueue { + std::vector<vertex_info *> queue; + + void checkheap() { +#ifdef __GNUC__ + CARVE_ASSERT(std::__is_heap(queue.begin(), queue.end(), vertex_info_ordering())); +#endif + } + + public: + EarQueue() { + } + + size_t size() const { + return queue.size(); + } + + void push(vertex_info *v) { +#if defined(CARVE_DEBUG) + checkheap(); +#endif + queue.push_back(v); + std::push_heap(queue.begin(), queue.end(), vertex_info_ordering()); + } + + vertex_info *pop() { +#if defined(CARVE_DEBUG) + checkheap(); +#endif + std::pop_heap(queue.begin(), queue.end(), vertex_info_ordering()); + vertex_info *v = queue.back(); + queue.pop_back(); + return v; + } + + void remove(vertex_info *v) { +#if defined(CARVE_DEBUG) + checkheap(); +#endif + CARVE_ASSERT(std::find(queue.begin(), queue.end(), v) != queue.end()); + double score = v->score; + if (v != queue[0]) { + v->score = queue[0]->score + 1; + std::make_heap(queue.begin(), queue.end(), vertex_info_ordering()); + } + CARVE_ASSERT(v == queue[0]); + std::pop_heap(queue.begin(), queue.end(), vertex_info_ordering()); + CARVE_ASSERT(queue.back() == v); + queue.pop_back(); + v->score = score; + } + + void changeScore(vertex_info *v, double score) { +#if defined(CARVE_DEBUG) + checkheap(); +#endif + CARVE_ASSERT(std::find(queue.begin(), queue.end(), v) != queue.end()); + if (v->score != score) { + v->score = score; + std::make_heap(queue.begin(), queue.end(), vertex_info_ordering()); + } + } + + // 39% of execution time + void updateVertex(vertex_info *v) { + double spre = v->score; + bool qpre = v->isCandidate(); + v->recompute(); + bool qpost = v->isCandidate(); + double spost = v->score; + + v->score = spre; + + if (qpre) { + if (qpost) { + if (v->score != spre) { + changeScore(v, spost); + } + } else { + remove(v); + } + } else { + if (qpost) { + push(v); + } + } + } + }; + + + + int windingNumber(vertex_info *begin, const carve::geom2d::P2 &point) { + int wn = 0; + + vertex_info *v = begin; + do { + if (v->p.y <= point.y) { + if (v->next->p.y > point.y && carve::geom2d::orient2d(v->p, v->next->p, point) > 0.0) { + ++wn; + } + } else { + if (v->next->p.y <= point.y && carve::geom2d::orient2d(v->p, v->next->p, point) < 0.0) { + --wn; + } + } + v = v->next; + } while (v != begin); + + return wn; + } + + + + bool internalToAngle(const vertex_info *a, + const vertex_info *b, + const vertex_info *c, + const carve::geom2d::P2 &p) { + return carve::geom2d::internalToAngle(a->p, b->p, c->p, p); + } + + + + bool findDiagonal(vertex_info *begin, vertex_info *&v1, vertex_info *&v2) { + vertex_info *t; + std::vector<vertex_info *> heap; + + v1 = begin; + do { + heap.clear(); + + for (v2 = v1->next->next; v2 != v1->prev; v2 = v2->next) { + if (!internalToAngle(v1->next, v1, v1->prev, v2->p) || + !internalToAngle(v2->next, v2, v2->prev, v1->p)) continue; + + heap.push_back(v2); + std::push_heap(heap.begin(), heap.end(), vertex_info_l2norm_inc_ordering(v1)); + } + + while (heap.size()) { + std::pop_heap(heap.begin(), heap.end(), vertex_info_l2norm_inc_ordering(v1)); + v2 = heap.back(); heap.pop_back(); + +#if defined(CARVE_DEBUG) + std::cerr << "testing: " << v1 << " - " << v2 << std::endl; + std::cerr << " length = " << (v2->p - v1->p).length() << std::endl; + std::cerr << " pos: " << v1->p << " - " << v2->p << std::endl; +#endif + // test whether v1-v2 is a valid diagonal. + double v_min_x = std::min(v1->p.x, v2->p.x); + double v_max_x = std::max(v1->p.x, v2->p.x); + + bool intersected = false; + + for (t = v1->next; !intersected && t != v1->prev; t = t->next) { + vertex_info *u = t->next; + if (t == v2 || u == v2) continue; + + double l1 = carve::geom2d::orient2d(v1->p, v2->p, t->p); + double l2 = carve::geom2d::orient2d(v1->p, v2->p, u->p); + + if ((l1 > 0.0 && l2 > 0.0) || (l1 < 0.0 && l2 < 0.0)) { + // both on the same side; no intersection + continue; + } + + double dx13 = v1->p.x - t->p.x; + double dy13 = v1->p.y - t->p.y; + double dx43 = u->p.x - t->p.x; + double dy43 = u->p.y - t->p.y; + double dx21 = v2->p.x - v1->p.x; + double dy21 = v2->p.y - v1->p.y; + double ua_n = dx43 * dy13 - dy43 * dx13; + double ub_n = dx21 * dy13 - dy21 * dx13; + double u_d = dy43 * dx21 - dx43 * dy21; + + if (carve::math::ZERO(u_d)) { + // parallel + if (carve::math::ZERO(ua_n)) { + // colinear + if (std::max(t->p.x, u->p.x) >= v_min_x && std::min(t->p.x, u->p.x) <= v_max_x) { + // colinear and intersecting + intersected = true; + } + } + } else { + // not parallel + double ua = ua_n / u_d; + double ub = ub_n / u_d; + + if (0.0 <= ua && ua <= 1.0 && 0.0 <= ub && ub <= 1.0) { + intersected = true; + } + } +#if defined(CARVE_DEBUG) + if (intersected) { + std::cerr << " failed on edge: " << t << " - " << u << std::endl; + std::cerr << " pos: " << t->p << " - " << u->p << std::endl; + } +#endif + } + + if (!intersected) { + // test whether midpoint winding == 1 + + carve::geom2d::P2 mid = (v1->p + v2->p) / 2; + if (windingNumber(begin, mid) == 1) { + // this diagonal is ok + return true; + } + } + } + + // couldn't find a diagonal from v1 that was ok. + v1 = v1->next; + } while (v1 != begin); + return false; + } + + + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + void dumpPoly(const std::vector<carve::geom2d::P2> &points, + const std::vector<carve::triangulate::tri_idx> &result) { + static int step = 0; + std::ostringstream filename; + filename << "poly_" << step++ << ".svg"; + std::cerr << "dumping to " << filename.str() << std::endl; + std::ofstream out(filename.str().c_str()); + + double minx = points[0].x, maxx = points[0].x; + double miny = points[0].y, maxy = points[0].y; + + for (size_t i = 1; i < points.size(); ++i) { + minx = std::min(points[i].x, minx); maxx = std::max(points[i].x, maxx); + miny = std::min(points[i].y, miny); maxy = std::max(points[i].y, maxy); + } + double scale = 100 / std::max(maxx-minx, maxy-miny); + + maxx *= scale; minx *= scale; + maxy *= scale; miny *= scale; + + double width = maxx - minx + 10; + double height = maxy - miny + 10; + + out << "\ +<?xml version=\"1.0\"?>\n\ +<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n\ +<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"" << width << "\" height=\"" << height << "\">\n"; + + out << "<polygon fill=\"rgb(0,0,0)\" stroke=\"blue\" stroke-width=\"0.1\" points=\""; + for (size_t i = 0; i < points.size(); ++i) { + if (i) out << ' '; + double x, y; + x = scale * (points[i].x) - minx + 5; + y = scale * (points[i].y) - miny + 5; + out << x << ',' << y; + } + out << "\" />" << std::endl; + + for (size_t i = 0; i < result.size(); ++i) { + out << "<polygon fill=\"rgb(255,255,255)\" stroke=\"black\" stroke-width=\"0.1\" points=\""; + double x, y; + x = scale * (points[result[i].a].x) - minx + 5; + y = scale * (points[result[i].a].y) - miny + 5; + out << x << ',' << y << ' '; + x = scale * (points[result[i].b].x) - minx + 5; + y = scale * (points[result[i].b].y) - miny + 5; + out << x << ',' << y << ' '; + x = scale * (points[result[i].c].x) - minx + 5; + y = scale * (points[result[i].c].y) - miny + 5; + out << x << ',' << y; + out << "\" />" << std::endl; + } + + out << "</svg>" << std::endl; + } +#endif +} + + + +double carve::triangulate::detail::vertex_info::triScore(const vertex_info *p, const vertex_info *v, const vertex_info *n) { + + // different scoring functions. +#if 0 + bool convex = isLeft(p, v, n); + if (!convex) return -1e-5; + + double a1 = carve::geom2d::atan2(p->p - v->p) - carve::geom2d::atan2(n->p - v->p); + double a2 = carve::geom2d::atan2(v->p - n->p) - carve::geom2d::atan2(p->p - n->p); + if (a1 < 0) a1 += M_PI * 2; + if (a2 < 0) a2 += M_PI * 2; + + return std::min(a1, std::min(a2, M_PI - a1 - a2)) / (M_PI / 3); +#endif + +#if 1 + // range: 0 - 1 + double a, b, c; + + bool convex = isLeft(p, v, n); + if (!convex) return -1e-5; + + a = (n->p - v->p).length(); + b = (p->p - n->p).length(); + c = (v->p - p->p).length(); + + if (a < 1e-10 || b < 1e-10 || c < 1e-10) return 0.0; + + return std::max(std::min((a+b)/c, std::min((a+c)/b, (b+c)/a)) - 1.0, 0.0); +#endif +} + + + +double carve::triangulate::detail::vertex_info::calcScore() const { + +#if 0 + // examine only this triangle. + double this_tri = triScore(prev, this, next); + return this_tri; +#endif + +#if 1 + // attempt to look ahead in the neighbourhood to attempt to clip ears that have good neighbours. + double this_tri = triScore(prev, this, next); + double next_tri = triScore(prev, next, next->next); + double prev_tri = triScore(prev->prev, prev, next); + + return this_tri + std::max(next_tri, prev_tri) * .2; +#endif + +#if 0 + // attempt to penalise ears that will require producing a sliver triangle. + double score = triScore(prev, this, next); + + double a1, a2; + a1 = carve::geom2d::atan2(prev->p - next->p); + a2 = carve::geom2d::atan2(next->next->p - next->p); + if (fabs(a1 - a2) < 1e-5) score -= .5; + + a1 = carve::geom2d::atan2(next->p - prev->p); + a2 = carve::geom2d::atan2(prev->prev->p - prev->p); + if (fabs(a1 - a2) < 1e-5) score -= .5; + + return score; +#endif +} + + + +bool carve::triangulate::detail::vertex_info::isClipable() const { + for (const vertex_info *v_test = next->next; v_test != prev; v_test = v_test->next) { + if (v_test->convex) { + continue; + } + + if (v_test->p == prev->p || + v_test->p == next->p) { + continue; + } + + if (v_test->p == p) { + if (v_test->next->p == prev->p && + v_test->prev->p == next->p) { + return false; + } + if (v_test->next->p == prev->p || + v_test->prev->p == next->p) { + continue; + } + } + + if (pointInTriangle(prev, this, next, v_test)) { + return false; + } + } + return true; +} + + + +size_t carve::triangulate::detail::removeDegeneracies(vertex_info *&begin, std::vector<carve::triangulate::tri_idx> &result) { + vertex_info *v = begin; + vertex_info *n; + size_t count = 0; + do { + bool remove = false; + if (v->p == v->next->p) { + remove = true; + } else if (v->p == v->next->next->p) { + if (v->next->p == v->next->next->next->p) { + // a 'z' in the loop: z (a) b a b c -> remove a-b-a -> z (a) a b c -> remove a-a-b (next loop) -> z a b c + // z --(a)-- b + // / + // / + // a -- b -- d + remove = true; + } else { + // a 'shard' in the loop: z (a) b a c d -> remove a-b-a -> z (a) a b c d -> remove a-a-b (next loop) -> z a b c d + // z --(a)-- b + // / + // / + // a -- c -- d + // n.b. can only do this if the shard is pointing out of the polygon. i.e. b is outside z-a-c + remove = !internalToAngle(v->next->next->next, v, v->prev, v->next->p); + } + } + + if (remove) { + result.push_back(carve::triangulate::tri_idx(v->idx, v->next->idx, v->next->next->idx)); + n = v->next; + if (n == begin) begin = n->next; + n->remove(); + count++; + delete n; + continue; + } + + v = v->next; + } while (v != begin); + return count; +} + + + +bool carve::triangulate::detail::splitAndResume(vertex_info *begin, std::vector<carve::triangulate::tri_idx> &result) { + vertex_info *v1, *v2; + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + { + std::vector<carve::triangulate::tri_idx> dummy; + std::vector<carve::geom2d::P2> dummy_p; + vertex_info *v = begin; + do { + dummy_p.push_back(v->p); + v = v->next; + } while (v != begin); + std::cerr << "input to splitAndResume:" << std::endl; + dumpPoly(dummy_p, dummy); + } +#endif + + + if (!findDiagonal(begin, v1, v2)) return false; + + vertex_info *v1_copy = new vertex_info(*v1); + vertex_info *v2_copy = new vertex_info(*v2); + + v1->next = v2; + v2->prev = v1; + + v1_copy->next->prev = v1_copy; + v2_copy->prev->next = v2_copy; + + v1_copy->prev = v2_copy; + v2_copy->next = v1_copy; + + bool r1 = doTriangulate(v1, result); + bool r2 = doTriangulate(v1_copy, result); + return r1 && r2; +} + + + +bool carve::triangulate::detail::doTriangulate(vertex_info *begin, std::vector<carve::triangulate::tri_idx> &result) { +#if defined(CARVE_DEBUG) + std::cerr << "entering doTriangulate" << std::endl; +#endif + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + { + std::vector<carve::triangulate::tri_idx> dummy; + std::vector<carve::geom2d::P2> dummy_p; + vertex_info *v = begin; + do { + dummy_p.push_back(v->p); + v = v->next; + } while (v != begin); + dumpPoly(dummy_p, dummy); + } +#endif + + EarQueue vq; + + vertex_info *v = begin; + size_t remain = 0; + do { + if (v->isCandidate()) vq.push(v); + v = v->next; + remain++; + } while (v != begin); + +#if defined(CARVE_DEBUG) + std::cerr << "remain = " << remain << std::endl; +#endif + + while (vq.size()) { + vertex_info *v = vq.pop(); + if (!v->isClipable()) { + v->failed = true; + continue; + } + + continue_clipping: + vertex_info *n = v->next; + vertex_info *p = v->prev; + + result.push_back(carve::triangulate::tri_idx(v->prev->idx, v->idx, v->next->idx)); + +#if defined(CARVE_DEBUG) + { + std::vector<carve::geom2d::P2> temp; + temp.push_back(v->prev->p); + temp.push_back(v->p); + temp.push_back(v->next->p); + std::cerr << "clip " << v << " idx = " << v->idx << " score = " << v->score << " area = " << carve::geom2d::signedArea(temp) << " " << temp[0] << " " << temp[1] << " " << temp[2] << std::endl; + } +#endif + + v->remove(); + remain--; + if (v == begin) begin = v->next; + delete v; + + vq.updateVertex(n); + vq.updateVertex(p); + + if (n->score < p->score) { std::swap(n, p); } + + if (n->score > 0.25 && n->isCandidate() && n->isClipable()) { + vq.remove(n); + v = n; +#if defined(CARVE_DEBUG) + std::cerr << " continue clipping (n), score = " << n->score << std::endl; +#endif + goto continue_clipping; + } + + if (p->score > 0.25 && p->isCandidate() && p->isClipable()) { + vq.remove(p); + v = p; +#if defined(CARVE_DEBUG) + std::cerr << " continue clipping (p), score = " << n->score << std::endl; +#endif + goto continue_clipping; + } + +#if defined(CARVE_DEBUG) + std::cerr << "looking for new start point" << std::endl; + std::cerr << "remain = " << remain << std::endl; +#endif + } + +#if defined(CARVE_DEBUG) + std::cerr << "doTriangulate complete; remain=" << remain << std::endl; +#endif + + bool ret = true; + + if (remain > 3) { + std::vector<carve::geom2d::P2> temp; + temp.reserve(remain); + vertex_info *v = begin; + + do { + temp.push_back(v->p); + v = v->next; + } while (v != begin); + + if (carve::geom2d::signedArea(temp) == 0) { + // XXX: this test will fail in cases where the boundary is + // twisted so that a negative area balances a positive area. +#if defined(CARVE_DEBUG) + std::cerr << "skeleton remains. complete." << std::endl; +#endif + goto done; + } + +#if defined(CARVE_DEBUG) + std::cerr << "before removeDegeneracies: remain=" << remain << std::endl; +#endif + remain -= removeDegeneracies(begin, result); +#if defined(CARVE_DEBUG) + std::cerr << "after removeDegeneracies: remain=" << remain << std::endl; +#endif + } + + if (remain > 3) { + return splitAndResume(begin, result); + } else if (remain == 3) { + result.push_back(carve::triangulate::tri_idx(begin->idx, begin->next->idx, begin->next->next->idx)); + ret = true; + } else { + ret = true; + } + + done: + vertex_info *d = begin; + do { + vertex_info *n = d->next; + delete d; + d = n; + } while (d != begin); + + return ret; +} + + + +bool testCandidateAttachment(const std::vector<std::vector<carve::geom2d::P2> > &poly, + std::vector<std::pair<size_t, size_t> > ¤t_f_loop, + size_t curr, + carve::geom2d::P2 hole_min) { + const size_t SZ = current_f_loop.size(); + + if (!carve::geom2d::internalToAngle(pvert(poly, current_f_loop[(curr+1) % SZ]), + pvert(poly, current_f_loop[curr]), + pvert(poly, current_f_loop[(curr+SZ-1) % SZ]), + hole_min)) { + return false; + } + + if (hole_min == pvert(poly, current_f_loop[curr])) { + return true; + } + + carve::geom2d::LineSegment2 test(hole_min, pvert(poly, current_f_loop[curr])); + + size_t v1 = current_f_loop.size() - 1; + size_t v2 = 0; + double v1_side = carve::geom2d::orient2d(test.v1, test.v2, pvert(poly, current_f_loop[v1])); + double v2_side = 0; + + while (v2 != current_f_loop.size()) { + v2_side = carve::geom2d::orient2d(test.v1, test.v2, pvert(poly, current_f_loop[v2])); + + if (v1_side != v2_side) { + // XXX: need to test vertices, not indices, because they may + // be duplicated. + if (pvert(poly, current_f_loop[v1]) != pvert(poly, current_f_loop[curr]) && + pvert(poly, current_f_loop[v2]) != pvert(poly, current_f_loop[curr])) { + carve::geom2d::LineSegment2 test2(pvert(poly, current_f_loop[v1]), pvert(poly, current_f_loop[v2])); + if (carve::geom2d::lineSegmentIntersection_simple(test, test2)) { + // intersection; failed. + return false; + } + } + } + + v1 = v2; + v1_side = v2_side; + ++v2; + } + return true; +} + + + +void +carve::triangulate::incorporateHolesIntoPolygon( + const std::vector<std::vector<carve::geom2d::P2> > &poly, + std::vector<std::pair<size_t, size_t> > &result, + size_t poly_loop, + const std::vector<size_t> &hole_loops) { + typedef std::vector<carve::geom2d::P2> loop_t; + + size_t N = poly[poly_loop].size(); + + // work out how much space to reserve for the patched in holes. + for (size_t i = 0; i < hole_loops.size(); i++) { + N += 2 + poly[hole_loops[i]].size(); + } + + // this is the vector that we will build the result in. + result.clear(); + result.reserve(N); + + // this is a heap of result indices that defines the vertex test order. + std::vector<size_t> f_loop_heap; + f_loop_heap.reserve(N); + + // add the poly loop to result. + for (size_t i = 0; i < poly[poly_loop].size(); ++i) { + result.push_back(std::make_pair((size_t)poly_loop, i)); + } + + if (hole_loops.size() == 0) { + return; + } + + std::vector<std::pair<size_t, size_t> > h_loop_min_vertex; + + h_loop_min_vertex.reserve(hole_loops.size()); + + // find the major axis for the holes - this is the axis that we + // will sort on for finding vertices on the polygon to join + // holes up to. + // + // it might also be nice to also look for whether it is better + // to sort ascending or descending. + // + // another trick that could be used is to modify the projection + // by 90 degree rotations or flipping about an axis. just as + // long as we keep the carve::geom3d::Vector pointers for the + // real data in sync, everything should be ok. then we wouldn't + // need to accomodate axes or sort order in the main loop. + + // find the bounding box of all the holes. + carve::geom2d::P2 h_min, h_max; + h_min = h_max = poly[hole_loops[0]][0]; + for (size_t i = 0; i < hole_loops.size(); ++i) { + const loop_t &hole = poly[hole_loops[i]]; + for (size_t j = 0; j < hole.size(); ++j) { + assign_op(h_min, h_min, hole[j], carve::util::min_functor()); + assign_op(h_max, h_max, hole[j], carve::util::max_functor()); + } + } + // choose the axis for which the bbox is largest. + int axis = (h_max.x - h_min.x) > (h_max.y - h_min.y) ? 0 : 1; + + // for each hole, find the minimum vertex in the chosen axis. + for (size_t i = 0; i < hole_loops.size(); ++i) { + const loop_t &hole = poly[hole_loops[i]]; + size_t best, curr; + best = 0; + for (curr = 1; curr != hole.size(); ++curr) { + if (detail::axisOrdering(hole[curr], hole[best], axis)) { + best = curr; + } + } + h_loop_min_vertex.push_back(std::make_pair(hole_loops[i], best)); + } + + // sort the holes by the minimum vertex. + std::sort(h_loop_min_vertex.begin(), h_loop_min_vertex.end(), order_h_loops_2d(poly, axis)); + + // now, for each hole, find a vertex in the current polygon loop that it can be joined to. + for (unsigned i = 0; i < h_loop_min_vertex.size(); ++i) { + // the index of the vertex in the hole to connect. + size_t hole_i = h_loop_min_vertex[i].first; + size_t hole_i_connect = h_loop_min_vertex[i].second; + + carve::geom2d::P2 hole_min = poly[hole_i][hole_i_connect]; + + f_loop_heap.clear(); + // we order polygon loop vertices that may be able to be connected + // to the hole vertex by their distance to the hole vertex + heap_ordering_2d _heap_ordering(poly, result, hole_min, axis); + + const size_t SZ = result.size(); + for (size_t j = 0; j < SZ; ++j) { + // it is guaranteed that there exists a polygon vertex with + // coord < the min hole coord chosen, which can be joined to + // the min hole coord without crossing the polygon + // boundary. also, because we merge holes in ascending + // order, it is also true that this join can never cross + // another hole (and that doesn't need to be tested for). + if (pvert(poly, result[j]).v[axis] <= hole_min.v[axis]) { + f_loop_heap.push_back(j); + std::push_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering); + } + } + + // we are going to test each potential (according to the + // previous test) polygon vertex as a candidate join. we order + // by closeness to the hole vertex, so that the join we make + // is as small as possible. to test, we need to check the + // joining line segment does not cross any other line segment + // in the current polygon loop (excluding those that have the + // vertex that we are attempting to join with as an endpoint). + size_t attachment_point = result.size(); + + while (f_loop_heap.size()) { + std::pop_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering); + size_t curr = f_loop_heap.back(); + f_loop_heap.pop_back(); + // test the candidate join from result[curr] to hole_min + + if (!testCandidateAttachment(poly, result, curr, hole_min)) { + continue; + } + + attachment_point = curr; + break; + } + + if (attachment_point == result.size()) { + CARVE_FAIL("didn't manage to link up hole!"); + } + + patchHoleIntoPolygon_2d(result, attachment_point, hole_i, hole_i_connect, poly[hole_i].size()); + } +} + + + +std::vector<std::pair<size_t, size_t> > +carve::triangulate::incorporateHolesIntoPolygon(const std::vector<std::vector<carve::geom2d::P2> > &poly) { +#if 1 + std::vector<std::pair<size_t, size_t> > result; + std::vector<size_t> hole_indices; + hole_indices.reserve(poly.size() - 1); + for (size_t i = 1; i < poly.size(); ++i) { + hole_indices.push_back(i); + } + + incorporateHolesIntoPolygon(poly, result, 0, hole_indices); + + return result; + +#else + typedef std::vector<carve::geom2d::P2> loop_t; + size_t N = poly[0].size(); + // + // work out how much space to reserve for the patched in holes. + for (size_t i = 0; i < poly.size(); i++) { + N += 2 + poly[i].size(); + } + + // this is the vector that we will build the result in. + std::vector<std::pair<size_t, size_t> > current_f_loop; + current_f_loop.reserve(N); + + // this is a heap of current_f_loop indices that defines the vertex test order. + std::vector<size_t> f_loop_heap; + f_loop_heap.reserve(N); + + // add the poly loop to current_f_loop. + for (size_t i = 0; i < poly[0].size(); ++i) { + current_f_loop.push_back(std::make_pair((size_t)0, i)); + } + + if (poly.size() == 1) { + return current_f_loop; + } + + std::vector<std::pair<size_t, size_t> > h_loop_min_vertex; + + h_loop_min_vertex.reserve(poly.size() - 1); + + // find the major axis for the holes - this is the axis that we + // will sort on for finding vertices on the polygon to join + // holes up to. + // + // it might also be nice to also look for whether it is better + // to sort ascending or descending. + // + // another trick that could be used is to modify the projection + // by 90 degree rotations or flipping about an axis. just as + // long as we keep the carve::geom3d::Vector pointers for the + // real data in sync, everything should be ok. then we wouldn't + // need to accomodate axes or sort order in the main loop. + + // find the bounding box of all the holes. + double min_x, min_y, max_x, max_y; + min_x = max_x = poly[1][0].x; + min_y = max_y = poly[1][0].y; + for (size_t i = 1; i < poly.size(); ++i) { + const loop_t &hole = poly[i]; + for (size_t j = 0; j < hole.size(); ++j) { + min_x = std::min(min_x, hole[j].x); + min_y = std::min(min_y, hole[j].y); + max_x = std::max(max_x, hole[j].x); + max_y = std::max(max_y, hole[j].y); + } + } + + // choose the axis for which the bbox is largest. + int axis = (max_x - min_x) > (max_y - min_y) ? 0 : 1; + + // for each hole, find the minimum vertex in the chosen axis. + for (size_t i = 1; i < poly.size(); ++i) { + const loop_t &hole = poly[i]; + size_t best, curr; + best = 0; + for (curr = 1; curr != hole.size(); ++curr) { + if (detail::axisOrdering(hole[curr], hole[best], axis)) { + best = curr; + } + } + h_loop_min_vertex.push_back(std::make_pair(i, best)); + } + + // sort the holes by the minimum vertex. + std::sort(h_loop_min_vertex.begin(), h_loop_min_vertex.end(), order_h_loops_2d(poly, axis)); + + // now, for each hole, find a vertex in the current polygon loop that it can be joined to. + for (unsigned i = 0; i < h_loop_min_vertex.size(); ++i) { + // the index of the vertex in the hole to connect. + size_t hole_i = h_loop_min_vertex[i].first; + size_t hole_i_connect = h_loop_min_vertex[i].second; + + carve::geom2d::P2 hole_min = poly[hole_i][hole_i_connect]; + + f_loop_heap.clear(); + // we order polygon loop vertices that may be able to be connected + // to the hole vertex by their distance to the hole vertex + heap_ordering_2d _heap_ordering(poly, current_f_loop, hole_min, axis); + + const size_t SZ = current_f_loop.size(); + for (size_t j = 0; j < SZ; ++j) { + // it is guaranteed that there exists a polygon vertex with + // coord < the min hole coord chosen, which can be joined to + // the min hole coord without crossing the polygon + // boundary. also, because we merge holes in ascending + // order, it is also true that this join can never cross + // another hole (and that doesn't need to be tested for). + if (pvert(poly, current_f_loop[j]).v[axis] <= hole_min.v[axis]) { + f_loop_heap.push_back(j); + std::push_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering); + } + } + + // we are going to test each potential (according to the + // previous test) polygon vertex as a candidate join. we order + // by closeness to the hole vertex, so that the join we make + // is as small as possible. to test, we need to check the + // joining line segment does not cross any other line segment + // in the current polygon loop (excluding those that have the + // vertex that we are attempting to join with as an endpoint). + size_t attachment_point = current_f_loop.size(); + + while (f_loop_heap.size()) { + std::pop_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering); + size_t curr = f_loop_heap.back(); + f_loop_heap.pop_back(); + // test the candidate join from current_f_loop[curr] to hole_min + + if (!testCandidateAttachment(poly, current_f_loop, curr, hole_min)) { + continue; + } + + attachment_point = curr; + break; + } + + if (attachment_point == current_f_loop.size()) { + CARVE_FAIL("didn't manage to link up hole!"); + } + + patchHoleIntoPolygon_2d(current_f_loop, attachment_point, hole_i, hole_i_connect, poly[hole_i].size()); + } + + return current_f_loop; +#endif +} + + + +std::vector<std::vector<std::pair<size_t, size_t> > > +carve::triangulate::mergePolygonsAndHoles(const std::vector<std::vector<carve::geom2d::P2> > &poly) { + std::vector<size_t> poly_indices, hole_indices; + + poly_indices.reserve(poly.size()); + hole_indices.reserve(poly.size()); + + for (size_t i = 0; i < poly.size(); ++i) { + if (carve::geom2d::signedArea(poly[i]) < 0) { + poly_indices.push_back(i); + } else { + hole_indices.push_back(i); + } + } + + std::vector<std::vector<std::pair<size_t, size_t> > > result; + result.resize(poly_indices.size()); + + if (hole_indices.size() == 0) { + for (size_t i = 0; i < poly.size(); ++i) { + result[i].resize(poly[i].size()); + for (size_t j = 0; j < poly[i].size(); ++j) { + result[i].push_back(std::make_pair(i, j)); + } + } + return result; + } + + if (poly_indices.size() == 1) { + incorporateHolesIntoPolygon(poly, result[0], poly_indices[0], hole_indices); + + return result; + } + + throw carve::exception("not implemented"); +} + + + +void carve::triangulate::triangulate(const std::vector<carve::geom2d::P2> &poly, + std::vector<carve::triangulate::tri_idx> &result) { + std::vector<detail::vertex_info *> vinfo; + const size_t N = poly.size(); + +#if defined(CARVE_DEBUG) + std::cerr << "TRIANGULATION BEGINS" << std::endl; +#endif + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + dumpPoly(poly, result); +#endif + + result.clear(); + if (N < 3) { + return; + } + + result.reserve(poly.size() - 2); + + if (N == 3) { + result.push_back(tri_idx(0, 1, 2)); + return; + } + + vinfo.resize(N); + + vinfo[0] = new detail::vertex_info(poly[0], 0); + for (size_t i = 1; i < N-1; ++i) { + vinfo[i] = new detail::vertex_info(poly[i], i); + vinfo[i]->prev = vinfo[i-1]; + vinfo[i-1]->next = vinfo[i]; + } + vinfo[N-1] = new detail::vertex_info(poly[N-1], N-1); + vinfo[N-1]->prev = vinfo[N-2]; + vinfo[N-1]->next = vinfo[0]; + vinfo[0]->prev = vinfo[N-1]; + vinfo[N-2]->next = vinfo[N-1]; + + for (size_t i = 0; i < N; ++i) { + vinfo[i]->recompute(); + } + + detail::vertex_info *begin = vinfo[0]; + + removeDegeneracies(begin, result); + doTriangulate(begin, result); + +#if defined(CARVE_DEBUG) + std::cerr << "TRIANGULATION ENDS" << std::endl; +#endif + +#if defined(CARVE_DEBUG_WRITE_PLY_DATA) + dumpPoly(poly, result); +#endif +} + + + +void carve::triangulate::detail::tri_pair_t::flip(vert_edge_t &old_edge, + vert_edge_t &new_edge, + vert_edge_t perim[4]) { + unsigned ai, bi; + unsigned cross_ai, cross_bi; + + findSharedEdge(ai, bi); + old_edge = ordered_vert_edge_t(a->v[ai], b->v[bi]); + + cross_ai = P(ai); + cross_bi = P(bi); + new_edge = ordered_vert_edge_t(a->v[cross_ai], b->v[cross_bi]); + + score = -score; + + a->v[N(ai)] = b->v[cross_bi]; + b->v[N(bi)] = a->v[cross_ai]; + + perim[0] = ordered_vert_edge_t(a->v[P(ai)], a->v[ai]); + perim[1] = ordered_vert_edge_t(a->v[N(ai)], a->v[ai]); // this edge was a b-edge + + perim[2] = ordered_vert_edge_t(b->v[P(bi)], b->v[bi]); + perim[3] = ordered_vert_edge_t(b->v[N(bi)], b->v[bi]); // this edge was an a-edge +} + + + +void carve::triangulate::detail::tri_pairs_t::insert(unsigned a, unsigned b, carve::triangulate::tri_idx *t) { + tri_pair_t *tp; + if (a < b) { + tp = storage[vert_edge_t(a,b)]; + if (!tp) { + tp = storage[vert_edge_t(a,b)] = new tri_pair_t; + } + tp->a = t; + } else { + tp = storage[vert_edge_t(b,a)]; + if (!tp) { + tp = storage[vert_edge_t(b,a)] = new tri_pair_t; + } + tp->b = t; + } +} diff --git a/extern/carve/mkfiles.sh b/extern/carve/mkfiles.sh new file mode 100755 index 00000000000..d117d7531bd --- /dev/null +++ b/extern/carve/mkfiles.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +find ./include/ -type f | sed -r 's/^\.\///' > files.txt +find ./lib/ -type f | sed -r 's/^\.\///' >> files.txt diff --git a/extern/carve/patches/files/config.h b/extern/carve/patches/files/config.h new file mode 100644 index 00000000000..fdae2d2843f --- /dev/null +++ b/extern/carve/patches/files/config.h @@ -0,0 +1,12 @@ +#define CARVE_VERSION "2.0.0a" + +#undef CARVE_DEBUG +#undef CARVE_DEBUG_WRITE_PLY_DATA + +#if defined(__GNUC__) +# if !defined(HAVE_BOOST_UNORDERED_COLLECTIONS) +# define HAVE_TR1_UNORDERED_COLLECTIONS +# endif + +# define HAVE_STDINT_H +#endif diff --git a/extern/carve/patches/files/random.hpp b/extern/carve/patches/files/random.hpp new file mode 100644 index 00000000000..15866bb496e --- /dev/null +++ b/extern/carve/patches/files/random.hpp @@ -0,0 +1,773 @@ +#pragma once + +#include <iostream> +#include <vector> +#include <limits> +#include <stdexcept> +#include <cmath> +#include <algorithm> + +#if !defined(_MSC_VER) +#include <stdint.h> +#endif + +namespace boost { + +// type_traits could help here, but I don't want to depend on type_traits. +template<class T> +struct ptr_helper +{ + typedef T value_type; + typedef T& reference_type; + typedef const T& rvalue_type; + static reference_type ref(T& r) { return r; } + static const T& ref(const T& r) { return r; } +}; + +template<class T> +struct ptr_helper<T&> +{ + typedef T value_type; + typedef T& reference_type; + typedef T& rvalue_type; + static reference_type ref(T& r) { return r; } + static const T& ref(const T& r) { return r; } +}; + +template<class T> +struct ptr_helper<T*> +{ + typedef T value_type; + typedef T& reference_type; + typedef T* rvalue_type; + static reference_type ref(T * p) { return *p; } + static const T& ref(const T * p) { return *p; } +}; + +template<class UniformRandomNumberGenerator> +class pass_through_engine +{ +private: + typedef ptr_helper<UniformRandomNumberGenerator> helper_type; + +public: + typedef typename helper_type::value_type base_type; + typedef typename base_type::result_type result_type; + + explicit pass_through_engine(UniformRandomNumberGenerator rng) + // make argument an rvalue to avoid matching Generator& constructor + : _rng(static_cast<typename helper_type::rvalue_type>(rng)) + { } + + result_type min () const { return (base().min)(); } + result_type max () const { return (base().max)(); } + base_type& base() { return helper_type::ref(_rng); } + const base_type& base() const { return helper_type::ref(_rng); } + + result_type operator()() { return base()(); } + +private: + UniformRandomNumberGenerator _rng; +}; + +template<class RealType> +class new_uniform_01 +{ +public: + typedef RealType input_type; + typedef RealType result_type; + // compiler-generated copy ctor and copy assignment are fine + result_type min () const { return result_type(0); } + result_type max () const { return result_type(1); } + void reset() { } + + template<class Engine> + result_type operator()(Engine& eng) { + for (;;) { + typedef typename Engine::result_type base_result; + result_type factor = result_type(1) / + (result_type((eng.max)()-(eng.min)()) + + result_type(std::numeric_limits<base_result>::is_integer ? 1 : 0)); + result_type result = result_type(eng() - (eng.min)()) * factor; + if (result < result_type(1)) + return result; + } + } + + template<class CharT, class Traits> + friend std::basic_ostream<CharT,Traits>& + operator<<(std::basic_ostream<CharT,Traits>& os, const new_uniform_01&) + { + return os; + } + + template<class CharT, class Traits> + friend std::basic_istream<CharT,Traits>& + operator>>(std::basic_istream<CharT,Traits>& is, new_uniform_01&) + { + return is; + } +}; + +template<class UniformRandomNumberGenerator, class RealType> +class backward_compatible_uniform_01 +{ + typedef ptr_helper<UniformRandomNumberGenerator> traits; + typedef pass_through_engine<UniformRandomNumberGenerator> internal_engine_type; +public: + typedef UniformRandomNumberGenerator base_type; + typedef RealType result_type; + + static const bool has_fixed_range = false; + + explicit backward_compatible_uniform_01(typename traits::rvalue_type rng) + : _rng(rng), + _factor(result_type(1) / + (result_type((_rng.max)()-(_rng.min)()) + + result_type(std::numeric_limits<base_result>::is_integer ? 1 : 0))) + { + } + // compiler-generated copy ctor and copy assignment are fine + + result_type min () const { return result_type(0); } + result_type max () const { return result_type(1); } + typename traits::value_type& base() { return _rng.base(); } + const typename traits::value_type& base() const { return _rng.base(); } + void reset() { } + + result_type operator()() { + for (;;) { + result_type result = result_type(_rng() - (_rng.min)()) * _factor; + if (result < result_type(1)) + return result; + } + } + + template<class CharT, class Traits> + friend std::basic_ostream<CharT,Traits>& + operator<<(std::basic_ostream<CharT,Traits>& os, const backward_compatible_uniform_01& u) + { + os << u._rng; + return os; + } + + template<class CharT, class Traits> + friend std::basic_istream<CharT,Traits>& + operator>>(std::basic_istream<CharT,Traits>& is, backward_compatible_uniform_01& u) + { + is >> u._rng; + return is; + } + +private: + typedef typename internal_engine_type::result_type base_result; + internal_engine_type _rng; + result_type _factor; +}; + +// A definition is required even for integral static constants +template<class UniformRandomNumberGenerator, class RealType> +const bool backward_compatible_uniform_01<UniformRandomNumberGenerator, RealType>::has_fixed_range; + +template<class UniformRandomNumberGenerator> +struct select_uniform_01 +{ + template<class RealType> + struct apply + { + typedef backward_compatible_uniform_01<UniformRandomNumberGenerator, RealType> type; + }; +}; + +template<> +struct select_uniform_01<float> +{ + template<class RealType> + struct apply + { + typedef new_uniform_01<float> type; + }; +}; + +template<> +struct select_uniform_01<double> +{ + template<class RealType> + struct apply + { + typedef new_uniform_01<double> type; + }; +}; + +template<> +struct select_uniform_01<long double> +{ + template<class RealType> + struct apply + { + typedef new_uniform_01<long double> type; + }; +}; + +// Because it is so commonly used: uniform distribution on the real [0..1) +// range. This allows for specializations to avoid a costly int -> float +// conversion plus float multiplication +template<class UniformRandomNumberGenerator = double, class RealType = double> +class uniform_01 + : public select_uniform_01<UniformRandomNumberGenerator>::template apply<RealType>::type +{ + typedef typename select_uniform_01<UniformRandomNumberGenerator>::template apply<RealType>::type impl_type; + typedef ptr_helper<UniformRandomNumberGenerator> traits; +public: + + uniform_01() {} + + explicit uniform_01(typename traits::rvalue_type rng) + : impl_type(rng) + { + } + + template<class CharT, class Traits> + friend std::basic_ostream<CharT,Traits>& + operator<<(std::basic_ostream<CharT,Traits>& os, const uniform_01& u) + { + os << static_cast<const impl_type&>(u); + return os; + } + + template<class CharT, class Traits> + friend std::basic_istream<CharT,Traits>& + operator>>(std::basic_istream<CharT,Traits>& is, uniform_01& u) + { + is >> static_cast<impl_type&>(u); + return is; + } +}; + +template<class UniformRandomNumberGenerator, class IntType = unsigned long> +class uniform_int_float +{ +public: + typedef UniformRandomNumberGenerator base_type; + typedef IntType result_type; + + uniform_int_float(base_type rng, IntType min_arg = 0, IntType max_arg = 0xffffffff) + : _rng(rng), _min(min_arg), _max(max_arg) + { + init(); + } + + result_type min () const { return _min; } + result_type max () const { return _max; } + base_type& base() { return _rng.base(); } + const base_type& base() const { return _rng.base(); } + + result_type operator()() + { + return static_cast<IntType>(_rng() * _range) + _min; + } + + template<class CharT, class Traits> + friend std::basic_ostream<CharT,Traits>& + operator<<(std::basic_ostream<CharT,Traits>& os, const uniform_int_float& ud) + { + os << ud._min << " " << ud._max; + return os; + } + + template<class CharT, class Traits> + friend std::basic_istream<CharT,Traits>& + operator>>(std::basic_istream<CharT,Traits>& is, uniform_int_float& ud) + { + is >> std::ws >> ud._min >> std::ws >> ud._max; + ud.init(); + return is; + } + +private: + void init() + { + _range = static_cast<base_result>(_max-_min)+1; + } + + typedef typename base_type::result_type base_result; + uniform_01<base_type> _rng; + result_type _min, _max; + base_result _range; +}; + + +template<class UniformRandomNumberGenerator, class CharT, class Traits> +std::basic_ostream<CharT,Traits>& +operator<<( + std::basic_ostream<CharT,Traits>& os + , const pass_through_engine<UniformRandomNumberGenerator>& ud + ) +{ + return os << ud.base(); +} + +template<class UniformRandomNumberGenerator, class CharT, class Traits> +std::basic_istream<CharT,Traits>& +operator>>( + std::basic_istream<CharT,Traits>& is + , const pass_through_engine<UniformRandomNumberGenerator>& ud + ) +{ + return is >> ud.base(); +} + + + +template<class RealType = double> +class normal_distribution +{ +public: + typedef RealType input_type; + typedef RealType result_type; + + explicit normal_distribution(const result_type& mean_arg = result_type(0), + const result_type& sigma_arg = result_type(1)) + : _mean(mean_arg), _sigma(sigma_arg), _valid(false) + { + //assert(_sigma >= result_type(0)); + } + + // compiler-generated copy constructor is NOT fine, need to purge cache + normal_distribution(const normal_distribution& other) + : _mean(other._mean), _sigma(other._sigma), _valid(false) + { + } + + // compiler-generated copy ctor and assignment operator are fine + + RealType mean() const { return _mean; } + RealType sigma() const { return _sigma; } + + void reset() { _valid = false; } + + template<class Engine> + result_type operator()(Engine& eng) + { +#ifndef BOOST_NO_STDC_NAMESPACE + // allow for Koenig lookup + using std::sqrt; using std::log; using std::sin; using std::cos; +#endif + if(!_valid) { + _r1 = eng(); + _r2 = eng(); + _cached_rho = sqrt(-result_type(2) * log(result_type(1)-_r2)); + _valid = true; + } else { + _valid = false; + } + // Can we have a boost::mathconst please? + const result_type pi = result_type(3.14159265358979323846); + + return _cached_rho * (_valid ? + cos(result_type(2)*pi*_r1) : + sin(result_type(2)*pi*_r1)) + * _sigma + _mean; + } + +#ifndef BOOST_RANDOM_NO_STREAM_OPERATORS + template<class CharT, class Traits> + friend std::basic_ostream<CharT,Traits>& + operator<<(std::basic_ostream<CharT,Traits>& os, const normal_distribution& nd) + { + os << nd._mean << " " << nd._sigma << " " + << nd._valid << " " << nd._cached_rho << " " << nd._r1; + return os; + } + + template<class CharT, class Traits> + friend std::basic_istream<CharT,Traits>& + operator>>(std::basic_istream<CharT,Traits>& is, normal_distribution& nd) + { + is >> std::ws >> nd._mean >> std::ws >> nd._sigma + >> std::ws >> nd._valid >> std::ws >> nd._cached_rho + >> std::ws >> nd._r1; + return is; + } +#endif +private: + result_type _mean, _sigma; + result_type _r1, _r2, _cached_rho; + bool _valid; +}; + +// http://www.math.keio.ac.jp/matumoto/emt.html +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +class mersenne_twister +{ +public: + typedef UIntType result_type; + static const int word_size = w; + static const int state_size = n; + static const int shift_size = m; + static const int mask_bits = r; + static const UIntType parameter_a = a; + static const int output_u = u; + static const int output_s = s; + static const UIntType output_b = b; + static const int output_t = t; + static const UIntType output_c = c; + static const int output_l = l; + + static const bool has_fixed_range = false; + + mersenne_twister() { seed(); } + + explicit mersenne_twister(const UIntType& value) + { seed(value); } + template<class It> mersenne_twister(It& first, It last) { seed(first,last); } + + template<class Generator> \ + explicit mersenne_twister(Generator& gen) + { seed(gen); } + + // compiler-generated copy ctor and assignment operator are fine + + void seed() { seed(UIntType(5489)); } + + void seed(const UIntType& value) + { + // New seeding algorithm from + // http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/emt19937ar.html + // In the previous versions, MSBs of the seed affected only MSBs of the + // state x[]. + const UIntType mask = ~0u; + x[0] = value & mask; + for (i = 1; i < n; i++) { + // See Knuth "The Art of Computer Programming" Vol. 2, 3rd ed., page 106 + x[i] = (1812433253UL * (x[i-1] ^ (x[i-1] >> (w-2))) + i) & mask; + } + } + + // For GCC, moving this function out-of-line prevents inlining, which may + // reduce overall object code size. However, MSVC does not grok + // out-of-line definitions of member function templates. + template<class Generator> \ + void seed(Generator& gen) + { +/*#ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS + BOOST_STATIC_ASSERT(!std::numeric_limits<result_type>::is_signed); +#endif*/ + // I could have used std::generate_n, but it takes "gen" by value + for(int j = 0; j < n; j++) + x[j] = gen(); + i = n; + } + + template<class It> + void seed(It& first, It last) + { + int j; + for(j = 0; j < n && first != last; ++j, ++first) + x[j] = *first; + i = n; + if(first == last && j < n) + throw std::invalid_argument("mersenne_twister::seed"); + } + + result_type min () const { return 0; } + result_type max () const + { + // avoid "left shift count >= with of type" warning + result_type res = 0; + for(int j = 0; j < w; ++j) + res |= (1u << j); + return res; + } + + result_type operator()(); + static bool validation(result_type v) { return val == v; } + + template<class CharT, class Traits> + friend std::basic_ostream<CharT,Traits>& + operator<<(std::basic_ostream<CharT,Traits>& os, const mersenne_twister& mt) + { + for(int j = 0; j < mt.state_size; ++j) + os << mt.compute(j) << " "; + return os; + } + + template<class CharT, class Traits> + friend std::basic_istream<CharT,Traits>& + operator>>(std::basic_istream<CharT,Traits>& is, mersenne_twister& mt) + { + for(int j = 0; j < mt.state_size; ++j) + is >> mt.x[j] >> std::ws; + // MSVC (up to 7.1) and Borland (up to 5.64) don't handle the template + // value parameter "n" available from the class template scope, so use + // the static constant with the same value + mt.i = mt.state_size; + return is; + } + + friend bool operator==(const mersenne_twister& x, const mersenne_twister& y) + { + for(int j = 0; j < state_size; ++j) + if(x.compute(j) != y.compute(j)) + return false; + return true; + } + + friend bool operator!=(const mersenne_twister& x, const mersenne_twister& y) + { return !(x == y); } + +private: + // returns x(i-n+index), where index is in 0..n-1 + UIntType compute(unsigned int index) const + { + // equivalent to (i-n+index) % 2n, but doesn't produce negative numbers + return x[ (i + n + index) % (2*n) ]; + } + void twist(int block); + + // state representation: next output is o(x(i)) + // x[0] ... x[k] x[k+1] ... x[n-1] x[n] ... x[2*n-1] represents + // x(i-k) ... x(i) x(i+1) ... x(i-k+n-1) x(i-k-n) ... x[i(i-k-1)] + // The goal is to always have x(i-n) ... x(i-1) available for + // operator== and save/restore. + + UIntType x[2*n]; + int i; +}; + +// A definition is required even for integral static constants + +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +const bool mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::has_fixed_range; +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +const int mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::state_size; +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +const int mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::shift_size; +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +const int mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::mask_bits; +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +const UIntType mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::parameter_a; +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +const int mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::output_u; +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +const int mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::output_s; +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +const UIntType mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::output_b; +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +const int mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::output_t; +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +const UIntType mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::output_c; +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +const int mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::output_l; + +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +void mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::twist(int block) +{ + const UIntType upper_mask = (~0u) << r; + const UIntType lower_mask = ~upper_mask; + + if(block == 0) { + for(int j = n; j < 2*n; j++) { + UIntType y = (x[j-n] & upper_mask) | (x[j-(n-1)] & lower_mask); + x[j] = x[j-(n-m)] ^ (y >> 1) ^ (y&1 ? a : 0); + } + } else if (block == 1) { + // split loop to avoid costly modulo operations + { // extra scope for MSVC brokenness w.r.t. for scope + for(int j = 0; j < n-m; j++) { + UIntType y = (x[j+n] & upper_mask) | (x[j+n+1] & lower_mask); + x[j] = x[j+n+m] ^ (y >> 1) ^ (y&1 ? a : 0); + } + } + + for(int j = n-m; j < n-1; j++) { + UIntType y = (x[j+n] & upper_mask) | (x[j+n+1] & lower_mask); + x[j] = x[j-(n-m)] ^ (y >> 1) ^ (y&1 ? a : 0); + } + // last iteration + UIntType y = (x[2*n-1] & upper_mask) | (x[0] & lower_mask); + x[n-1] = x[m-1] ^ (y >> 1) ^ (y&1 ? a : 0); + i = 0; + } +} + +template<class UIntType, int w, int n, int m, int r, UIntType a, int u, + int s, UIntType b, int t, UIntType c, int l, UIntType val> +inline typename mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::result_type +mersenne_twister<UIntType,w,n,m,r,a,u,s,b,t,c,l,val>::operator()() +{ + if(i == n) + twist(0); + else if(i >= 2*n) + twist(1); + // Step 4 + UIntType z = x[i]; + ++i; + z ^= (z >> u); + z ^= ((z << s) & b); + z ^= ((z << t) & c); + z ^= (z >> l); + return z; +} + +typedef mersenne_twister<uint32_t,32,351,175,19,0xccab8ee7,11, + 7,0x31b6ab00,15,0xffe50000,17, 0xa37d3c92> mt11213b; + +// validation by experiment from mt19937.c +typedef mersenne_twister<uint32_t,32,624,397,31,0x9908b0df,11, + 7,0x9d2c5680,15,0xefc60000,18, 3346425566U> mt19937; + + +template<class RealType = double, class Cont = std::vector<RealType> > +class uniform_on_sphere +{ +public: + typedef RealType input_type; + typedef Cont result_type; + + explicit uniform_on_sphere(int dim = 2) : _container(dim), _dim(dim) { } + + // compiler-generated copy ctor and assignment operator are fine + + void reset() { _normal.reset(); } + + template<class Engine> + const result_type & operator()(Engine& eng) + { + RealType sqsum = 0; + for(typename Cont::iterator it = _container.begin(); + it != _container.end(); + ++it) { + RealType val = _normal(eng); + *it = val; + sqsum += val * val; + } + using std::sqrt; + // for all i: result[i] /= sqrt(sqsum) + std::transform(_container.begin(), _container.end(), _container.begin(), + std::bind2nd(std::divides<RealType>(), sqrt(sqsum))); + return _container; + } + + template<class CharT, class Traits> + friend std::basic_ostream<CharT,Traits>& + operator<<(std::basic_ostream<CharT,Traits>& os, const uniform_on_sphere& sd) + { + os << sd._dim; + return os; + } + + template<class CharT, class Traits> + friend std::basic_istream<CharT,Traits>& + operator>>(std::basic_istream<CharT,Traits>& is, uniform_on_sphere& sd) + { + is >> std::ws >> sd._dim; + sd._container.resize(sd._dim); + return is; + } + +private: + normal_distribution<RealType> _normal; + result_type _container; + int _dim; +}; + + + +template<bool have_int, bool want_int> +struct engine_helper; + + +template<> +struct engine_helper<true, true> +{ + template<class Engine, class DistInputType> + struct impl + { + typedef pass_through_engine<Engine> type; + }; +}; + +template<> +struct engine_helper<false, false> +{ + template<class Engine, class DistInputType> + struct impl + { + typedef uniform_01<Engine, DistInputType> type; + }; +}; + +template<> +struct engine_helper<true, false> +{ + template<class Engine, class DistInputType> + struct impl + { + typedef uniform_01<Engine, DistInputType> type; + }; +}; + +template<> +struct engine_helper<false, true> +{ + template<class Engine, class DistInputType> + struct impl + { + typedef uniform_int_float<Engine, unsigned long> type; + }; +}; + +template<class Engine, class Distribution> +class variate_generator +{ +private: + typedef pass_through_engine<Engine> decorated_engine; + +public: + typedef typename decorated_engine::base_type engine_value_type; + typedef Engine engine_type; + typedef Distribution distribution_type; + typedef typename Distribution::result_type result_type; + + variate_generator(Engine e, Distribution d) + : _eng(decorated_engine(e)), _dist(d) { } + + result_type operator()() { return _dist(_eng); } + template<class T> + result_type operator()(T value) { return _dist(_eng, value); } + + engine_value_type& engine() { return _eng.base().base(); } + const engine_value_type& engine() const { return _eng.base().base(); } + + distribution_type& distribution() { return _dist; } + const distribution_type& distribution() const { return _dist; } + + result_type min () const { return (distribution().min)(); } + result_type max () const { return (distribution().max)(); } + +private: + enum { + have_int = std::numeric_limits<typename decorated_engine::result_type>::is_integer, + want_int = std::numeric_limits<typename Distribution::input_type>::is_integer + }; + typedef typename engine_helper<have_int, want_int>::template impl<decorated_engine, typename Distribution::input_type>::type internal_engine_type; + + internal_engine_type _eng; + distribution_type _dist; +}; + +} // namespace boost diff --git a/extern/carve/patches/includes.patch b/extern/carve/patches/includes.patch new file mode 100644 index 00000000000..bdf97c846e7 --- /dev/null +++ b/extern/carve/patches/includes.patch @@ -0,0 +1,84 @@ +diff -r c8cbec41cd35 include/carve/exact.hpp +--- a/include/carve/exact.hpp Thu Dec 01 15:51:44 2011 -0500 ++++ b/include/carve/exact.hpp Wed Jan 11 18:48:16 2012 +0600 +@@ -21,7 +21,7 @@ + + #include <vector> + #include <numeric> +- ++#include <algorithm> + + + namespace carve { +diff -r c8cbec41cd35 include/carve/geom2d.hpp +--- a/include/carve/geom2d.hpp Thu Dec 01 15:51:44 2011 -0500 ++++ b/include/carve/geom2d.hpp Wed Jan 11 18:48:16 2012 +0600 +@@ -25,6 +25,7 @@ + #include <carve/geom.hpp> + + #include <vector> ++#include <algorithm> + + #include <math.h> + +diff -r c8cbec41cd35 include/carve/mesh_impl.hpp +--- a/include/carve/mesh_impl.hpp Thu Dec 01 15:51:44 2011 -0500 ++++ b/include/carve/mesh_impl.hpp Wed Jan 11 18:48:16 2012 +0600 +@@ -24,6 +24,8 @@ + #include <iostream> + #include <deque> + ++#include <stddef.h> ++ + namespace carve { + namespace mesh { + +diff -r c8cbec41cd35 include/carve/polyhedron_base.hpp +--- a/include/carve/polyhedron_base.hpp Thu Dec 01 15:51:44 2011 -0500 ++++ b/include/carve/polyhedron_base.hpp Wed Jan 11 18:48:16 2012 +0600 +@@ -25,6 +25,8 @@ + #include <carve/edge_decl.hpp> + #include <carve/face_decl.hpp> + ++#include <stddef.h> ++ + namespace carve { + namespace poly { + +diff -r c8cbec41cd35 include/carve/rtree.hpp +--- a/include/carve/rtree.hpp Thu Dec 01 15:51:44 2011 -0500 ++++ b/include/carve/rtree.hpp Wed Jan 11 18:48:16 2012 +0600 +@@ -27,6 +27,10 @@ + #include <cmath> + #include <limits> + ++#if defined(HAVE_STDINT_H) ++# include <stdint.h> ++#endif ++ + namespace carve { + namespace geom { + +diff -r c8cbec41cd35 include/carve/vector.hpp +--- a/include/carve/vector.hpp Thu Dec 01 15:51:44 2011 -0500 ++++ b/include/carve/vector.hpp Wed Jan 11 18:48:16 2012 +0600 +@@ -24,6 +24,7 @@ + #include <carve/geom3d.hpp> + + #include <sstream> ++#include <algorithm> + + #include <math.h> + +diff -r c8cbec41cd35 src/extrude.cpp +--- a/src/extrude.cpp Thu Dec 01 15:51:44 2011 -0500 ++++ b/src/extrude.cpp Wed Jan 11 18:48:16 2012 +0600 +@@ -34,6 +34,8 @@ + #include <cctype> + #include <stdexcept> + ++#include <stdexcept> ++ + template<unsigned ndim> + carve::geom::vector<ndim> lerp( + double t, diff --git a/extern/carve/patches/mesh_iterator.patch b/extern/carve/patches/mesh_iterator.patch new file mode 100644 index 00000000000..1b9e12866bf --- /dev/null +++ b/extern/carve/patches/mesh_iterator.patch @@ -0,0 +1,21 @@ +diff -r c8cbec41cd35 include/carve/mesh.hpp +--- a/include/carve/mesh.hpp Thu Dec 01 15:51:44 2011 -0500 ++++ b/include/carve/mesh.hpp Thu Jan 12 00:19:58 2012 +0600 +@@ -719,13 +719,13 @@ + void rev(size_t n); + void adv(int n); + +- FaceIter operator++(int) { FaceIter tmp = *this; fwd(1); return tmp; } +- FaceIter operator+(int v) { FaceIter tmp = *this; adv(v); return tmp; } ++ FaceIter operator++(int) { FaceIter tmp = *this; tmp.fwd(1); return tmp; } ++ FaceIter operator+(int v) { FaceIter tmp = *this; tmp.adv(v); return tmp; } + FaceIter &operator++() { fwd(1); return *this; } + FaceIter &operator+=(int v) { adv(v); return *this; } + +- FaceIter operator--(int) { FaceIter tmp = *this; rev(1); return tmp; } +- FaceIter operator-(int v) { FaceIter tmp = *this; adv(-v); return tmp; } ++ FaceIter operator--(int) { FaceIter tmp = *this; tmp.rev(1); return tmp; } ++ FaceIter operator-(int v) { FaceIter tmp = *this; tmp.adv(-v); return tmp; } + FaceIter &operator--() { rev(1); return *this; } + FaceIter &operator-=(int v) { adv(-v); return *this; } + diff --git a/extern/carve/patches/series b/extern/carve/patches/series new file mode 100644 index 00000000000..6e2a36a0576 --- /dev/null +++ b/extern/carve/patches/series @@ -0,0 +1,4 @@ +strict_flags.patch +includes.patch +win32.patch +mesh_iterator.patch diff --git a/extern/carve/patches/strict_flags.patch b/extern/carve/patches/strict_flags.patch new file mode 100644 index 00000000000..5e4b867ba26 --- /dev/null +++ b/extern/carve/patches/strict_flags.patch @@ -0,0 +1,46 @@ +diff -r 47dfdaff1dd5 include/carve/csg_triangulator.hpp +--- a/include/carve/csg_triangulator.hpp Thu Jan 12 15:49:04 2012 -0500 ++++ b/include/carve/csg_triangulator.hpp Fri Jan 13 03:13:32 2012 +0600 +@@ -174,6 +174,7 @@ + + double scoreQuad(edge_map_t::iterator i, edge_map_t &edge_map) { + if (!(*i).second.first || !(*i).second.second) return -1; ++ return 0; + } + + carve::mesh::MeshSet<3>::face_t *mergeQuad(edge_map_t::iterator i, edge_map_t &edge_map) { +diff -r 47dfdaff1dd5 include/carve/exact.hpp +--- a/include/carve/exact.hpp Thu Jan 12 15:49:04 2012 -0500 ++++ b/include/carve/exact.hpp Fri Jan 13 03:13:32 2012 +0600 +@@ -379,7 +379,7 @@ + prod_2_1(b, a, r); + } + +- static inline double prod_4_1(const double *a, const double *b, double *r) { ++ static inline void prod_4_1(const double *a, const double *b, double *r) { + double b_sp[2]; split(b[0], b_sp); + double t1[2]; prod_1_1s(a+0, b, b_sp, t1); + r[0] = t1[0]; +@@ -639,8 +639,9 @@ + } + + +- exact_t operator+(const exact_t &a, const exact_t &b) { +- } ++ // XXX: not implemented yet ++ //exact_t operator+(const exact_t &a, const exact_t &b) { ++ //} + + + +diff -r 47dfdaff1dd5 src/selfintersect.cpp +--- a/src/selfintersect.cpp Thu Jan 12 15:49:04 2012 -0500 ++++ b/src/selfintersect.cpp Fri Jan 13 03:13:32 2012 +0600 +@@ -465,6 +465,7 @@ + + // returns true if no intersection, based upon edge^a_i and edge^b_j separating axis. + bool sat_edge(const vec3 tri_a[3], const vec3 tri_b[3], unsigned i, unsigned j) { ++ return false; + } + + diff --git a/extern/carve/patches/win32.patch b/extern/carve/patches/win32.patch new file mode 100644 index 00000000000..e0834ef1ce1 --- /dev/null +++ b/extern/carve/patches/win32.patch @@ -0,0 +1,29 @@ +diff -r 47dfdaff1dd5 include/carve/win32.h +--- a/include/carve/win32.h Thu Jan 12 15:49:04 2012 -0500 ++++ b/include/carve/win32.h Fri Jan 13 03:15:51 2012 +0600 +@@ -32,14 +32,19 @@ + + # if _MSC_VER < 1600 + // stdint.h is not available before VS2010 +-typedef char int8_t; +-typedef short int16_t; +-typedef long int32_t; ++#if defined(_WIN32) && !defined(__MINGW32__) ++/* The __intXX are built-in types of the visual complier! So we don't ++ need to include anything else here. ++ This typedefs should be in sync with types from MEM_sys_types.h */ + +-typedef unsigned char uint8_t; +-typedef unsigned short uint16_t; +-typedef unsigned long uint32_t; ++typedef signed __int8 int8_t; ++typedef signed __int16 int16_t; ++typedef signed __int32 int32_t; + ++typedef unsigned __int8 uint8_t; ++typedef unsigned __int16 uint16_t; ++typedef unsigned __int32 uint32_t; ++#endif + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + # else |