Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'extern/carve')
-rw-r--r--extern/carve/CMakeLists.txt166
-rw-r--r--extern/carve/LICENSE.GPL2361
-rw-r--r--extern/carve/SConscript22
-rwxr-xr-xextern/carve/bundle.sh123
-rw-r--r--extern/carve/files.txt107
-rw-r--r--extern/carve/include/carve/aabb.hpp150
-rw-r--r--extern/carve/include/carve/aabb_impl.hpp423
-rw-r--r--extern/carve/include/carve/carve.hpp238
-rw-r--r--extern/carve/include/carve/cbrt.h93
-rw-r--r--extern/carve/include/carve/classification.hpp115
-rw-r--r--extern/carve/include/carve/collection.hpp51
-rw-r--r--extern/carve/include/carve/collection/unordered.hpp43
-rw-r--r--extern/carve/include/carve/collection/unordered/boost_impl.hpp45
-rw-r--r--extern/carve/include/carve/collection/unordered/fallback_impl.hpp40
-rw-r--r--extern/carve/include/carve/collection/unordered/libstdcpp_impl.hpp61
-rw-r--r--extern/carve/include/carve/collection/unordered/std_impl.hpp23
-rw-r--r--extern/carve/include/carve/collection/unordered/tr1_impl.hpp58
-rw-r--r--extern/carve/include/carve/collection/unordered/vcpp_impl.hpp65
-rw-r--r--extern/carve/include/carve/collection_types.hpp63
-rw-r--r--extern/carve/include/carve/colour.hpp47
-rw-r--r--extern/carve/include/carve/config.h12
-rw-r--r--extern/carve/include/carve/convex_hull.hpp52
-rw-r--r--extern/carve/include/carve/csg.hpp498
-rw-r--r--extern/carve/include/carve/csg_triangulator.hpp434
-rw-r--r--extern/carve/include/carve/debug_hooks.hpp97
-rw-r--r--extern/carve/include/carve/djset.hpp134
-rw-r--r--extern/carve/include/carve/edge_decl.hpp68
-rw-r--r--extern/carve/include/carve/edge_impl.hpp23
-rw-r--r--extern/carve/include/carve/exact.hpp702
-rw-r--r--extern/carve/include/carve/face_decl.hpp208
-rw-r--r--extern/carve/include/carve/face_impl.hpp140
-rw-r--r--extern/carve/include/carve/faceloop.hpp103
-rw-r--r--extern/carve/include/carve/geom.hpp363
-rw-r--r--extern/carve/include/carve/geom2d.hpp403
-rw-r--r--extern/carve/include/carve/geom3d.hpp310
-rw-r--r--extern/carve/include/carve/geom_impl.hpp651
-rw-r--r--extern/carve/include/carve/gnu_cxx.h4
-rw-r--r--extern/carve/include/carve/heap.hpp425
-rw-r--r--extern/carve/include/carve/input.hpp251
-rw-r--r--extern/carve/include/carve/interpolator.hpp332
-rw-r--r--extern/carve/include/carve/intersection.hpp267
-rw-r--r--extern/carve/include/carve/iobj.hpp106
-rw-r--r--extern/carve/include/carve/kd_node.hpp308
-rw-r--r--extern/carve/include/carve/math.hpp60
-rw-r--r--extern/carve/include/carve/math_constants.hpp33
-rw-r--r--extern/carve/include/carve/matrix.hpp262
-rw-r--r--extern/carve/include/carve/mesh.hpp845
-rw-r--r--extern/carve/include/carve/mesh_impl.hpp1015
-rw-r--r--extern/carve/include/carve/mesh_ops.hpp975
-rw-r--r--extern/carve/include/carve/mesh_simplify.hpp1574
-rw-r--r--extern/carve/include/carve/octree_decl.hpp193
-rw-r--r--extern/carve/include/carve/octree_impl.hpp79
-rw-r--r--extern/carve/include/carve/pointset.hpp24
-rw-r--r--extern/carve/include/carve/pointset_decl.hpp61
-rw-r--r--extern/carve/include/carve/pointset_impl.hpp36
-rw-r--r--extern/carve/include/carve/pointset_iter.hpp18
-rw-r--r--extern/carve/include/carve/poly.hpp24
-rw-r--r--extern/carve/include/carve/poly_decl.hpp25
-rw-r--r--extern/carve/include/carve/poly_impl.hpp25
-rw-r--r--extern/carve/include/carve/polyhedron_base.hpp149
-rw-r--r--extern/carve/include/carve/polyhedron_decl.hpp184
-rw-r--r--extern/carve/include/carve/polyhedron_impl.hpp287
-rw-r--r--extern/carve/include/carve/polyline.hpp24
-rw-r--r--extern/carve/include/carve/polyline_decl.hpp151
-rw-r--r--extern/carve/include/carve/polyline_impl.hpp160
-rw-r--r--extern/carve/include/carve/polyline_iter.hpp198
-rw-r--r--extern/carve/include/carve/rescale.hpp100
-rw-r--r--extern/carve/include/carve/rtree.hpp501
-rw-r--r--extern/carve/include/carve/spacetree.hpp264
-rw-r--r--extern/carve/include/carve/tag.hpp44
-rw-r--r--extern/carve/include/carve/timing.hpp96
-rw-r--r--extern/carve/include/carve/tree.hpp324
-rw-r--r--extern/carve/include/carve/triangulator.hpp175
-rw-r--r--extern/carve/include/carve/triangulator_impl.hpp851
-rw-r--r--extern/carve/include/carve/util.hpp31
-rw-r--r--extern/carve/include/carve/vcpp_config.h17
-rw-r--r--extern/carve/include/carve/vector.hpp163
-rw-r--r--extern/carve/include/carve/vertex_decl.hpp111
-rw-r--r--extern/carve/include/carve/vertex_impl.hpp24
-rwxr-xr-xextern/carve/include/carve/win32.h53
-rw-r--r--extern/carve/lib/aabb.cpp29
-rw-r--r--extern/carve/lib/carve.cpp29
-rw-r--r--extern/carve/lib/convex_hull.cpp100
-rw-r--r--extern/carve/lib/csg.cpp93
-rw-r--r--extern/carve/lib/csg_collector.cpp371
-rw-r--r--extern/carve/lib/csg_collector.hpp24
-rw-r--r--extern/carve/lib/csg_data.hpp52
-rw-r--r--extern/carve/lib/csg_detail.hpp71
-rw-r--r--extern/carve/lib/edge.cpp23
-rw-r--r--extern/carve/lib/face.cpp278
-rw-r--r--extern/carve/lib/geom2d.cpp260
-rw-r--r--extern/carve/lib/geom3d.cpp164
-rw-r--r--extern/carve/lib/intersect.cpp1668
-rw-r--r--extern/carve/lib/intersect_classify_common.hpp46
-rw-r--r--extern/carve/lib/intersect_classify_common_impl.hpp362
-rw-r--r--extern/carve/lib/intersect_classify_edge.cpp820
-rw-r--r--extern/carve/lib/intersect_classify_group.cpp220
-rw-r--r--extern/carve/lib/intersect_common.hpp83
-rw-r--r--extern/carve/lib/intersect_debug.cpp65
-rw-r--r--extern/carve/lib/intersect_debug.hpp29
-rw-r--r--extern/carve/lib/intersect_face_division.cpp1709
-rw-r--r--extern/carve/lib/intersect_group.cpp232
-rw-r--r--extern/carve/lib/intersect_half_classify_group.cpp199
-rw-r--r--extern/carve/lib/intersection.cpp92
-rw-r--r--extern/carve/lib/math.cpp347
-rw-r--r--extern/carve/lib/mesh.cpp1203
-rw-r--r--extern/carve/lib/octree.cpp399
-rw-r--r--extern/carve/lib/pointset.cpp59
-rw-r--r--extern/carve/lib/polyhedron.cpp1103
-rw-r--r--extern/carve/lib/polyline.cpp67
-rw-r--r--extern/carve/lib/tag.cpp24
-rw-r--r--extern/carve/lib/timing.cpp436
-rw-r--r--extern/carve/lib/triangulator.cpp1211
-rwxr-xr-xextern/carve/mkfiles.sh4
-rw-r--r--extern/carve/patches/files/config.h12
-rw-r--r--extern/carve/patches/files/random.hpp773
-rw-r--r--extern/carve/patches/includes.patch84
-rw-r--r--extern/carve/patches/mesh_iterator.patch21
-rw-r--r--extern/carve/patches/series4
-rw-r--r--extern/carve/patches/strict_flags.patch46
-rw-r--r--extern/carve/patches/win32.patch29
121 files changed, 30182 insertions, 0 deletions
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> &current_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> > &current_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