Technical Documentation: CGAL/cgal
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (README.md)
The Computational Geometry Algorithms Library (CGAL) is a C++ library that
aims to provide easy access to efficient and reliable algorithms in
computational geometry.
CGAL Releases
The primary vector of distribution of CGAL are source tarballs, released
twice a year, announced on the web site of CGAL.
Getting Started with CGAL
Since version 5.0, CGAL is a header-only library, meaning that
it is no longer needed to build CGAL libraries before it can be used.
Head over to the CGAL manual
for usage guides and tutorials that will get you started smoothly.
License
See the file LICENSE.md.
CGAL Git Repository Layout
The Git repository of CGAL has a different layout from release tarballs. It
contains a CMakeLists.txt file that serves as anchor for configuring and building programs,
and a set of subfolders, so called packages. Most packages
implement a data structure or an algorithm for CGAL (e.g., Convex_hull_2,
or Triangulation_3); however some packages serve special needs:
Installation- meta-files and CMake-supportMaintenance- infrastructural supportCore,CGALimageIO,Qt_widget,GraphicsView- component librariesScripts- scripts to simplify developer's and user's workTestsuite- infrastructure for testsuiteDocumentation- infrastructure for CGAL's manualSTL_Extension- extensions to the standard template library
More Information
- The CGAL web site
- Documentation of the latest CGAL release
- Documentation of the CGAL
mainbranch (updated weekly) - CGAL daily testsuite results
- Guidelines for CGAL developers and Information for new developers
Documentation Doc CMakeLists (`Documentation/doc/CMakeLists.txt`)
cmake_minimum_required(VERSION 3.12...3.31)
project(Documentation NONE)
Check whether this cmake script is the top level one
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
decide if this is a branch build
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/../VERSION")
set(CGAL_BRANCH_BUILD
ON
CACHE INTERNAL "CGAL Branch build")
endif()
if(CGAL_BRANCH_BUILD)
set(CGAL_ROOT "${CMAKE_SOURCE_DIR}/../..")
else()
set(CGAL_ROOT "${CMAKE_SOURCE_DIR}/..")
endif()
get_filename_component(CGAL_ROOT ${CGAL_ROOT} REALPATH)
else()
option(BUILD_DOC "Build the doxygen documentation" OFF)
if(NOT BUILD_DOC)
return()
endif()
set(CGAL_ROOT "${CMAKE_SOURCE_DIR}")
endif()
cmake_minimum_required(VERSION 3.18...3.31) # for list(SORT ... COMPARE NATURAL)
find_package(Doxygen REQUIRED)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
if (NOT Python3_EXECUTABLE)
message(FATAL_ERROR "Cannot build the documentation without Python3!")
return()
endif()
message(VERBOSE "Using Python version ${Python3_VERSION}: ${Python3_EXECUTABLE}")
if(NOT DOXYGEN_FOUND)
message(WARNING "Cannot build the documentation without Doxygen!")
return()
endif()
#starting from cmake 3.9 the usage of DOXYGEN_EXECUTABLE is deprecated
if(TARGET Doxygen::doxygen)
get_property(
DOXYGEN_EXECUTABLE
TARGET Doxygen::doxygen
PROPERTY IMPORTED_LOCATION)
endif()
macro(subdirlist result curdir)
file(
GLOB children
RELATIVE ${curdir}
${curdir}/*)
set(dirlist "")
foreach(child ${children})
if(IS_DIRECTORY ${curdir}/${child})
set(dirlist ${dirlist} ${child})
endif()
endforeach()
set(${result} ${dirlist})
endmacro()
function(configure_doxygen_package CGAL_PACKAGE_NAME)
if(CGAL_BRANCH_BUILD)
set(CGAL_PACKAGE_DIR ${CGAL_ROOT}/${CGAL_PACKAGE_NAME})
else()
set(CGAL_PACKAGE_DIR ${CGAL_ROOT})
endif()
set(CGAL_PACKAGE_DOC_DIR ${CGAL_PACKAGE_DIR}/doc/${CGAL_PACKAGE_NAME})
set(CGAL_PACKAGE_INCLUDE_DIR ${CGAL_PACKAGE_DIR}/include/)
if(NOT EXISTS ${CGAL_PACKAGE_DOC_DIR}/Doxyfile.in)
return()
endif()
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CGAL_PACKAGE_DOC_DIR}/Doxyfile.in)
set(CGAL_DOC_PACKAGE_DEFAULTS
${CGAL_DOC_DXY_DIR}/${CGAL_PACKAGE_NAME}_defaults.dxy)
file(REMOVE ${CGAL_DOC_PACKAGE_DEFAULTS})
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"@INCLUDE = ${CGAL_DOC_DOXY_DEFAULT}\n")
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"OUTPUT_DIRECTORY = ${CGAL_DOC_OUTPUT_DIR}/${CGAL_PACKAGE_NAME}\n")
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS} "HTML_OUTPUT = .\n")
if(CGAL_DOC_CREATE_LOGS)
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"WARN_LOGFILE = ${CGAL_DOC_LOG_DIR}/${CGAL_PACKAGE_NAME}.log\n")
endif()
if(CGAL_DOC_RELEASE)
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS} "GENERATE_TODOLIST = NO\n")
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS} "GENERATE_BUGLIST = NO\n")
endif()
if(EXISTS "${CGAL_PACKAGE_DIR}/examples")
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"EXAMPLE_PATH = ${CGAL_PACKAGE_DIR}/examples\n")
endif()
if(CGAL_GENERATE_XML)
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS} "GENERATE_XML = YES\n")
endif()
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"GENERATE_TAGFILE = ${CGAL_DOC_TAG_GEN_DIR}/${CGAL_PACKAGE_NAME}.tag\n")
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"STRIP_FROM_PATH = ${CGAL_PACKAGE_DOC_DIR}/\n")
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"STRIP_FROM_PATH += ${CGAL_PACKAGE_DIR}/include/\n")
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"STRIP_FROM_INC_PATH = ${CGAL_PACKAGE_DOC_DIR}/\n")
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"STRIP_FROM_INC_PATH += ${CGAL_PACKAGE_DIR}/include/\n")
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"ALIASES += "cgalPkgDescriptionBegin{2}=\details "\n")
file(
APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"ALIASES += "cgalPkgManuals{2}="\n")
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS} "INPUT = ${CGAL_PACKAGE_DOC_DIR}\n")
#special cases for Stream_support and Documentation package: stream support also needs include/CGAL while Documentation
if((NOT EXISTS "${CGAL_PACKAGE_DOC_DIR}/CGAL" AND (NOT ${CGAL_PACKAGE_NAME} STREQUAL "Documentation")) OR ${CGAL_PACKAGE_NAME} STREQUAL "Stream_support")
# This package has in-source documentation.
if(CGAL_BRANCH_BUILD) # use the entire include subdir
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"INPUT += ${CGAL_PACKAGE_DIR}/include/\n")
else() # use the filelist
if(EXISTS "${CGAL_PACKAGE_DOC_DIR}/filelist.txt")
file(STRINGS "${CGAL_PACKAGE_DOC_DIR}/filelist.txt" CGAL_PKG_FILES)
foreach(pkg_file ${CGAL_PKG_FILES})
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"INPUT += ${CGAL_PACKAGE_DIR}/include/${pkg_file}\n")
endforeach()
endif()
endif()
endif()
if(EXISTS "${CGAL_PACKAGE_DIR}/include/CGAL/${CGAL_PACKAGE_NAME}/internal")
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"EXCLUDE += ${CGAL_PACKAGE_DIR}/include/CGAL/${CGAL_PACKAGE_NAME}/internal\n")
endif()
IMAGE_PATH is set by default. For Documentation, we generate the extra path using packages.txt
set(IMAGE_PATHS "${CGAL_PACKAGE_DOC_DIR}/fig")
set(DEPENDENCIES "")
figure out the dependencies, using the file `dependencies` or `packages.txt` for Documentation
if(${CGAL_PACKAGE_NAME} STREQUAL "Documentation")
set(PackageFile ${CGAL_PACKAGE_DOC_DIR}/packages.txt)
file(STRINGS ${PackageFile} EntriesAsList REGEX "^\\package_listing{.+}$")
foreach(Line ${EntriesAsList})
string(REGEX REPLACE "^\\package_listing{(.+)}$" "\1" PKG "${Line}")
list(APPEND DEPENDENCIES ${PKG})
if(CGAL_BRANCH_BUILD)
set(IMG_DIR "${CGAL_ROOT}/${PKG}/doc/${PKG}/fig")
else()
set(IMG_DIR "${CGAL_ROOT}/doc/${PKG}/fig")
endif()
if(EXISTS ${IMG_DIR})
list(APPEND IMAGE_PATHS ${IMG_DIR})
endif()
endforeach() else()
if(EXISTS ${CGAL_PACKAGE_DOC_DIR}/dependencies)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CGAL_PACKAGE_DOC_DIR}/dependencies)
file(STRINGS ${CGAL_PACKAGE_DOC_DIR}/dependencies DEPENDENCIES)
endif()
endif()
foreach(depend ${DEPENDENCIES})
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS}
"TAGFILES += ${CGAL_DOC_TAG_DIR}/${depend}.tag=../${depend}\n")
endforeach()
foreach(image_path ${IMAGE_PATHS})
file(APPEND ${CGAL_DOC_PACKAGE_DEFAULTS} "IMAGE_PATH += ${image_path}\n")
endforeach()
configure_file(${CGAL_PACKAGE_DOC_DIR}/Doxyfile.in
${CGAL_DOC_DXY_DIR}/${CGAL_PACKAGE_NAME}.dxy)
TODO we also want to run html_post_process per package as a custom_command with POST_BUILD
target that build the doc and put the tag file in the generation directory CGAL_DOC_TAG_GEN_DIR
add_custom_target(
${CGAL_PACKAGE_NAME}_internal_doxygen_run
${DOXYGEN_EXECUTABLE} ${CGAL_DOC_DXY_DIR}/${CGAL_PACKAGE_NAME}.dxy)
set_target_properties(${CGAL_PACKAGE_NAME}_internal_doxygen_run
PROPERTIES FOLDER Documentation/Packages)
#target moving the tag file into the read directory CGAL_DOC_TAG_DIR
if("${CGAL_PACKAGE_NAME}" STREQUAL "Documentation")
set(CGAL_PACKAGE_TAGFILE "Manual.tag")
else()
set(CGAL_PACKAGE_TAGFILE "${CGAL_PACKAGE_NAME}.tag")
endif()
add_custom_target(
${CGAL_PACKAGE_NAME}_copy_doc_tags
${CMAKE_COMMAND} -E copy "${CGAL_DOC_TAG_GEN_DIR}/${CGAL_PACKAGE_TAGFILE}"
"${CGAL_DOC_TAG_DIR}/${CGAL_PACKAGE_TAGFILE}")
#add the doc target doing both the doc generation and then the tag file copy
add_custom_target(
${CGAL_PACKAGE_NAME}_doc
${DOXYGEN_EXECUTABLE} ${CGAL_DOC_DXY_DIR}/${CGAL_PACKAGE_NAME}.dxy
COMMAND
${CMAKE_COMMAND} -E copy
"${CGAL_DOC_TAG_GEN_DIR}/${CGAL_PACKAGE_TAGFILE}"
"${CGAL_DOC_TAG_DIR}/${CGAL_PACKAGE_TAGFILE}")
${depend}_doc)
don't do this for now
foreach(depend ${DEPENDENCIES})
add_dependencies(${CGAL_PACKAGE_NAME}_doc
${depend}_doc)
endforeach()
endfunction()
set up the directories and variables
if(CGAL_DOC_MATHJAX_LOCATION)
set(CGAL_DOC_MATHJAX_LOCATION_FULL_OPTION_LINE
"MATHJAX_RELPATH = ${CGAL_DOC_MATHJAX_LOCATION}")
else()
set(CGAL_DOC_MATHJAX_LOCATION
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2")
endif()
set(CGAL_DOC_OUTPUT_DIR "${CMAKE_BINARY_DIR}/doc_output")
file(MAKE_DIRECTORY "${CGAL_DOC_OUTPUT_DIR}")
option(
CGAL_DOC_CREATE_LOGS
"Write log files or print warnings and errors to cerr when building the doc."
OFF)
if(CGAL_DOC_CREATE_LOGS)
set(CGAL_DOC_LOG_DIR "${CMAKE_BINARY_DIR}/doc_log")
file(MAKE_DIRECTORY "${CGAL_DOC_LOG_DIR}")
endif()
option(CGAL_EXTRACT_ALL_NO_DETAILED_IF_EMPTY
"Use CGAL special doxygen setting EXTRACT_ALL_NO_DETAILED_IF_EMPTY." ON)
if(CGAL_EXTRACT_ALL_NO_DETAILED_IF_EMPTY)
set(CGAL_OPT_EXTRACT_ALL_NO_DETAILED_IF_EMPTY
"EXTRACT_ALL_NO_DETAILED_IF_EMPTY = YES")
else()
The default is NO, so we could leave it out, but it is better to have a commented out placeholder
this will work for versions with and without the setting.
set(CGAL_OPT_EXTRACT_ALL_NO_DETAILED_IF_EMPTY
"#EXTRACT_ALL_NO_DETAILED_IF_EMPTY = NO")
endif()
option(CGAL_NO_ADDITIONAL_DETAILS
"Use CGAL special doxygen setting NO_ADDITIONAL_DETAILS." ON)
if(CGAL_NO_ADDITIONAL_DETAILS)
set(CGAL_OPT_NO_ADDITIONAL_DETAILS
"NO_ADDITIONAL_DETAILS = YES")
else()
The default is NO, so we could leave it out, but it is better to have a commented out placeholder
this will work for versions with and without the setting.
set(CGAL_OPT_NO_ADDITIONAL_DETAILS
"#NO_ADDITIONAL_DETAILS = NO")
endif()
#we use two directories for the generation/reading of tag files to prevent issues
#if the targets are built in parallel
set(CGAL_DOC_TAG_GEN_DIR "${CMAKE_BINARY_DIR}/doc_gen_tags")
file(MAKE_DIRECTORY "${CGAL_DOC_TAG_GEN_DIR}")
set(CGAL_DOC_TAG_DIR "${CMAKE_BINARY_DIR}/doc_tags")
file(MAKE_DIRECTORY "${CGAL_DOC_TAG_DIR}")
set(CGAL_DOC_DXY_DIR "${CMAKE_BINARY_DIR}/doc_dxy")
file(MAKE_DIRECTORY "${CGAL_DOC_DXY_DIR}")
#Setting the resource directory depending on the version of doxygen
set(CGAL_DOC_RESOURCE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/resources")
first look if resources for the specific doxygen version is available, fallback
on the default otherwise
#select only the version number (not the commit hash)
string(REPLACE " " ";" DOXYGEN_VERSION ${DOXYGEN_VERSION})
list(GET DOXYGEN_VERSION 0 DOXYGEN_VERSION)
message(VERBOSE "Doxygen version ${DOXYGEN_VERSION}: ${DOXYGEN_EXECUTABLE}")
The Doxygen version is search in that sorted list (to find the index for which the version is less or equal)
set(CGAL_DOXYGEN_RESOURCES_VERSIONS 1.9.6 1.14.0)
list(SORT CGAL_DOXYGEN_RESOURCES_VERSIONS COMPARE NATURAL)
The GLOB is here to check that the list in CGAL_DOXYGEN_RESOURCES_VERSIONS is correct.
CGAL_DOXYGEN_RESOURCES_DIRS is also used below.
file(GLOB CGAL_DOXYGEN_RESOURCES_DIRS
RELATIVE "${CGAL_DOC_RESOURCE_PREFIX_DIR}"
"${CGAL_DOC_RESOURCE_PREFIX_DIR}/*")
list(SORT CGAL_DOXYGEN_RESOURCES_DIRS COMPARE NATURAL)
if(NOT CGAL_DOXYGEN_RESOURCES_DIRS STREQUAL CGAL_DOXYGEN_RESOURCES_VERSIONS)
message(WARNING "The directories in ${CGAL_DOC_RESOURCE_PREFIX_DIR} do not match the expected versions:\n"
" [${CGAL_DOXYGEN_RESOURCES_DIRS}] vs [${CGAL_DOXYGEN_RESOURCES_VERSIONS}]")
endif()
function(CGAL_insert_in_sorted_list list_name value)
set(list ${${list_name}})
if(NOT value IN_LIST list)
list(APPEND list "${value}")
list(SORT list COMPARE NATURAL)
endif()
set(${list_name} ${list} PARENT_SCOPE)
endfunction()
if(DOXYGEN_VERSION IN_LIST CGAL_DOXYGEN_RESOURCES_DIRS)
list(FIND CGAL_DOXYGEN_RESOURCES_DIRS "${DOXYGEN_VERSION}" DOXYGEN_VERSION_INDEX)
else()
CGAL_insert_in_sorted_list(CGAL_DOXYGEN_RESOURCES_DIRS ${DOXYGEN_VERSION})
list(FIND CGAL_DOXYGEN_RESOURCES_DIRS ${DOXYGEN_VERSION} DOXYGEN_VERSION_INDEX)
math(EXPR DOXYGEN_VERSION_INDEX "${DOXYGEN_VERSION_INDEX} - 1")
endif()
list(GET CGAL_DOXYGEN_RESOURCES_DIRS "${DOXYGEN_VERSION_INDEX}" CGAL_DOC_RESOURCE_DIR)
set(CGAL_DOC_RESOURCE_DIR "${CGAL_DOC_RESOURCE_PREFIX_DIR}/${CGAL_DOC_RESOURCE_DIR}")
if(NOT EXISTS "${CGAL_DOC_RESOURCE_DIR}")
message(FATAL_ERROR "Doxygen resources for version ${DOXYGEN_VERSION} not found")
else()
message(VERBOSE "Using Doxygen resources from ${CGAL_DOC_RESOURCE_DIR}")
endif()
set(CGAL_DOC_BIBLIO_DIR "${CMAKE_CURRENT_LIST_DIR}/biblio")
set(CGAL_DOC_SCRIPT_DIR "${CMAKE_CURRENT_LIST_DIR}/scripts")
set(CGAL_DOC_HEADER ${CGAL_DOC_DXY_DIR}/header.html)
configure_file(${CGAL_DOC_RESOURCE_DIR}/header.html ${CGAL_DOC_HEADER} @ONLY)
set(CGAL_DOC_HEADER_PACKAGE ${CGAL_DOC_DXY_DIR}/header_package.html)
configure_file(${CGAL_DOC_RESOURCE_DIR}/header_package.html
${CGAL_DOC_HEADER_PACKAGE} @ONLY)
#Generate the bibtex file
#this is needed also for BaseDoxyfile.in
string(TIMESTAMP CGAL_BUILD_YEAR4 "%Y")
string(TIMESTAMP CGAL_BUILD_YEAR2 "%y")
string(TIMESTAMP CGAL_BUILD_MONTH "%m")
if(${CGAL_BUILD_MONTH} GREATER 6)
set(CGAL_RELEASE_YEAR_ID "${CGAL_BUILD_YEAR2}b")
else()
set(CGAL_RELEASE_YEAR_ID "${CGAL_BUILD_YEAR2}a")
endif()
if(NOT CGAL_CREATED_VERSION_NUM)
if(CGAL_BRANCH_BUILD)
include(${CGAL_ROOT}/CGALConfigVersion.cmake)
# Prefer the public release version (e.g. "6.0-beta1") which includes
# pre-release suffixes; fall back to computing from components when it
# is not available (issue #8322).
if(CGAL_VERSION_PUBLIC_RELEASE_VERSION)
set(CGAL_CREATED_VERSION_NUM "${CGAL_VERSION_PUBLIC_RELEASE_VERSION}")
elseif(CGAL_BUGFIX_VERSION AND CGAL_BUGFIX_VERSION GREATER 0)
set(CGAL_CREATED_VERSION_NUM
"${CGAL_MAJOR_VERSION}.${CGAL_MINOR_VERSION}.${CGAL_BUGFIX_VERSION}")
else()
set(CGAL_CREATED_VERSION_NUM
"${CGAL_MAJOR_VERSION}.${CGAL_MINOR_VERSION}")
endif() else()
if(EXISTS "${CGAL_ROOT}/doc/public_release_name")
file(STRINGS "${CGAL_ROOT}/doc/public_release_name" CGAL_VERSION_LINE)
string(REGEX REPLACE "CGAL-" "" CGAL_CREATED_VERSION_NUM
"${CGAL_VERSION_LINE}")
else()
#read version.h and get the line with CGAL_VERSION
file(STRINGS "${CGAL_ROOT}/include/CGAL/version.h" CGAL_VERSION_LINE
REGEX "CGAL_VERSION ")
#extract release id, including any pre-release suffix like -beta1, -dev,
#or multi-segment ids such as -Ic-33
string(REGEX MATCH "[0-9]+\.[0-9]+(\.[0-9]+)?(-[a-zA-Z0-9]+)*" CGAL_CREATED_VERSION_NUM
"${CGAL_VERSION_LINE}")
endif()
endif()
endif()
Introduce our own version variable. This way we avoid tying this too
closely to the convoluted versioning code and can adapt without a
huge diff.
set(CGAL_DOC_VERSION ${CGAL_CREATED_VERSION_NUM})
generate how_to_cite files
execute_process(
COMMAND ${Python3_EXECUTABLE} ${CGAL_DOC_SCRIPT_DIR}/generate_how_to_cite.py
${CGAL_ROOT} ${CMAKE_BINARY_DIR} "${CGAL_BRANCH_BUILD}"
RESULT_VARIABLE GENERATE_HOW_TO_CITE_RESULT)
if(NOT GENERATE_HOW_TO_CITE_RESULT EQUAL "0")
message(FATAL_ERROR "generate_how_to_cite did not exit correctly")
endif()
configure_file(${CMAKE_BINARY_DIR}/how_to_cite_cgal.bib.in
${CMAKE_BINARY_DIR}/how_to_cite_cgal.bib)
configure_file(${CMAKE_BINARY_DIR}/how_to_cite_cgal.txt.in
${CMAKE_BINARY_DIR}/how_to_cite_cgal.txt)
configure_file(${CMAKE_BINARY_DIR}/how_to_cite.html.in
${CMAKE_BINARY_DIR}/how_to_cite.html)
configure_file(${CGAL_DOC_RESOURCE_DIR}/BaseDoxyfile.in
${CGAL_DOC_DXY_DIR}/BaseDoxyfile)
set(CGAL_DOC_DOXY_DEFAULT "${CGAL_DOC_DXY_DIR}/BaseDoxyfile")
if (BE_QUIET)
file(APPEND ${CGAL_DOC_DOXY_DEFAULT} "WARN_LOGFILE=doxygen.log\n")
endif()
pkglist_filter gets the path to the pkglist_filter of this source
directory.
if(WIN32)
configure_file(${CGAL_DOC_SCRIPT_DIR}/pkglist_filter.bat
${CMAKE_BINARY_DIR}/pkglist_filter.bat)
endif()
configure_file(${CGAL_DOC_SCRIPT_DIR}/pkglist_filter
${CMAKE_BINARY_DIR}/pkglist_filter)
configure_file(${CGAL_DOC_SCRIPT_DIR}/pkglist_filter.py
${CMAKE_BINARY_DIR}/pkglist_filter.py)
set(CGAL_DOC_PACKAGES "")
if(CGAL_BRANCH_BUILD)
file(
GLOB CGAL_ALL_PACKAGES
RELATIVE ${CGAL_ROOT}
"${CGAL_ROOT}/*")
foreach(pkg ${CGAL_ALL_PACKAGES})
#detect packages with documentation to process
if(IS_DIRECTORY ${CGAL_ROOT}/${pkg})
if(EXISTS "${CGAL_ROOT}/${pkg}/doc/${pkg}/PackageDescription.txt")
list(APPEND CGAL_DOC_PACKAGES ${pkg})
endif()
endif()
#set up directories of packages
if(IS_DIRECTORY ${CGAL_ROOT}/${pkg}/examples)
set(CGAL_${pkg}EXAMPLE_DIR "${CGAL_ROOT}/${pkg}/examples")
endif()
if(IS_DIRECTORY ${CGAL_ROOT}/${pkg}/include)
set(CGAL${pkg}INCLUDE_DIR "${CGAL_ROOT}/${pkg}/include")
endif()
if(IS_DIRECTORY ${CGAL_ROOT}/${pkg}/demo)
set(CGAL${pkg}_DEMO_DIR "${CGAL_ROOT}/${pkg}/demo")
endif()
endforeach()
else()
subdirlist(CGAL_DOC_PACKAGES_TMP ${CGAL_ROOT}/doc)
set(CGAL_DOC_PACKAGES)
foreach(package ${CGAL_DOC_PACKAGES_TMP})
if(EXISTS "${CGAL_ROOT}/doc/${package}/PackageDescription.txt")
list(APPEND CGAL_DOC_PACKAGES "${package}")
endif()
endforeach()
subdirlist(CGAL_EXAMPLE_PACKAGES ${CGAL_ROOT}/examples)
subdirlist(CGAL_DEMO_PACKAGES ${CGAL_ROOT}/demo)
list(APPEND CGAL_EXAMPLE_PACKAGES "BGL"
)# manually add BGL to the list of packages
foreach(pkg ${CGAL_EXAMPLE_PACKAGES})
set(CGAL_${pkg}EXAMPLE_DIR "${CGAL_ROOT}/examples")
# This might not be entirely correct, but there is no reliable,
# easy way to get a list of all package names when not in a
# branch build.
set(CGAL${pkg}INCLUDE_DIR "${CGAL_ROOT}/include")
endforeach()
foreach(pkg ${CGAL_DEMO_PACKAGES})
set(CGAL${pkg}_DEMO_DIR "${CGAL_ROOT}/demo")
endforeach()
endif()
#special cases
if(NOT CGAL_BRANCH_BUILD)
foreach(pkg "Mesher_level" "Surface_mesh_decomposition")
set(CGAL_${pkg}_INCLUDE_DIR "${CGAL_ROOT}/include")
endforeach()
endif()
option(CGAL_BUILD_THREE_DOC "Build the documentation of the Three package" OFF)
if(NOT CGAL_BUILD_THREE_DOC)
list(REMOVE_ITEM CGAL_DOC_PACKAGES "Three")
endif()
foreach(package ${CGAL_DOC_PACKAGES})
configure_doxygen_package(${package})
endforeach()
Add a custom target "doc"
add_custom_target(doc)
add_custom_target(doc_pre)
add_custom_target(doc_post)
do the main package manually, it isn't part of ${CGAL_CONFIGURED_PACKAGES_NAMES}
configure_doxygen_package("Documentation")
set_target_properties(Documentation_doc PROPERTIES FOLDER Documentation)
hard-code that doc depends on all packages
foreach(depend ${CGAL_DOC_PACKAGES})
add_dependencies(doc_pre ${depend}_internal_doxygen_run)
add_dependencies(doc_post ${depend}_copy_doc_tags)
add_dependencies(${depend}_copy_doc_tags doc_pre)
endforeach()
add_dependencies(doc_pre Documentation_internal_doxygen_run)
add_dependencies(doc_post Documentation_copy_doc_tags)
add_dependencies(Documentation_copy_doc_tags doc_pre)
#total level doc dependencies
add_dependencies(doc doc_post)
set(CGAL_DOC_TESTSUITE_SCRIPT "${CGAL_DOC_SCRIPT_DIR}/testsuite.py")
add_custom_target(
doc_with_postprocessing
${Python3_EXECUTABLE} ${CGAL_DOC_SCRIPT_DIR}/html_output_post_processing.py
--output ${CGAL_DOC_OUTPUT_DIR} --resources ${CGAL_DOC_RESOURCE_DIR})
add_dependencies(doc_with_postprocessing doc)
if(CGAL_DOC_CREATE_LOGS)
add_custom_target(
Documentation_test
${Python3_EXECUTABLE} ${CGAL_DOC_TESTSUITE_SCRIPT} --output-dir
${CGAL_DOC_OUTPUT_DIR} --doc-log-dir ${CGAL_DOC_LOG_DIR})
add_dependencies(Documentation_test doc)
add_custom_target(
Documentation_test_publish
${Python3_EXECUTABLE}
${CGAL_DOC_TESTSUITE_SCRIPT}
--output-dir
${CGAL_DOC_OUTPUT_DIR}
--doc-log-dir
${CGAL_DOC_LOG_DIR}
--publish
${CGAL_DOC_PUBLISH_DIR}
--do-copy-results)
add_dependencies(Documentation_test_publish doc)
add_custom_target(
doc_and_publish_testsuite
${Python3_EXECUTABLE}
${CGAL_DOC_TESTSUITE_SCRIPT}
--output-dir
${CGAL_DOC_OUTPUT_DIR}
--doc-log-dir
${CGAL_DOC_LOG_DIR}
--publish
${CGAL_DOC_PUBLISH_DIR}
--do-copy-results
--cgal-version
"${CGAL_FULL_VERSION}"
--version-to-keep
10)
add_dependencies(doc_and_publish_testsuite doc_with_postprocessing)
endif()
AABB Tree Doc AABB Tree PackageDescription (`AABB_tree/doc/AABB_tree/PackageDescription.txt`)
/// \defgroup PkgAABBTreeRef Reference Manual
/// \defgroup PkgAABBTreeConcepts Concepts
/// \ingroup PkgAABBTreeRef
/*!
\addtogroup PkgAABBTreeRef
\cgalPkgDescriptionBegin{2D and 3D Fast Intersection and Distance Computation,PkgAABBTree}
\cgalPkgPicture{aabb-teaser-thumb.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Pierre Alliez, Stéphane Tayeb, and Camille Wormser}
\cgalPkgDesc{The AABB (axis-aligned bounding box) tree component offers a static data structure and algorithms to perform efficient intersection and distance queries on sets of finite 2D and 3D geometric objects.}
\cgalPkgManuals{Chapter_Fast_Intersection_and_Distance_Computation,PkgAABBTreeRef}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{3.5}
\cgalPkgBib{cgal:atw-aabb}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgDemo{AABB Tree,AABB_demo.zip}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
\cgalCRPSection{Concepts}
AABBPrimitiveAABBPrimitiveWithSharedDataAABBGeomTraits_2AABBGeomTraits_3AABBTraitsAABBRayIntersectionGeomTraits_2AABBRayIntersectionGeomTraits_3AABBRayIntersectionTraits
\cgalCRPSection{Classes}
CGAL::AABB_traits<GeomTraits,Primitive>(deprecated, useCGAL::AABB_traits_3<GeomTraits,Primitive>)CGAL::AABB_traits_2<GeomTraits,Primitive>CGAL::AABB_traits_3<GeomTraits,Primitive>CGAL::AABB_tree<AT>
\cgalCRPSection{Primitives}
CGAL::AABB_triangle_primitive_2<GeomTraits, Iterator, CacheDatum>CGAL::AABB_indexed_triangle_primitive_2<GeomTraits, IndexIterator, PointRange, CacheDatum, PointMap>CGAL::AABB_segment_primitive_2<GeomTraits, Iterator, CacheDatum>CGAL::AABB_polyline_segment_primitive_2<GeomTraits, Iterator, PointRange, CacheDatum, PointMap>CGAL::AABB_triangle_primitive<GeomTraits, Iterator, CacheDatum>(deprecated, useCGAL::AABB_triangle_primitive_3<GeomTraits, Iterator, CacheDatum>)CGAL::AABB_triangle_primitive_3<GeomTraits, Iterator, CacheDatum>CGAL::AABB_segment_primitive<GeomTraits, Iterator, CacheDatum>(deprecated, useCGAL::AABB_segment_primitive_3<GeomTraits, Iterator, CacheDatum>)CGAL::AABB_segment_primitive_3<GeomTraits, Iterator, CacheDatum>CGAL::AABB_primitiveCGAL::AABB_halfedge_graph_segment_primitive<HalfedgeGraph,Vpm,OneHalfedgeGraphPerTree,CacheDatum>CGAL::AABB_face_graph_triangle_primitive<FaceGraph,Vpm,OneFaceGraphPerTree,CacheDatum>
\cgalCRPSection{Functions}
CGAL::AABB_trees::do_intersect()CGAL::AABB_trees::all_pairs_of_intersecting_primitives()
*/
AABB Tree Doc AABB Tree Aabb Tree (`AABB_tree/doc/AABB_tree/aabb_tree.txt`)
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_Fast_Intersection_and_Distance_Computation
\cgalAutoToc
\authors Pierre Alliez, Stephane Tayeb, and Camille Wormser
\section aabb_tree_introduction Introduction
The AABB tree component offers a static data structure and algorithms
to perform efficient intersection and distance queries against sets of
finite 2D or 3D geometric objects. The set of geometric objects stored in
the data structure can be queried for intersection detection,
intersection computation and distance. The intersection queries can be
of any type, provided that the corresponding intersection predicates
and constructors are implemented in the traits class. The distance
queries are limited to point queries. Examples of intersection queries
include line objects (rays, lines, segments) against sets of
triangles, or plane objects (planes, triangles) against sets of
segments. An example of a distance query consists of finding the
closest point from a point query to a set of triangles.
Similarly to the package \ref PkgBoxIntersectionD,
this component might also be used for finding all intersecting pairs of objects,
and can even achieve a better performance when running on several threads.
The AABB tree data structure takes as input an iterator range of
geometric data, which is then converted into primitives. From these
primitives a hierarchy of axis-aligned bounding boxes (AABBs) is
constructed and used to speed up intersection and distance queries.
Each primitive gives access to both one input geometric object
(so-called datum) and one reference id to this object. A typical
example primitive wraps a 3D triangle as datum and a face handle of a
polyhedral surface as id. Each intersection query can return the
intersection objects (e.g., 3D points or segments for ray queries) as
well as the id (here the face handle) of the intersected
primitives. Similarly, each distance query can return the closest
point from the point query as well as the id of the closest primitive.
\cgalFigureBegin{aabbtree_meca,anchor.png}
Left: surface triangle mesh of a mechanical part. Right: AABB tree constructed.
\cgalFigureEnd
\section aabb_tree_interface Interface
The main entry point to the component is the class AABB_tree
which represents a static AABB tree constructed from an iterator range
of geometric data. Once instantiated an AABB tree can be queried for
intersection and distance queries.
\b Intersections. Assume for example that the tree contains triangle
primitives. The tree can be queried for intersection against line
objects (rays, segments or line) in various ways. We distinguish
intersection tests which do not construct any intersection
objects, from intersections which construct the intersection
objects.
Tests:
- Function
AABB_tree::do_intersect()tests if the input primitives are
intersected by the query. This function is fast as it involves only
predicates and stops after the first encountered intersection. - Function
AABB_tree::number_of_intersected_primitives()counts all
intersected primitives. - Function
AABB_tree::all_intersected_primitives()enumerates all intersected
primitives ids without constructing the corresponding intersection
objects. - Function
AABB_tree::any_intersected_primitive()returns the first
encountered intersecting primitive id (if any) without constructing
the corresponding intersection object, and stops after the first
encountered intersection. Note that the traversal order of the tree is
such that first herein does not refer to any particular ordering of
the intersections with respect to the query. - Function
AABB_tree::first_intersected_primitive()returns
the intersecting primitive id (if any) of the corresponding
intersection object that is closest to the source of the ray.
Constructions:
- Function
AABB_tree::all_intersections()detects and constructs all
intersection objects with the input primitives. - Function
AABB_tree::any_intersection()detects and constructs the first
encountered intersection and constructs the corresponding object. This
function is fast as it stops after the first encountered intersection. - Function
AABB_tree::first_intersection()detects and constructs
the intersection object that is closest to the source of the ray.
\b Distance. An AABB tree computes the closest point from a
given point query to the input primitives through the functionAABB_tree::closest_point(). In addition, it can compute the id of the
closest primitive from a given point query through the functionAABB_tree::closest_point_and_primitive(), i.e., the id of the
primitive which realizes the minimum distance from the point
query. The AABB tree uses a secondary search structure to speed up the
distance queries. The construction of this secondary structure should
be requested by the user by a call toAABB_tree::accelerate_distance_queries() before the first the distance
computation. This data structure is not generated by default because
it is used only for distance computations.
\warning Having degenerate primitives in the AABB-tree is not recommended as the underlying
predicates and constructions of the traits class might not be able to handle them.
For example if one is using CGAL::AABB_traits with a Kernel from \cgal,
having degenerate triangles or segments in the AABB-tree will result in an undefined
behavior or a crash.
\section two_aabb_tree_interface Operations between Two AABB Trees
Given two AABB trees, the function CGAL::AABB_trees::do_intersect() determines whether any
primitive of the first tree intersects a primitive of the second tree.
To enumerate all intersecting pairs of primitives, use CGAL::AABB_trees::all_pairs_of_intersecting_primitives().
These functions provide an alternative to CGAL::box_intersection_d(). In practice, CGAL::AABB_trees::all_pairs_of_intersecting_primitives() generally provides better performance,
particularly when using a parallel execution tag (see \ref aabb_tree_perf).
Note that compared to CGAL::box_intersection_d(), the tree of primitives is actually constructed. It becomes even more interesting in term of runtime
if several calls to the aforementioned intersection functions are done with the same tree.
\section aabb_tree_examples Examples
\subsection aabb_tree_examples_1 Tree of Triangles, for Intersection and Distance Queries
In the following example a set of 3D triangles is stored in a
list. The AABB primitive wraps a triangle as datum and an
iterator in the list as id. We compute the number of input
triangles intersected by a ray query, as well as the closest point and
the squared distance from a point query.
\cgalExample{AABB_tree/AABB_triangle_3_example.cpp}
\subsection aabb_tree_examples_2 Tree of Polyhedron Triangle Facets for Intersection Queries
In the following example the AABB primitive wraps a facet handle of a
triangle polyhedral surface as id and the corresponding 3D
triangle as geometric object. From a segment query we test the
intersections, then compute the number of intersections, compute the
first encountered intersection (generally a point), compute all
intersections (where each intersection is a pair of one \cgal object
and one primitive id - here a face handle) and compute all intersected
primitives. The latter involves only tests and no predicates and is
hence faster than computing all intersections. We also compute the
first encountered intersection with a plane query, which is generally
a segment. We finally compute the first intersection along a ray.
\cgalExample{AABB_tree/AABB_polyhedron_facet_intersection_example.cpp}
\subsection aabb_ray_shooting Ray Shooting
In the following example we load a closed polyhedral surface and perform a
ray shooting query from the centroid of each face, orthogonally to the face
towards the interior. As the centroid is computed with floating point
arithmetic the first face hit by the ray may be the face of the centroid.
The skip functor takes care of ignoring the face.
\cgalExample{AABB_tree/AABB_ray_shooting_example.cpp}
\subsection aabb_tree_examples_3 Tree of Polyhedron Triangle Facets for Distance Queries
In the following example the AABB primitive wraps a facet handle of a
triangle polyhedral surface as id and the corresponding 3D
triangle as geometric object. From a point query we compute the
squared distance, the closest point as well as the closest point and
primitive id. The latter returns a pair composed of a point and a face
handle.
\cgalExample{AABB_tree/AABB_polyhedron_facet_distance_example.cpp}
\subsection aabb_tree_examples_4 Tree of Segments for Intersection and Distance Queries
In the following example the segments are stored into a list, and the
AABB primitive wraps a segment as datum and an iterator in the
list as id. We compute the number of intersections with plane
and triangles queries, and the closest point from a point query.
\cgalExample{AABB_tree/AABB_segment_3_example.cpp}
\subsection aabb_tree_examples_8 Tree of Polyline and Polygon Segments for Intersection and Distance Queries
The following example uses segments given by a polyline and a polygon. The difference is that the polygon is closed. The id in this case is the iterator pointing to a Point_2 in the polyline or the polygon. The datum, a Segment_2, is created on the fly from the points using the id as the source and the following Point_2 as the target. We count the intersections with a Segment_2 and the closest point and id from a point query.
\cgalExample{AABB_tree/AABB_polyline_segment_2_example.cpp}
\subsection aabb_tree_examples_5 Tree of Polyhedron Edge Segments for Intersection and Distance Queries
In the following example the AABB primitive wraps a halfedge handle asid and generates a 3D segment on the fly, each time its methoddatum is called. We compute the number of intersections with a
triangle query and the closest point from a point query.
\cgalExample{AABB_tree/AABB_polyhedron_edge_example.cpp}
\subsection aabb_tree_examples_6 Incremental Insertion of Primitives
The AABB tree is a static data structure, but it allows to insert
primitives, and will internally rebuild triggered by the first query,
or because the user calls the AABB_tree::build() method. The following
example illustrates this for two polyhedral surfaces.
\cgalExample{AABB_tree/AABB_insertion_example.cpp}
\subsection aabb_tree_examples_7 Trees of Custom Primitives
The AABB tree example folder contains three examples of trees
constructed with custom primitives. In \ref AABB_tree/AABB_custom_example.cpp "AABB_custom_example.cpp"
the primitive contains triangles which are defined by three pointers
to custom points. In \ref AABB_tree/AABB_custom_triangle_soup_example.cpp "AABB_custom_triangle_soup_example.cpp" all input
triangles are stored into a single array as to form a triangle
soup. The primitive internally uses a boost::iterator_adaptor
as to provide the three functions AABBPrimitive::id(), AABBPrimitive::datum(),
and AABBPrimitive::reference_point() required by the primitive concept. In
\ref AABB_tree/AABB_custom_indexed_triangle_set_example.cpp "AABB_custom_indexed_triangle_set_example.cpp" the input is an indexed
triangle set stored through two arrays: one array of points and one
array of indices which refer to the point array. Here also the
primitive internally uses a boost::iterator_adaptor.
\subsection aabb_tree_examples_9 Intersection of Two Trees
In the following example, we compute all the intersections between two triangle soups both representing a tetrahedron. The triangle soups
are stored as a vector of Triangle_3. We first compute if the tetrahedra do intersect and then we
compute the indices of the intersecting triangles.
\cgalExample{AABB_tree/AABB_intersection_example.cpp}
\section aabb_tree_perf Performances
We provide some performance numbers for the case where the AABB tree
contains a set of polyhedron triangle facets. We measure the tree
construction time, the memory occupancy and the number of queries per
second for a variety of intersection and distance queries. The machine
used is a PC running Linux Mint with an Intel CPU Core Ultra 7 155H with 16 cores and 32GB of RAM.
By default, the kernel used is Simple_cartesian<double> (the fastest in our experiments). The
program has been compiled with the compiler gcc 13.3 2023 with the O3
option which maximizes speed.
\subsection aabb_tree_perf_cons Construction
The surface triangle mesh chosen for benchmarking the tree
construction is the knot model (14,400 triangles) depicted by
\cgalFigureRef{figAABB-tree-bench}. We measure the tree construction time (both
AABB tree alone and AABB tree with internal KD-tree) for four denser versions of the knot model.
| Number of triangles | Construction (in ms) (Parallel) | (Sequential) | Construction with internal KD-tree (in ms) (Parallel) | (Sequential) |
|---|---|---|---|---|
| 230,400 | 20 | 36 | 48 | 66 |
| 921,600 | 72 | 187 | 155 | 306 |
| 3,686,400 | 292 | 816 | 729 | 1426 |
| 14,745,600 | 1,425 | 4,196 | 3,028 | 7,016 |
\subsection aabb_tree_perf_mem Memory
When using the polyhedron triangle facet primitive (defined inCGAL/AABB_face_graph_triangle_primitive.h) the AABB tree occupies
approximately 61 bytes per primitive (without constructing the
internal KD-tree). It increases to approximately 150 bytes per
primitive when constructing the internal KD-tree with one reference
point per primitive (the default mode when calling the functionAABB_tree::accelerate_distance_queries()). Note that the polyhedron
facet primitive stores only one facet handle as primitive id
and computes on the fly a 3D triangle from the facet handle stored
internally. When explicitly storing a 3D triangle in the primitive the
tree occupies approximately 140 bytes per primitive instead of 60
(without constructing the internal KD-tree).
The following table provides orders of memory occupancy in MBytes for
an increasing number of triangles. As the internal KD-tree used to
accelerate the distance queries dominates the memory occupancy, we
recommend to specify for large models a lower number of reference
point (evenly distributed) to construct the internal KD-tree through
the function AABB_tree::accelerate_distance_queries() which
takes an iterator range as input.
| Number of triangles | AABB tree (in MBytes) | AABB tree with internal KD-tree (in MBytes) |
|---|---|---|
| 18,400 | 1.10 | 2.76 |
| 102,400 | 6.33 | 14.73 |
| 1,022,400 | 59.56 | 151.31 |
| 1,822,400 | 108.34 | 291.84 |
\subsection aabb_tree_perf_inter Intersections
The following table measures the number of intersection queries per
second on one core on the 14,400 triangle version of the knot mesh model for ray,
line, segment and plane queries. Each ray query is generated by
choosing a random source point within the mesh bounding box and a
random vector. A line or segment query is generated by choosing two
random points inside the bounding box. A plane query is generated by
picking a random point inside the bounding box and a random normal
vector. Note that a plane query generally intersects many triangles of
the input surface mesh. This explains the low performance numbers for
the intersection functions which enumerate all intersections.
| Function | Segment | Ray | Line | Plane |
|---|---|---|---|---|
| AABB_tree::do_intersect() | 513,505 | 788,246 | 773,947 | 1,863,721 |
| AABB_tree::any_intersected_primitive() | 521,651 | 852,929 | 831,919 | 2,125,582 |
| AABB_tree::any_intersection() | 504,836 | 835,164 | 821,369 | 1,730,017 |
| AABB_tree::number_of_intersected_primitives() | 186,102 | 397,069 | 210,052 | 49,941 |
| AABB_tree::all_intersected_primitives() | 178,769 | 397,458 | 192,482 | 49,324 |
| AABB_tree::all_intersections() | 178,536 | 352,541 | 193,700 | 21,745 |
Curve of \cgalFigureRef{figAABB-tree-bench} plots the number of queries per second
(here the AABB_tree::all_intersections() function with random segment
queries) against the number of input triangles for the knot triangle
surface mesh.
\cgalFigureBegin{figAABB-tree-bench,bench.png, knot.png}
Number of queries per second against number of triangles for the knot model with 14K (shown) to 15M triangles. We call the all_intersections() function with segment queries randomly chosen within the bounding box.
\cgalFigureEnd
The following table measures the number of AABB_tree::all_intersections()
queries per second against several kernels. We use the 14,400 triangle
version of the knot mesh model for random segment queries.
| Kernel | Queries/s (all_intersections() with segment queries) |
|---|---|
Simple_cartesian<double> |
201,618 |
Simple_cartesian<float> |
193,414 |
Cartesian<double> |
164,784 |
Cartesian<float> |
159,686 |
Exact_predicates_inexact_constructions_kernel |
119,590 |
\subsection aabb_tree_perf_dist Distances
The surface triangle mesh chosen for benchmarking distances is again
the knot model in six increasing resolutions. In the following table we first measure the tree
construction time (which includes the construction of the internal
KD-tree data structure used to accelerate the distance queries by up
to one order of magnitude in our experiments). We then measure the
number of queries per second for the three types distance queries
(AABB_tree::closest_point(), AABB_tree::squared_distance() andAABB_tree::closest_point_and_primitive()) from point queries randomly chosen
inside the bounding box.
| Nb triangles | Closest_point() | Squared_distance() | Closest_point_and_primitive() |
|---|---|---|---|
| 14,400 | 196,043 | 196,356 | 196,708 |
| 57,600 | 123,388 | 123,844 | 123,567 |
| 230,400 | 54,480 | 53,235 | 54,329 |
| 921,600 | 25,761 | 26,890 | 25,386 |
| 3,686,400 | 13,696 | 13,389 | 13,440 |
| 14,745,600 | 7,158 | 6,913 | 7,010 |
\subsection aabb_two_trees_perf Intersection of Two AABB Trees
We test the intersection of two AABB trees using 3 models: two common models of mesh processing (Iphigenia and Nefertiti), and twisted knot to maximize the number of intersection. For each model,
we test their intersection with a translated version of themselves (Note: We do not exploit the fact that it is the same model, the tree is built twice).
We compare here CGAL::AABB_trees::all_pairs_of_intersecting_primitives() and CGAL::box_intersection_d().
\cgalFigureBegin{figAABB-tree-model,iphigenia.png, nefertiti.png, twisted_knot.png}
The three input models used to illustrate AABB trees intersection performance. Iphigenia (left), Nefertiti (center), Twisted knot (right).
\cgalFigureEnd
| Models | Nb Triangles | Nb Intersections | AABB | Tree (ms) | | | Box d || AABB | Tree Seq. | (ms) | | Box d Seq. |
| ----: | ----: | ----: | ----: | ----: | ----: | ----: | ----: || ----: | ----: | ----: | ----: | ----: |
| | | | Build | Do intersect | All pairs | All pairs + Build | || Build | Do intersect | All pairs | All pairs + Build | |
| Iphigenia × Iphigenia Tr. | 703,512 × 703,512 | 47,677 | 91 | <0.1 | 37 | 128 | 424 || 318 | <0.1 | 169 | 488 | 868 |
| Nefertiti × Nefertiti Tr. | 2,018,232 × 2,018,232 | 14,246 | 261 | <0.1 | 10 | 271 | 1,006 ||1,087 | <0.1 | 46 | 1,134 | 2,348 |
| Twisted knot × Twisted knot Rot. | 600,000 × 600,000 | 240,144 | 154 | <0.1 | 57 | 211 | 850 || 541 | <0.1 | 440 | 981 | 2,993 |
\subsection aabb_tree_perf_summary Summary
The experiments described above are neither exhaustive nor conclusive
as we have chosen one specific case where the input primitives are the
facets of a triangle surface polyhedron. Nevertheless, we now provide
some general observations and advises about how to put the AABB tree
to use with satisfactory performances. While the tree construction
times and memory occupancy do not fluctuate much in our experiments
depending on the input surface triangle mesh, the performance
expressed in number of queries varies greatly depending on a complex
combination of criteria: type of kernel, number of input primitives,
distribution of primitives in space, type of function queried, type of
query and location of query in space.
%Kernel: The type of \cgal kernel turns out to dominate the final
execution times, the maximum performances being obtained with theSimple_cartesiankernel parameterized with the double precision
number type. In applications where the intersection and distance
execution times are crucial it is possible to use this kernel for
the AABB tree in combination with a more robust kernel for the main
data structure.Primitives: Although the number of input primitives plays an obvious
role in the final performance, their distribution in space is at
least equally important in order to obtain a well-balanced AABB
tree. Ideally the primitives must be evenly distributed in space and
the long primitives spanning the bounding box of the tree root node
must be avoided as much as possible. It is often beneficial to split
these long primitives into smaller ones before constructing the
tree, e.g., through recursive longest edge bisection for triangle
surface meshes.Function: The type of function queried plays another important
role. Obviously the exhaustive functions, which list all
intersections, are slower than the ones stopping after the first
intersection. Within each of these functions the ones which call
only intersection tests (AABB_tree::do_intersect(),AABB_tree::number_of_intersected_primitives(),AABB_tree::any_intersected_primitive(),AABB_tree::all_intersected_primitives()) are
faster than the ones which explicitly construct the intersections
(AABB_tree::any_intersection()andAABB_tree::all_intersections()).Query: The type of query (e.g., line, ray, segment or plane used
above) plays another role, strongly correlated with the type of
function (exhaustive or not, and whether or not it constructs the
intersections). When all intersection constructions are needed, the
final execution times highly depend on the complexity of the general
intersection object. For example a plane query generally intersects
a surface triangle mesh into many segments while a segment query
generally intersects a surface triangle mesh into few
points. Finally, the location of the query in space also plays an
obvious role in the performances, especially for the distance
queries. Assuming the internal KD-tree constructed through the
functionAABB_tree::accelerate_distance_queries(), it is preferable
to specify a query point already close to the surface triangle mesh
so that the query traverses only few AABBs of the tree. For a large
number of primitive data (greater than 2M faces in our experiments)
however we noticed that it is not necessary (and sometimes even
slower) to use all reference points when constructing the
KD-tree. In these cases we recommend to specify through the functionAABB_tree::accelerate_distance_queries()fewer reference
points (typically not more than 100K) evenly distributed over the
input primitives.
\section aabb_tree_impl Implementation Details
The AABB tree construction is initialized by computing the AABB of the
whole set of input primitives. All primitives are then sorted along
the longest coordinate axis of this box, and the primitives are
separated into two equal size sets. This procedure is applied
recursively until an AABB contains a single primitive. The tree is
leafless as presented in OPCODE \cgalCite{cgal:t-ocdl-05}. An
intersection query traverses the tree by computing intersection tests
only with respect to the AABBs during traversal, and with respect to
the input primitives at the end of traversal (in the leafs of the
tree).
The reference id is not used internally but simply used by the AABB
tree to refer to the primitive in the results provided to the user. It
follows that, while in most cases each reference id corresponds to a
unique primitive, this is not a requirement of the component. This way
a user may use these reference ids as labels, each of them being
shared by several geometric objects.
A distance query between a query point q and the input primitives is
turned into a ball query centered at q. The ball traverses the
tree while recursively querying intersections with the AABBs, and
computes the closest point p from the query point to the input
primitives at the leafs of the tree. The ball radius is then shrunk to
the distance between p and q for all remaining recursive
traversals of the tree. Efficiency is achieved through setting the
initial ball radius to a small value still guaranteed to intersect the
input primitives. This is achieved by constructing an internal secondary data
structure which provides a good hint to the algorithm at the beginning
of the traversal (done by default).
Calling AABB_tree::do_not_accelerate_distance_queries() will disable
the construction and the usage of this internal secondary data structure.
\section aabb_tree_history Design and Implementation History
Camille Wormser and Pierre Alliez started working on a data structure
for efficient collision detection in 2007. The generic design for
implementing both intersection and distance queries, and for generic
queries and primitives was developed by Camille Wormser. In 2009,
Pierre Alliez, Stéphane Tayeb and Camille Wormser made the
implementation CGAL-compliant, with the help of Laurent Rineau for
optimizing the tree construction. Additionally, Andreas
Fabri, Jane Tournois, Mariette Yvinec and Sylvain Lefèbvre are
given thanks for helpful comments and discussions during that period.
Later, Sébastien Loriot contributed several improvements:
thread-safe queries, introduction of shared data stored in the traits
for lighter primitive types, ...
In 2024, the package was made compatible with 2D and 3D primitives by
Andreas Fabri, Sébastien Loriot, and Sven Oesau.
In 2026, the package was extended to support the intersection of two AABB trees.
This work also introduced performance optimizations and parallelization, contributed by Leo Valque.
*/
}
AABB Tree Doc AABB Tree Examples (`AABB_tree/doc/AABB_tree/examples.txt`)
/*!
\example AABB_tree/AABB_custom_example.cpp
\example AABB_tree/AABB_custom_indexed_triangle_set_array_example.cpp
\example AABB_tree/AABB_custom_indexed_triangle_set_example.cpp
\example AABB_tree/AABB_custom_triangle_soup_example.cpp
\example AABB_tree/AABB_insertion_example.cpp
\example AABB_tree/AABB_polyhedron_edge_example.cpp
\example AABB_tree/AABB_polyhedron_facet_distance_example.cpp
\example AABB_tree/AABB_polyhedron_facet_intersection_example.cpp
\example AABB_tree/AABB_polyline_segment_2_example.cpp
\example AABB_tree/AABB_segment_3_example.cpp
\example AABB_tree/AABB_segment_2_example.cpp
\example AABB_tree/AABB_ray_shooting_example.cpp
\example AABB_tree/AABB_indexed_triangle_2_example.cpp
\example AABB_tree/AABB_triangle_2_example.cpp
\example AABB_tree/AABB_triangle_3_example.cpp
\example AABB_tree/AABB_halfedge_graph_edge_example.cpp
\example AABB_tree/AABB_face_graph_triangle_example.cpp
\example AABB_tree/AABB_intersection_example.cpp
*/
Advancing Front Surface Reconstruction Doc Advancing Front Surface Reconstruction Advancing Front Surface Reconstruction (`Advancing_front_surface_reconstruction/doc/Advancing_front_surface_reconstruction/Advancing_front_surface_reconstruction.txt`)
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_Advancing_Front_Surface_Reconstruction
\anchor I1ChapterAdvancingFrontSurfaceReconstruction
\cgalAutoToc
\author Tran Kai Frank Da and David Cohen-Steiner
Surface reconstruction from an unstructured point cloud amounts to
generate a plausible surface that approximates well the input
points. This problem is ill-posed as many surfaces can be generated. A
wide range of approaches have been proposed to tackle this
problem. Among them are variational methods
\cgalCite{s-lsm-96}\cgalCite{zomk-insru-00}, tensor voting
\cgalCite{cgal:ml-cfsg-00}, implicit surface
\cgalCite{hddhjmss-pssr-94}\cgalCite{bc-ssrnn-00}, and Delaunay
triangulations.
For Delaunay based algorithms the output surface is commonly generated
as the union of some triangles selected in the 3D Delaunay
triangulation of the input points. Such algorithms are either
volume-based by generating as output the boundary of selected
tetrahedra \cgalCite{abe-cbscc-97}\cgalCite{ack-pcubm-01},
or surface-based by selecting a set of triangles.
In most surface based Delaunay algorithms the triangles are
selected independently, that is in parallel \cgalCite{agj-lcsr-00}\cgalCite{ab-srvf-98}.
This chapter presents a surface-based Delaunay surface
reconstruction algorithm that sequentially selects the triangles, that
is it uses previously selected triangles to select a new triangle for
advancing the front. At each advancing step the most plausible
triangle is selected, and such that the triangles selected
generates an orientable manifold triangulated surface.
Two other examples of this greedy approach are the ball pivoting
algorithm and Boyer-Petitjean's algorithm \cgalCite{bmrst-bpasr-99}\cgalCite{pb-rnrps-01}. In both algorithms
a triangulated surface is incrementally grown starting from a seed
triangle. Ball pivoting is fast, but the quality of the reconstruction
depends on user defined parameters corresponding to the sampling
density. The Boyer-Petitjean approach can handle non-uniform sampling,
but fails when near co-circular points are encountered, and it does
not provide any guarantee on the topology of the surface.
We describe next the algorithm and provide examples.
\note A \ref tuto_reconstruction "detailed tutorial on surface reconstruction"
is provided with a guide to choose the most appropriate method along
with pre- and postprocessing.
\section AFSR_Definitions Definitions and the Algorithm
A detailed description of the algorithm and the underlying theory are provided
in \cgalCite{cgal:csd-gdbsra-04}.
The first step of the algorithm is the construction of a 3D Delaunay
triangulation of the point set.
The radius of a triangle \f$ t \f$ is the radius of the smallest sphere
passing through the vertices of \f$ t\f$ and enclosing no sample
point. In other words, the radius \f$ r_t\f$ is the distance from any
vertex of \f$ t\f$ to the Voronoi edge dual to \f$ t\f$. This triangle with
three boundary edges is the initial triangulated surface, and its
boundary is the advancing front.
The Delaunay triangle with the smallest radius is the starting point
for the greedy algorithm.
The algorithm maintains a priority queue of candidate triangles, that
is of valid triangles incident to the boundary edges of the current
surface. The priority is the plausibility. While the priority queue is
not empty, the algorithm pops from the queue the most plausible
candidate triangle and adds it to the surface. New candidate triangles
are pushed to the priority queue when new boundary edges appear on the
advancing front. As the algorithm creates a two-manifold surface some
candidate triangles can not be selected due to topological constraints
which are explained next.
\subsection AFSR_Topology Topological Constraints
Any triangle \f$t\f$ considered as the next potential candidate shares an
edge \f$e\f$ with the front of the current reconstruction. Let \f$b\f$
be the vertex of \f$t\f$ opposite to \f$e\f$. There are four
configurations where \f$t\f$ is added to the surface.
- extension, if \f$b\f$ is not yet on the surface.
- hole filling, if \f$b\f$ is on the front and both neighbors of \f$b\f$ on the front are on edge \f$e\f$.
- ear filling, if \f$b\f$ is on the front and one neighbor of \f$b\f$ on the front is on edge \f$e\f$.
- glueing, if \f$b\f$ is on the front and no neighbor of \f$b\f$ on the front is on edge \f$e\f$.
\cgalFigureBegin{figAFSRvalid,valid.png}
Valid candidates.
\cgalFigureEnd
While the first three operations never induce a non-manifold edge or vertex,
we only can perform gluing, if triangle \f$t\f$ has a twin facet, that is a
triangle with an edge on the front and incident to \f$b\f$, and the
third vertex on edge \f$e\f$.
A triangle is said valid when the above operations can be applied.
\subsection AFSR_Selection Plausibility of a Candidate Triangle
Valid triangles for an edge on the front are compared through their
radius. While the radius is a good criterion in the case of 2D smooth
curve reconstruction \cgalCite{b-cccda-94}, we need another criterion
for 3D surface reconstruction, namely the dihedral angle between
triangles on the surface, that is the angle between the normals of the
triangles. There are three bounds namely \f$ \alpha_\mathrm{sliver} \f$,
\f$ \beta \f$, and \f$ \delta \f$.
The candidate triangle of an edge \f$ e \f$ is the triangle
with the smallest radius:
- that is valid for \f$ e \f$, and
- that has \f$ \beta_t < \alpha_\mathrm{sliver} \f$, and
- that has its internal angles with \f$ e \f$ smaller than \f$ \delta \f$.
There may be no such triangle. In the implementation
of the algorithm \f$ \alpha_\mathrm{sliver} \f$ and \f$ \delta\f$ are equal
to \f$ 5\pi/6 \f$.
We denote by \f$ \beta_t\f$ the angle between the normal of a triangle
\f$ t\f$ incident on a boundary edge \f$ e \f$ and the normal of the
triangle on the surface incident to \f$ e \f$.
We define the plausibility grade \f$ p(t) \f$ as \f$ 1/r_t \f$, if
\f$ \beta_t < \beta \f$, and \f$ -\beta_t \f$ else. The parameter \f$
\beta \f$ can be specified by the user and is set by default to \f$ \pi/6\f$.
Let's have a look at the figure below.
\cgalFigureBegin{figAFSRplausible,wedges.png}
Plausibility. Triangle t' and incident triangles sharing edge e seen from the side.
\cgalFigureEnd
\f$ \alpha_\mathrm{sliver}\f$ corresponds to the red wedge. The algorithm will never select triangle t1
even if it is the only candidate triangle.
\f$\beta\f$ corresponds to the green wedge. If there is a candidate triangle in this zone,
the one with the smallest radius is the most plausible.
If there is no candidate triangle in the green wedge, the triangle with the smallest
angle between its normal and the normal of t' is chosen. In the figure above
this would be triangle t4.
\subsection AFSR_Boundaries Dealing with Multiple Components, Boundaries and Sharp Edges
By construction the output of the algorithm is a connected orientable
manifold with or without boundary. To cope with multiple components we
merely look for a new seed facet among facets disjoint from the
surface. In case of noisy data or outliers, the user must filter out
small surface components.
It is impossible to handle all kinds of boundaries and non uniform sampling
at the same time, as a void can either be an undersampled area of the surface,
or a hole.
As we do not want the algorithm to rely on a uniformity condition on
the sampling it will fill holes cut off from "flat" regions of the
surface. However, in many cases a boundary component cannot be closed
by adding a spanning disk such that the resulting disk is well
sampled. Typically, closing a boundary component due to a transversal
clipping of the operation, would yield large dihedral angles at
boundary edges. Moreover, if the boundary is sufficiently well
sampled, the radii of the two triangles incident on a boundary edge
would be very different. These heuristic facts can be used for
boundary detection.
More specifically, we discard any candidate triangle \f$ t \f$, for an edge \f$ e \f$
such that \f$ p(t) < 0\f$, and \f$ r_t > \mathrm{radius_ratio_bound} \times r_{t'}\f$ where \f$ t'\f$ is
the triangle on the surface incident on \f$ e \f$. The parameter \f$\mathrm{radius_ratio_bound}\f$
is specified by the user and is set by default to 5.
For the example given in \cgalFigureRef{figAFSRplausible}, we said that if there
was no triangle t3 in the green wedge, triangle t4 would be chosen as it has
the smallest angle between its normal and the normal of triangle t'.
However, in case its radius was \f$\mathrm{radius_ratio_bound}\f$ times larger than the radius of triangle t',
triangle t2 would be chosen, assuming that its radius is not \f$\mathrm{radius_ratio_bound}\f$ times larger.
Note that this heuristic implies that
where the sampling is too sparse with respect to curvature, it must
be sufficiently uniform for our algorithm to work.
\section AFSR_Examples Examples
The first of the following three examples presents a free function for doing surface
reconstruction. For a sequence of points the function produces a sequence
of triplets of indices describing the triangles of the surface.
The second example presents a class that enables to traverse the
surface represented in a 2D triangulation data structure where
the faces are connected with the facets of underlying 3D Delaunay triangulation.
The third example shows how to get outliers and the boundaries of
the surface. The last example shows how to combine this algorithm with
two \cgal algorithms in order to reconstruct surfaces with sharp
features.
\subsection AFSR_Example_function Examples for Global Function
The global function advancing_front_surface_reconstruction()
takes an iterator range of points as input and writes for each face of the
reconstructed surface a triplet of point indices into an output iterator.
The following example writes the output triangulated surface to std::cout
in accordance to the OFF format.
The function has an overload with an additional argument that allows
to choose how to prioritize facets. It can be written in a way to
avoid the generation of triangles with a perimeter larger than a given
bound.
\cgalExample{Advancing_front_surface_reconstruction/reconstruction_fct.cpp}
While the first example just writes index triples, the second example
uses as output iterator a wrapper around a reference to a Surface_mesh
and calls the function add_face().
\cgalExample{Advancing_front_surface_reconstruction/reconstruction_surface_mesh.cpp}
\subsection AFSR_Example_class Example for the Reconstruction Class
The class Advancing_front_surface_reconstruction provides
access to a 2D triangulation data structure describing the output surface.
The latter can be explored by hopping from a face to its neighboring faces,
and by hopping from faces of the 2D triangulation data structure to
corresponding facets of the underlying 3D Delaunay triangulation.
The type of the 2D triangulation data structure describing the
reconstructed surface is the nested type
\link Advancing_front_surface_reconstruction::Triangulation_data_structure_2 Advancing_front_surface_reconstruction::Triangulation_data_structure_2\endlink.
The type Advancing_front_surface_reconstruction::Triangulation_data_structure_2::Vertex is model of the
concept TriangulationDataStructure_2::Vertex and has additionally
the method vertex_3() that returns an Advancing_front_surface_reconstruction::Vertex_handle to the
associated 3D vertex.
The type Advancing_front_surface_reconstruction::Triangulation_data_structure_2::Face is model of the conceptTriangulationDataStructure_2::Face and has additionally the methodfacet() that returns the associated Advancing_front_surface_reconstruction::Facet,
and a method is_on_surface() for testing if a face is part of the reconstructed
surface.
In case the surface
has boundaries, the 2D surface has one vertex which is associated to
the infinite vertex of the 3D triangulation.
The underlying 3D Delaunay triangulation can be accessed as well,
using the API of the class Delaunay_triangulation_3.
The following example writes the surface to std::cout in accordance
to the STL (Stereo Lithography) format.
\cgalExample{Advancing_front_surface_reconstruction/reconstruction_class.cpp}
\subsection AFSR_Example_boundaries Example for Outliers and Boundaries
Input points which are not on
a surface are outliers. The member function \link Advancing_front_surface_reconstruction::outliers() outliers()\endlink
returns an iterator range of those points.
Boundary edges can be traversed with the member function \link Advancing_front_surface_reconstruction::boundaries() boundaries()\endlink
It returns an iterator range type \link Advancing_front_surface_reconstruction::Boundary_range Boundary_range\endlink whose iterators have the value type
\link Advancing_front_surface_reconstruction::Vertex_on_boundary_range Vertex_on_boundary_range\endlink. This is again an iterator range whose iterators have the value type
\link Advancing_front_surface_reconstruction::Vertex_handle Vertex_handle\endlink.
\cgalExample{Advancing_front_surface_reconstruction/boundaries.cpp}
\subsection AFSR_Example_sharp_features Example for Sharp Features
The priority queue used by the advancing front surface reconstruction
algorithm can be modified to achieve robustness to sharp edges and
provide piecewise-planar or hybrid reconstruction as described in
\cgalCite{cgal:la-srpss-13}. Two other algorithms available in \cgal
must be applied first as a preprocessing to the point set:
\ref Chapter_Shape_Detection "Shape detection": detect planar sections of the input.
\ref Point_set_processing_3Structuring "Point set structuring":
resample the planar sections and edges and corners detected among
them.
\cgalFigureBegin{figAFSRstruct,structured_example.png}
Comparison of advancing front output.
Top: input point set and raw advancing front reconstruction
Bottom: structured point set and advancing front reconstruction with
structure-dependent priority functor
\cgalFigureEnd
The quality of the reconstruction can be significantly improved thanks
to point set structuring when dealing with shapes with sharp features,
as shown on the previous figure. The following example shows how to
define a priority functor that favors structurally coherent facets and
makes the advancing front algorithm robust to sharp features.
\cgalExample{Advancing_front_surface_reconstruction/reconstruction_structured.cpp}
/
} / namespace CGAL */
Advancing Front Surface Reconstruction Doc Advancing Front Surface Reconstruction PackageDescription (`Advancing_front_surface_reconstruction/doc/Advancing_front_surface_reconstruction/PackageDescription.txt`)
/// \defgroup PkgAdvancingFrontSurfaceReconstructionRef Reference Manual
/// \defgroup PkgAdvancingFrontSurfaceReconstructionRefConcepts Concepts
/// \ingroup PkgAdvancingFrontSurfaceReconstructionRef
/*!
\addtogroup PkgAdvancingFrontSurfaceReconstructionRef
\cgalPkgDescriptionBegin{Advancing Front Surface Reconstruction,PkgAdvancingFrontSurfaceReconstruction}
\cgalPkgPicture{afsr-detail.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Tran Kai Frank Da and David Cohen-Steiner}
\cgalPkgDesc{This package provides a greedy algorithm for surface reconstruction from an
unorganized point set. Starting from a seed facet, a piecewise linear
surface is grown by adding Delaunay triangles one by one. The most
plausible triangles are added first, in a way that avoids the appearance
of topological singularities. }
\cgalPkgManuals{Chapter_Advancing_Front_Surface_Reconstruction,PkgAdvancingFrontSurfaceReconstructionRef}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{4.7}
\cgalPkgDependsOn{\ref PkgTriangulation3}
\cgalPkgBib{cgal:dc-afsr}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
\cgalCRPSection{Concepts}
AdvancingFrontSurfaceReconstructionTraits_3
\cgalCRPSection{Classes}
CGAL::Advancing_front_surface_reconstructionCGAL::Advancing_front_surface_reconstruction_vertex_base_3CGAL::Advancing_front_surface_reconstruction_cell_base_3
\cgalCRPSection{Functions}
CGAL::advancing_front_surface_reconstruction()
*/
Advancing Front Surface Reconstruction Doc Advancing Front Surface Reconstruction Examples (`Advancing_front_surface_reconstruction/doc/Advancing_front_surface_reconstruction/examples.txt`)
/*!
\example Advancing_front_surface_reconstruction/reconstruction_fct.cpp
\example Advancing_front_surface_reconstruction/reconstruction_class.cpp
\example Advancing_front_surface_reconstruction/reconstruction_surface_mesh.cpp
\example Advancing_front_surface_reconstruction/boundaries.cpp
\example Advancing_front_surface_reconstruction/reconstruction_structured.cpp
*/
Algebraic Foundations Doc Algebraic Foundations Algebraic Foundations (`Algebraic_foundations/doc/Algebraic_foundations/Algebraic_foundations.txt`)
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_Algebraic_Foundations
\cgalAutoToc
\author Michael Hemmer
\section Algebraic_foundationsIntroduction Introduction
\cgal is targeting towards exact computation with non-linear objects,
in particular objects defined on algebraic curves and surfaces.
As a consequence types representing polynomials, algebraic extensions and
finite fields play a more important role in related implementations.
This package has been introduced to stay abreast of these changes.
Since in particular polynomials must be supported by the introduced framework
the package avoids the term number type. Instead, the package distinguishes
between the algebraic structure of a type and whether a type is embeddable on
the real axis, or real embeddable for short.
Moreover, the package introduces the notion of interoperable types which
allows an explicit handling of mixed operations.
\section Algebraic_foundationsAlgebraic Algebraic Structures
The algebraic structure concepts introduced within this section are
motivated by their well-known counterparts in traditional algebra,
but we also had to pay tribute to existing types and their restrictions.
To keep the interface minimal,
it was not desirable to cover all known algebraic structures,
e.g., we did not introduce concepts for such basic structures as groups or
exceptional structures as skew fields.
\cgalFigureBegin{figConceptHierarchyOfAlgebraicStructures,AlgebraicConceptHierarchy.png}
Concept Hierarchy of Algebraic Structures
\cgalFigureEnd
\cgalFigureRef{figConceptHierarchyOfAlgebraicStructures} shows the refinement
relationship of the algebraic structure concepts.IntegralDomain, UniqueFactorizationDomain, EuclideanRing andField correspond to the algebraic structures with the
same name. FieldWithSqrt, FieldWithKthRoot andFieldWithRootOf are fields that in addition are closed under
the operations 'sqrt', 'k-th root' and 'real root of a polynomial',
respectively. The concept IntegralDomainWithoutDivision also
corresponds to integral domains in the algebraic sense, the
distinction results from the fact that some implementations of
integral domains lack the (algebraically always well-defined) integral
division.
Note that Field refines IntegralDomain. This is because
most ring-theoretic notions like greatest common divisors become trivial forFields. Hence, we see Field as a refinement ofIntegralDomain and not as a
refinement of one of the more advanced ring concepts.
If an algorithm wants to rely on gcd or remainder computation, it is trying
to do things it should not do with a Field in the first place.
The main properties of an algebraic structure are collected in the classAlgebraic_structure_traits.
In particular the (most refined) concept each concrete model AS
fulfills is encoded in the tag
\link AlgebraicStructureTraits::Algebraic_category Algebraic_structure_traits<AS>::Algebraic_category \endlink .
An algebraic structure is at least Assignable,CopyConstructible, DefaultConstructible andEqualityComparable. Moreover, we require that it is
constructible from int.
For ease of use and since their semantic is sufficiently standard to presume
their existence, the usual arithmetic and comparison operators are required
to be realized via \cpp operator overloading.
The division operator is reserved for division in fields.
All other unary (e.g., sqrt) and binary functions
(e.g., gcd, div) must be models of the well-known \stl-conceptsAdaptableUnaryFunction or AdaptableBinaryFunction
concept and local to the traits class
(e.g., \link AlgebraicStructureTraits::Sqrt Algebraic_structure_traits<AS>::Sqrt()(x) \endlink).
This design allows us to profit from all parts in the
\stl and its programming style and avoids the name-lookup and
two-pass template compilation problems experienced with the old design
using overloaded functions. However, for ease of use and backward
compatibility all functionality is also
accessible through global functions defined within namespace CGAL,
e.g., \link sqrt CGAL::sqrt(x) \endlink. This is realized via function templates using
the according functor of the traits class. For an overview see the section "Global Functions" in the
\ref PkgAlgebraicFoundationsRef.
\subsection Algebraic_foundationsTagsinAlgebraicStructure Tags in Algebraic Structure Traits
\subsubsection Algebraic_foundationsAlgebraicCategory Algebraic Category
For a type AS, Algebraic_structure_traits<AS>
provides several tags. The most important tag is the Algebraic_category
tag, which indicates the most refined algebraic concept the type AS
fulfills. The tag is one of;Integral_domain_without_division_tag, Integral_domain_tag,Field_tag, Field_with_sqrt_tag, Field_with_kth_root_tag,Field_with_root_of_tag, Unique_factorization_domain_tag,Euclidean_ring_tag, or even Null_tag
in case the type is not a model of an algebraic structure concept.
The tags are derived from each other such that they reflect the
hierarchy of the algebraic structure concept, e.g.,Field_with_sqrt_tag is derived from Field_tag.
\subsubsection Algebraic_foundationsExactandNumericalSensitive Exact and Numerical Sensitive
Moreover, Algebraic_structure_traits<AS> provides the tags Is_exact
and Is_numerical_sensitive, which are both Boolean_tags.
An algebraic structure is considered exact,
if all operations required by its concept are computed such that a comparison
of two algebraic expressions is always correct.
An algebraic structure is considered as numerically sensitive,
if the performance of the type is sensitive to the condition number of an
algorithm.
Note that there is really a difference among these two notions,
e.g., the fundamental type int is not numerical sensitive but
considered inexact due to overflow.
Conversely, types as leda_real or CORE::Expr are exact but sensitive
to numerical issues due to the internal use of multi precision floating point
arithmetic. We expect that Is_numerical_sensitive is used for dispatching
of algorithms, while Is_exact is useful to enable assertions that can be
checked for exact types only.
Tags are very useful to dispatch between alternative implementations.
The following example illustrates a dispatch for Fields using overloaded
functions. The example only needs two overloads since the algebraic
category tags reflect the algebraic structure hierarchy.
\cgalExample{Algebraic_foundations/algebraic_structure_dispatch.cpp}
\section Algebraic_foundationsRealE Real Embeddable
\anchor secRealEmbeddable
Most number types represent some subset of the real numbers. From those types
we expect functionality to compute the sign, absolute value or double
approximations. In particular, we can expect an order on such a type that
reflects the order along the real axis.
All these properties are gathered in the concept ::RealEmbeddable.
The concept is orthogonal to the algebraic structure concepts,
i.e., it is possible
that a type is a model of RealEmbeddable only,
since the type may just represent values on the real axis
but does not provide any arithmetic operations.
As for algebraic structures this concept is also traits class oriented.
The main functionality related to RealEmbeddable is gathered in
the class Real_embeddable_traits. In particular, it provides the boolean
tag Is_real_embeddable indicating whether a type is a model ofRealEmbeddable. The comparison operators are required to be realized via
\cpp operator overloading.
All unary functions (e.g. sign, to_double) and
binary functions (e.g. compare ) are models of the \stl-conceptsAdaptableUnaryFunction and AdaptableBinaryFunction and are local
to Real_embeddable_traits.
In case a type is a model of IntegralDomainWithoutDivision andRealEmbeddable the number represented by an object of this type is
the same for arithmetic and comparison.
It follows that the ring represented by this type is a superset of the integers
and a subset of the real numbers and hence has characteristic zero.
In case the type is a model of Field and RealEmbeddable it is a
superset of the rational numbers.
\section Algebraic_foundationsRealN Real Number Types
Every \cgal Kernel comes with two real number types
(number types embeddable into the real numbers). One of them is aFieldNumberType, and the other a RingNumberType. The
coordinates of the basic kernel objects (points, vectors, etc.) come
from one of these types (the FieldNumberType in case of %Cartesian
kernels, and the RingNumberType for %Homogeneous kernels).
The concept FieldNumberType combines the requirements of the
concepts Field and RealEmbeddable, whileRingNumberType combines IntegralDomainWithoutDivision andRealEmbeddable. Algebraically, the real number types do not form
distinct structures and are therefore not listed in the concept
hierarchy of \cgalFigureRef{figConceptHierarchyOfAlgebraicStructures}.
\section Algebraic_foundationsInteroperability Interoperability
This section introduces two concepts for interoperability of types,
namely ImplicitInteroperable and ExplicitInteroperable. WhileExplicitInteroperable is the base concept, we start withImplicitInteroperable since it is the more intuitive one.
In general mixed operations are provided by overloaded operators and
functions or just via implicit constructor calls.
This level of interoperability is reflected by the conceptImplicitInteroperable. However, within template code the result type,
or so-called coercion type, of a mixed arithmetic operation may be unclear.
Therefore, the package introduces Coercion_traits
giving access to the coercion type via \link Coercion_traits::Type Coercion_traits<A,B>::Type \endlink
for two interoperable types A and B.
Some trivial examples are int and double with coercion type double
or Gmpz and Gmpq with coercion type Gmpq.
However, the coercion type is not necessarily one of the input types,
e.g. the coercion type of a polynomial
with integer coefficients that is multiplied by a rational type
is supposed to be a polynomial with rational coefficients.
Coercion_traits is also
required to provide a functor \link Coercion_traits::Cast Coercion_traits<A,B>::Cast() \endlink, that
converts from an input type into the coercion type. This is in fact the core
of the more basic concept ExplicitInteroperable.ExplicitInteroperable has been introduced to cover more complex cases
for which it is hard or impossible to guarantee implicit interoperability.
Note that this functor can be useful for ImplicitInteroperable types
as well, since it can be used to void redundant type conversions.
In case two types A and B are ExplicitInteroperable with
coercion type C they are valid argument types for all binary functors
provided by Algebraic_structure_traits and Real_embeddable_traits ofC. This is also true for the according global functions.
\subsection Algebraic_foundationsExamples Examples
The following example illustrates how two write code forExplicitInteroperable types.
\cgalExample{Algebraic_foundations/interoperable.cpp}
The following example illustrates a dispatch for ImplicitInteroperable andExplicitInteroperable types.
The binary function (that just multiplies its two arguments) is supposed to
take two ExplicitInteroperable arguments. For ImplicitInteroperable
types a variant that avoids the explicit cast is selected.
\cgalExample{Algebraic_foundations/implicit_interoperable_dispatch.cpp}
\section Algebraic_foundationsFractions Fractions
Beyond the need for performing algebraic operations on objects as a
whole, there are also number types which one would like to decompose into
numerator and denominator. This does not only hold for rational numbers
as Quotient, Gmpq, mpq_class or leda_rational, but
also for compound objects as Sqrt_extension or Polynomial
which may decompose into a (scalar)
denominator and a compound numerator with a simpler coefficient type
(e.g. integer instead of rational). Often operations can be performed faster on
these denominator-free multiples. In case a type is a Fraction
the relevant functionality as well as the numerator and denominator
type are provided by Fraction_traits. In particularFraction_traits provides a tag \link FractionTraits::Is_fraction Is_fraction \endlink that can be
used for dispatching.
A related class is Rational_traits which has been kept for backward
compatibility reasons. However, we recommend to use Fraction_traits since
it is more general and offers dispatching functionality.
\subsection Algebraic_foundationsExamples_1 Examples
The following example show a simple use of Fraction_traits:
\cgalExample{Algebraic_foundations/fraction_traits.cpp}
The following example illustrates the integralization of a vector,
i.e., the coefficient vector of a polynomial. Note that for minimizing
coefficient growth \link FractionTraits::Common_factor Fraction_traits<Type>::Common_factor \endlink is used to
compute the least common multiple of the denominators.
\cgalExample{Algebraic_foundations/integralize.cpp}
\section Algebraic_foundationsDesign Design and Implementation History
The package is part of \cgal since release 3.3. Of course the package is based
on the former Number type support of CGAL. This goes back to Stefan Schirra and Andreas Fabri. But on the other hand the package is to a large extend influenced
by the experience with the number type support in \exacus \cgalCite{beh-eeeafcs-05},
which in the main goes back to
Lutz Kettner, Susan Hert, Arno Eigenwillig, and Michael Hemmer.
However, the package abstracts from the pure support for
number types that are embedded on the real axis which allows the support of
polynomials, finite fields, and algebraic extensions as well. See also related
subsequent chapters.
/
} / namespace CGAL */
Algebraic Foundations Doc Algebraic Foundations PackageDescription (`Algebraic_foundations/doc/Algebraic_foundations/PackageDescription.txt`)
/// \defgroup PkgAlgebraicFoundationsRef Reference Manual
/// \defgroup PkgAlgebraicFoundationsAlgebraicStructuresConcepts Concepts
/// \ingroup PkgAlgebraicFoundationsRef
/*!
\addtogroup PkgAlgebraicFoundationsRef
\cgalPkgDescriptionBegin{Algebraic Foundations,PkgAlgebraicFoundations}
\cgalPkgPicture{Algebraic_foundations2.png}
\cgalPkgSummaryBegin
\cgalPkgAuthor{Michael Hemmer}
\cgalPkgDesc{This package defines what algebra means for \cgal, in terms of concepts, classes and functions. The main features are: (i) explicit concepts for interoperability of types (ii) separation between algebraic types (not necessarily embeddable into the reals), and number types (embeddable into the reals).}
\cgalPkgManuals{Chapter_Algebraic_Foundations,PkgAlgebraicFoundationsRef}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{3.3}
\cgalPkgBib{cgal:h-af}
\cgalPkgLicense{\ref licensesLGPL "LGPL"}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
\cgalCRPSection{Algebraic Structures}
\cgalCRPSubsection{Concepts}
IntegralDomainWithoutDivisionIntegralDomainUniqueFactorizationDomainEuclideanRingFieldFieldWithSqrtFieldWithKthRootFieldWithRootOfAlgebraicStructureTraitsAlgebraicStructureTraits_::IsZeroAlgebraicStructureTraits_::IsOneAlgebraicStructureTraits_::SquareAlgebraicStructureTraits_::SimplifyAlgebraicStructureTraits_::UnitPartAlgebraicStructureTraits_::IntegralDivisionAlgebraicStructureTraits_::DividesAlgebraicStructureTraits_::GcdAlgebraicStructureTraits_::DivModAlgebraicStructureTraits_::DivAlgebraicStructureTraits_::ModAlgebraicStructureTraits_::InverseAlgebraicStructureTraits_::SqrtAlgebraicStructureTraits_::IsSquareAlgebraicStructureTraits_::KthRootAlgebraicStructureTraits_::RootOf
\cgalCRPSubsection{Classes}
CGAL::Algebraic_structure_traits<T>CGAL::Integral_domain_without_division_tagCGAL::Integral_domain_tagCGAL::Field_tagCGAL::Field_with_sqrt_tagCGAL::Unique_factorization_domain_tagCGAL::Euclidean_ring_tag
\cgalCRPSubsection{Global Functions}
CGAL::is_zero()CGAL::is_one()CGAL::square()CGAL::simplify()CGAL::unit_part()CGAL::integral_division()CGAL::is_square()CGAL::gcd()CGAL::div_mod()CGAL::div()CGAL::mod()CGAL::inverse()CGAL::sqrt()CGAL::kth_root()CGAL::root_of()
\cgalCRPSection{Real Embeddable}
\cgalCRPSubsection{Concepts}
RealEmbeddableRealEmbeddableTraitsRealEmbeddableTraits_::IsZeroRealEmbeddableTraits_::AbsRealEmbeddableTraits_::SgnRealEmbeddableTraits_::IsPositiveRealEmbeddableTraits_::IsNegativeRealEmbeddableTraits_::CompareRealEmbeddableTraits_::ToDoubleRealEmbeddableTraits_::ToInterval
\cgalCRPSubsection{Classes}
CGAL::Real_embeddable_traits<T>
\cgalCRPSubsection{Global Functions}
CGAL::is_zero()CGAL::abs()CGAL::sign()CGAL::is_positive()CGAL::is_negative()CGAL::compare()CGAL::to_double()CGAL::to_interval()
\cgalCRPSection{Real Number Types}
\cgalCRPSubsection{Concepts}
RingNumberTypeFieldNumberType
\cgalCRPSection{Interoperability}
\cgalCRPSubsection{Concepts}
ExplicitInteroperableImplicitInteroperable
\cgalCRPSubsection{Classes}
CGAL::Coercion_traits<A,B>
\cgalCRPSection{Fractions}
\cgalCRPSubsection{Concepts}
FractionFractionTraitsFractionTraits_::DecomposeFractionTraits_::ComposeFractionTraits_::CommonFactor
\cgalCRPSubsection{Classes}
CGAL::Fraction_traits<T>
\cgalCRPSection{Miscellaneous}
\cgalCRPSubsection{Concepts}
FromIntConstructibleFromDoubleConstructible
*/
Algebraic Foundations Doc Algebraic Foundations Examples (`Algebraic_foundations/doc/Algebraic_foundations/examples.txt`)
/*!
\example Algebraic_foundations/algebraic_structure_dispatch.cpp
\example Algebraic_foundations/fraction_traits.cpp
\example Algebraic_foundations/implicit_interoperable_dispatch.cpp
\example Algebraic_foundations/integralize.cpp
\example Algebraic_foundations/interoperable.cpp
*/
Algebraic Kernel D Doc Algebraic Kernel D Algebraic Kernel D (`Algebraic_kernel_d/doc/Algebraic_kernel_d/Algebraic_kernel_d.txt`)
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_Algebraic_Kernel
\anchor chapteralgebraickerneld
\cgalAutoToc
\authors Eric Berberich, Michael Hemmer, Michael Kerber, Sylvain Lazard, Luis Peñaranda, and Monique Teillaud
\section Algebraic_kernel_dIntroduction Introduction
Real solving of polynomials is a fundamental problem with a wide application range.
This package provides black-box implementations of
algorithms to determine, compare, and approximate real roots of univariate polynomials
and bivariate polynomial systems. Such a black-box is called an Algebraic Kernel.
Since this package is aimed at providing more than one implementation, the interface of
the algebraic kernels is expressed in concepts. The main concepts provided by this package are theAlgebraicKernel_d_1 for univariate polynomial systems and AlgebraicKernel_d_2
for bivariate polynomial systems, the latter being a refinement of the first.
\section Algebraic_kernel_dAlgebraic Algebraic Kernel Concepts
\subsection Algebraic_kernel_dUnivariateAlgebraicKernel Univariate Algebraic Kernel
\subsection Algebraic_kernel_dMajorTypes Major Types
First of all, the univariate algebraic kernel provides construction, comparison and
approximation of real roots of univariate polynomials.
Thus, the major public types the AlgebraicKernel_d_1 provides are:
AlgebraicKernel_d_1::Polynomial_1 - the type representing univariate polynomials,
AlgebraicKernel_d_1::Coefficient - the coefficient type of these polynomials,
AlgebraicKernel_d_1::Algebraic_real_1 - the type representing real roots,
AlgebraicKernel_d_1::Bound - the type which is used to approximate these algebraic reals,
in particular, it is used to represent the boundaries of isolating intervals.
\subsection Algebraic_kernel_dConstructionofAlgebraic Construction of Algebraic Real Numbers
The kernel provides two different function objects to construct anAlgebraicKernel_d_1::Algebraic_real_1. The most general way
is to use AlgebraicKernel_d_1::Isolate_1; The function object
takes a univariate polynomial and writes all real roots into a given
output iterator. It is also possible to retrieve the multiplicity of
each root. The second option is to construct one particular algebraic
real using AlgebraicKernel_d_1::Construct_algebraic_real_1.
This function object provides construction from the native int type, the
coefficient type as well as the bound type. Moreover, it is possible
to construct an algebraic real by giving a polynomial and either an
isolating interval or the index of the root. A related function object
is AlgebraicKernel_d_1::Number_of_solutions_1 computing the number
of real roots of a polynomial.
\subsection Algebraic_kernel_dComparisonandApproximation Comparison and Approximation of Algebraic Real Numbers
An AlgebraicKernel_d_1::Algebraic_real_1 is model ofRealEmbeddable, for instance, it is possible to compare two
algebraic reals, to determine the sign of an algebraic real or to ask
for its double approximation, see also section \ref secRealEmbeddable.
Moreover, AlgebraicKernel_d_1::Compare_1 provides
comparison with int, the coefficient type and the bound type.
There are several ways to approximate an AlgebraicKernel_d_1::Algebraic_real_1:
AlgebraicKernel_d_1::Approximate_absolute_1 - provides an approximation that is
better than the passed absolute error bound,
AlgebraicKernel_d_1::Approximate_relative_1 - provides an approximation that is
better than the passed relative error bound,
AlgebraicKernel_d_1::Isolate_1 - returns an isolating interval with respect to
a given univariate polynomial,
A related function object is AlgebraicKernel_d_1::Bound_between_1, which computes
a number that isolates two algebraic real numbers.
\subsection Algebraic_kernel_dInterplaywithPolynomials Interplay with Polynomials
It is also possible to retrieve a representing polynomial from an
algebraic real using AlgebraicKernel_d_1::Compute_polynomial_1,
which guarantees that the algebraic real is a root of the returned
polynomial. As the name already indicates, this operation may be very
costly since the polynomial may not be computed yet. Moreover, it is
not guaranteed that the returned polynomial is the minimal polynomial
of the number. Together with AlgebraicKernel_d_1::Isolate_1, it
is possible to retrieve the traditional representation of an algebraic
real as a square free polynomial and an isolating interval.
Though the AlgebraicKernel_d_1 does not provide arithmetic
operations on AlgebraicKernel_d_1::Algebraic_real_1, it is
possible to compute the sign of a polynomial at a given algebraic
real using AlgebraicKernel_d_1::Sign_at_1. Or alternatively,
just compute whether the polynomial is zero at an algebraic real number
using AlgebraicKernel_d_1::Is_zero_at_1. Note that this operation
can be significantly less expensive, in particular if the polynomial
is not zero at the given algebraic real.
\subsection Algebraic_kernel_dAuxiliaryFunctionality Auxiliary Functionality for Polynomials
First of all the type AlgebraicKernel_d_1::Polynomial_1 is required
to be a model of the concept Polynomial_1, which is defined
in the Polynomial package (see chapter \ref Chapter_Polynomial).
This implies that all essential functionality is provided viaPolynomial_traits_d. However, the algebraic kernel also
provides several function objects to handle polynomials:
AlgebraicKernel_d_1::Is_square_free_1 - determines whether a polynomial is square free,
AlgebraicKernel_d_1::Make_square_free_1 - computes the square free part of a polynomial,
AlgebraicKernel_d_1::Square_free_factorize_1 - computes a square free factorization of a polynomial,
AlgebraicKernel_d_1::Is_coprime_1 - computes whether a pair of polynomials is square free,
AlgebraicKernel_d_1::Make_coprime_1 - decomposes two polynomials into the coprime factors and their common factor.
Though the polynomial package provides similar functionality we suggest to use
the function objects provided by the kernel, since the design of the algebraic kernel
allows for instance internal caching by the kernel.
Also note that AlgebraicKernel_d_1::Square_free_factorize_1 only computes the square free
factorization up to a constant factor. This is a slight modification with respect to its
counterpart in Polynomial_traits_d. In this way it was possible that the concepts just require
the coefficient type to be a model of IntegralDomain, instead of Field or UniqueFactorizationDomain.
For more details see also:
- \ref PolynomialTraits_d::SquareFreeFactorize
- \ref PolynomialTraits_d::SquareFreeFactorizeUpToConstantFactor
\subsection Algebraic_kernel_dDesignRationale Design Rationale
Most implementations of an AlgebraicKernel_d_1 will represent
an algebraic real number by the root of a square free polynomial and
an isolating interval, that is, the number is defined as the only
root of the polynomial within the interval. Usually, one will refrain
from computing the minimal polynomial since the computation of the
minimal polynomial is much more expensive and does not pay of. However,
besides the representation by a polynomial and an isolating interval
one can also imagine the representation by a polynomial and the index
of the root, e.g., as the \f$ i\f$th real root when enumerated from minus
to plus infinity. Moreover, it may very well be that the kernel just
computes an approximation of the number, whereas the representing
polynomial is not computed yet.
This is in particular relevant in relation to the AlgebraicKernel_d_2,
where AlgebraicKernel_d_1::Algebraic_real_1 is used to represent coordinates of solutions of bivariate systems.
Hence, the design does
not allow direct access to any, seemingly obvious, members of anAlgebraicKernel_d_1::Algebraic_real_1. Instead, there is, e.g.,AlgebraicKernel_d_1::Compute_polynomial_1 which emphasizes
that the requested polynomial may not be computed yet. Similarly,
there is no way to directly ask for the refinement of the current
isolating interval since this would impose a state to every object of
an AlgebraicKernel_d_1::Algebraic_real_1.
\subsection Algebraic_kernel_dBivariateAlgebraicKernel Bivariate Algebraic Kernel
The concept AlgebraicKernel_d_2 is a refinement of AlgebraicKernel_d_1,
that is, a model of AlgebraicKernel_d_2 is also a model of AlgebraicKernel_d_1.
Hence, the AlgebraicKernel_d_2 concept is designed such that occurring
names and functionalities are as similar as possible to those in theAlgebraicKernel_d_1 concept.
The following are a direct generalization of their univariate counterparts:
AlgebraicKernel_d_2::Polynomial_2,
AlgebraicKernel_d_2::Algebraic_real_2,
AlgebraicKernel_d_2::Construct_algebraic_real_2,
AlgebraicKernel_d_2::Isolate_2,
AlgebraicKernel_d_2::Is_square_free_2,
AlgebraicKernel_d_2::Make_square_free_2,
AlgebraicKernel_d_2::Square_free_factorize_2,
AlgebraicKernel_d_2::Is_coprime_2,
AlgebraicKernel_d_2::Make_coprime_2,
AlgebraicKernel_d_2::Solve_2,
AlgebraicKernel_d_2::Number_of_solutions_2,
AlgebraicKernel_d_2::Compare_xy_2,
AlgebraicKernel_d_2::Sign_at_2,
AlgebraicKernel_d_2::Is_zero_at_2.
For instance, AlgebraicKernel_d_2::Solve_2
provides the solution for a bivariate polynomial system.
However, it is also possible to obtain the coordinates of these
solutions with the additional functors:
AlgebraicKernel_d_2::Compute_x_2,
AlgebraicKernel_d_2::Compute_y_2.
In principal this would be sufficient generalization,
since functions such as isolating, approximating algebraic real numbers
could be implemented using these access functions ant
the corresponding functionalities in the univariate algebraic kernel.
However, one should be aware that an AlgebraicKernel_d_2::Algebraic_real_2
is not necessarily represented as a pair of univariate solutions, that is,
using AlgebraicKernel_d_2::Compute_y_2 may entail considerable
computations. Therefore, the concept also requires the following
additional functors that may allow a model to bypass this issue:
AlgebraicKernel_d_2::Compute_polynomial_x_2,
AlgebraicKernel_d_2::Compute_polynomial_y_2,
AlgebraicKernel_d_2::Isolate_x_2,
AlgebraicKernel_d_2::Isolate_y_2,
AlgebraicKernel_d_2::Compare_x_2,
AlgebraicKernel_d_2::Compare_y_2,
AlgebraicKernel_d_2::Approximate_absolute_x_2,
AlgebraicKernel_d_2::Approximate_relative_x_2,
AlgebraicKernel_d_2::Approximate_absolute_y_2,
AlgebraicKernel_d_2::Approximate_relative_y_2,
AlgebraicKernel_d_2::Bound_between_x_2,
AlgebraicKernel_d_2::Bound_between_y_2.
\section Algebraic_kernel_dModels Models
\subsection Algebraic_kernel_dGenericAlgebraicKernels Generic Algebraic Kernels
The package provides generic models of the univariate and bivariate algebraic
kernel, namely Algebraic_kernel_d_1<Coeff> and Algebraic_kernel_d_2<Coeff>,
respectively. Both kernels support a large set of number types as their
template argument, which defines the supported coefficient type. The supported
types are, for instance, Gmpz and Gmpq as well as the corresponding types
of LEDA and CORE.
The Algebraic_kernel_d_1<Coeff> represents an algebraic real root by a square
free polynomial and an isolating interval that uniquely defines the root.
The current method to isolate roots is the Bitstream Descartes
method \cgalCite{eigenwillig-phd-08}.
The used method to refine the approximation of an algebraic real root is a
slightly modified (filtered) version of the one presented in \cgalCite{abbott-qir-06}.
The method has quadratic convergence.
Algebraic_kernel_d_2<Coeff> is based on an algorithm computing a
geometric-topological analysis of a single curve \cgalCite{ekw-fast-07} and of a
pair of curves \cgalCite{ek-exact-08}.
The main idea behind both analyses is to compute the critical
x-coordinates of curves and curve pairs by projection (resultants), and compute
additional information about the critical fibers using subresultants
and Sturm-Habicht sequences \cgalCite{grlr-sturm-habicht-98}.
With that information, the fiber at
critical x-coordinates is computed by a variant of the Bitstream
Descartes method.
See also \cgalCite{kerber-phd-09} for a comprehensive description of
these techniques.
Almost all functors in the class that take a Polynomial_2
object as argument trigger such an analysis as a main computation
step. For efficiency, these analyses (of single curves and curve
pairs) are therefore cached internally for efficiency. For instance,
computing the pairwise solutions of 10 Polynomial_2 objects
requires 10 curve analyses and 45 curve pair analyses to be computed
internally.
A point \f$ p\f$ of type Algebraic_real_2 is represented
by its \f$ x\f$-coordinate \f$ x_0\f$ (as described in the Algebraic_kernel_d_1
paragraph above), an algebraic curve where \f$ p\f$ lies on, and an
integer \f$ i\f$, denoting that \f$ p\f$ is the \f$ i\f$th point in the fiber at \f$ x_0\f$,
counted from the bottom (ignoring a possible vertical line at \f$ x_0\f$).
Note that this determines the point uniquely, but the \f$ y\f$-coordinate
is not stored internally in terms of an Algebraic_real_1 object.
Querying such a representation by calling Compute_y_2 is a
time-consuming step, and should be avoided for efficiency reasons if possible.
\section Algebraic_kernel_dExamples Examples
\subsection CGALAK1ConstructionExamples Construction of Algebraic Real Numbers
The following example illustrates the construction of AlgebraicKernel_d_1::Algebraic_real_1
using AlgebraicKernel_d_1::Construct_algebraic_real_1:
\cgalExample{Algebraic_kernel_d/Construct_algebraic_real_1.cpp}
\subsection CGALAK1Solving Solving Univariate Polynomials
The following example illustrates the construction of AlgebraicKernel_d_1::Algebraic_real_1
using AlgebraicKernel_d_1::Solve_1: \cgalExample{Algebraic_kernel_d/Solve_1.cpp}
\subsection CGALAK1EGCompare_1 Comparison and Approximation of Algebraic Real Numbers
The following example illustrates the comparison of AlgebraicKernel_d_1::Algebraic_real_1 numbers:
\cgalExample{Algebraic_kernel_d/Compare_1.cpp}
\subsection CGALAK1EGIsolate_1 Isolation of Algebraic Real Numbers with respect to roots of other polynomials
The following example illustrates the isolation of AlgebraicKernel_d_1::Algebraic_real_1 numbers:
\cgalExample{Algebraic_kernel_d/Isolate_1.cpp}
\subsection CGALAK1EGSign_at_1 Interplay with Polynomials
The following example illustrates the sign evaluation of AlgebraicKernel_d_1::Algebraic_real_1 numbers in polynomials:
\cgalExample{Algebraic_kernel_d/Sign_at_1.cpp}
\section Algebraic_kernel_dDesign Design and Implementation History
This package is clearly split into a univariate and bivariate
kernel. However, with respect to its history the package splits into
a design part and an implementation part.
The concepts, which make up the design part,
were written by Eric Berberich, Michael Hemmer, and
Monique Teillaud.
The design history of the package is fairly old and several
ideas that influenced this package can already be found
in \cgalCite{cgal:bhkt-risak-07}. Since then, the initial design underwent
considerable changes. For instance, it was decided that the algebraic
numbers should be under the control of the algebraic kernel. On the other
hand the initial support for polynomials was extended to a separate
and independent package that is not restricted to a certain number of
variables. Thus, the authors want to thank for all the useful feedback and
ideas that was brought to them throughout the last years. In particular,
they want to thank Menelaos Karavelas and Elias Tsigaridas for their
initial contributions.
The two generic models where initially developed as part of the \exacus \cgalCite{beh-eeeafcs-05} project.
However, the models are now fully integrated into the \cgal library,
since also the relevant layers of \exacus are now part of \cgal.
The main authors for Algebraic_kernel_d_1<Coeff> and Algebraic_kernel_d_2<Coeff> are
Michael Hemmer and Michael Kerber, respectively. Notwithstanding, the authors also want to emphasize the
contribution of all authors of the \exacus project,
particularly the contribution of Arno Eigenwillig, Sebastian Limbach and Pavel Emeliyanenko.
In 2010, two univariate kernels that interface the library RS \cgalCite{cgal:r-rs} were
written by Luis Peñaranda and Sylvain Lazard \cgalCite{cgal:2009-ESA}.
Both models interface the library RS \cgalCite{cgal:r-rs} by Fabrice Rouillier.
The authors want to thank Fabrice Rouillier and Elias Tsigaridas for
strong support and many useful discussions that lead to the integration of RS.
Due to lack of maintenance, these kernels have been removed in 2024.
/
} / namespace CGAL */
Algebraic Kernel D Doc Algebraic Kernel D PackageDescription (`Algebraic_kernel_d/doc/Algebraic_kernel_d/PackageDescription.txt`)
/// \defgroup PkgAlgebraicKernelDRef Reference Manual
/// \defgroup PkgAlgebraicKernelDConcepts Concepts
/// \ingroup PkgAlgebraicKernelDRef
/// \defgroup PkgAlgebraicKernelDConceptsUni Univariate Algebraic Kernel
/// \ingroup PkgAlgebraicKernelDConcepts
/// \defgroup PkgAlgebraicKernelDConceptsBi Bivariate Algebraic Kernel
/// \ingroup PkgAlgebraicKernelDConcepts
/// \defgroup PkgAlgebraicKernelDModels Models
/// \ingroup PkgAlgebraicKernelDRef
/*!
\addtogroup PkgAlgebraicKernelDRef
\cgalPkgDescriptionBegin{Algebraic Kernel,PkgAlgebraicKernelD}
\cgalPkgPicture{Algebraic_kernel_d.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Eric Berberich, Michael Hemmer, Michael Kerber, Sylvain Lazard, Luis Peñaranda, and Monique Teillaud}
\cgalPkgDesc{Real solving of polynomials is a fundamental problem with a wide application range. This package is targeted to provide black-box implementation algorithms to determine, compare and approximate real roots of univariate polynomials and bivariate polynomial systems. Such a black-box is called an Algebraic %Kernel. So far the package only provides models for the univariate kernel. Nevertheless, it already defines concepts for the bivariate kernel, since this settles the interface for upcoming implementations.}
\cgalPkgManuals{Chapter_Algebraic_Kernel,PkgAlgebraicKernelDRef}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{3.6}
\cgalPkgBib{cgal:bht-ak}
\cgalPkgLicense{\ref licensesLGPL "LGPL"}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
\cgalCRPSection{Concepts}
\cgalCRPSubsection{Univariate Algebraic %Kernel}
AlgebraicKernel_d_1AlgebraicKernel_d_1::ConstructAlgebraicReal_1AlgebraicKernel_d_1::ComputePolynomial_1AlgebraicKernel_d_1::Isolate_1AlgebraicKernel_d_1::IsSquareFree_1AlgebraicKernel_d_1::MakeSquareFree_1AlgebraicKernel_d_1::SquareFreeFactorize_1AlgebraicKernel_d_1::IsCoprime_1AlgebraicKernel_d_1::MakeCoprime_1AlgebraicKernel_d_1::Solve_1AlgebraicKernel_d_1::NumberOfSolutions_1AlgebraicKernel_d_1::SignAt_1AlgebraicKernel_d_1::IsZeroAt_1AlgebraicKernel_d_1::Compare_1AlgebraicKernel_d_1::BoundBetween_1AlgebraicKernel_d_1::ApproximateAbsolute_1AlgebraicKernel_d_1::ApproximateRelative_1
\cgalCRPSubsection{Bivariate Algebraic %Kernel}
AlgebraicKernel_d_2AlgebraicKernel_d_2::ConstructAlgebraicReal_2AlgebraicKernel_d_2::ComputePolynomialX_2AlgebraicKernel_d_2::ComputePolynomialY_2AlgebraicKernel_d_2::Isolate_2AlgebraicKernel_d_2::IsolateX_2AlgebraicKernel_d_2::IsolateY_2AlgebraicKernel_d_2::IsSquareFree_2AlgebraicKernel_d_2::MakeSquareFree_2AlgebraicKernel_d_2::SquareFreeFactorize_2AlgebraicKernel_d_2::IsCoprime_2AlgebraicKernel_d_2::MakeCoprime_2AlgebraicKernel_d_2::Solve_2AlgebraicKernel_d_2::NumberOfSolutions_2AlgebraicKernel_d_2::SignAt_2AlgebraicKernel_d_2::IsZeroAt_2AlgebraicKernel_d_2::ComputeX_2AlgebraicKernel_d_2::ComputeY_2AlgebraicKernel_d_2::CompareX_2AlgebraicKernel_d_2::CompareY_2AlgebraicKernel_d_2::CompareXY_2AlgebraicKernel_d_2::ApproximateAbsoluteX_2AlgebraicKernel_d_2::ApproximateRelativeX_2AlgebraicKernel_d_2::ApproximateAbsoluteY_2AlgebraicKernel_d_2::ApproximateRelativeY_2AlgebraicKernel_d_2::BoundBetweenX_2AlgebraicKernel_d_2::BoundBetweenY_2
\cgalCRPSection{Models}
CGAL::Algebraic_kernel_d_1<Coeff>CGAL::Algebraic_kernel_d_2<Coeff>
*/
Algebraic Kernel D Doc Algebraic Kernel D Examples (`Algebraic_kernel_d/doc/Algebraic_kernel_d/examples.txt`)
/*!
\example Algebraic_kernel_d/Construct_algebraic_real_1.cpp
\example Algebraic_kernel_d/Solve_1.cpp
\example Algebraic_kernel_d/Compare_1.cpp
\example Algebraic_kernel_d/Isolate_1.cpp
\example Algebraic_kernel_d/Sign_at_1.cpp
*/
Alpha Shapes 2 Doc Alpha Shapes 2 Alpha Shapes 2 (`Alpha_shapes_2/doc/Alpha_shapes_2/Alpha_shapes_2.txt`)
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_2D_Alpha_Shapes
\anchor I1ChapterAlphashapes2D
\cgalAutoToc
\author Tran Kai Frank Da
\image html alphashape.png
\image latex alphashape.png
Assume we are given a set \f$ S\f$ of points in 2D or 3D and we would like to
have something like "the shape formed by these points". This is
quite a vague notion, and there are probably many possible
interpretations, the \f$ \alpha\f$-shape being one of them. Alpha shapes
can be used for shape reconstruction from a dense unorganized set of
data points. Indeed, an \f$ \alpha\f$-shape is demarcated by a frontier,
which is a linear approximation of the original shape \cgalCite{bb-srmua-97t}.
As mentioned in Edelsbrunner's and Mücke's paper \cgalCite{em-tdas-94},
one can intuitively think of an \f$ \alpha\f$-shape as the
following. Imagine a huge mass of ice-cream making up the space \f$ \mathbb{R}^3\f$
and containing the points as "hard" chocolate pieces. Using one of
these sphere-formed ice-cream spoons, we carve out all parts of the
ice-cream block we can reach without bumping into chocolate pieces,
thereby even carving out holes in the inside (e.g. parts not reachable
by simply moving the spoon from the outside). We will eventually end
up with a (not necessarily convex) object bounded by caps, arcs, and
points. If we now straighten all "round" faces to triangles and line
segments, we have an intuitive description of what is called the
\f$ \alpha\f$-shape of \f$ S\f$. The drawing above provides an example
of this process in 2D (where our ice-cream spoon is simply a circle).
Alpha shapes depend on a parameter \f$ \alpha\f$ after which they are named.
In the ice-cream analogy above, \f$ \alpha\f$ is the squared radius of the
carving spoon. A very small value will allow us to eat up all the
ice-cream except the chocolate points themselves. Thus, we already see
that the \f$ \alpha\f$-shape degenerates to the point-set \f$ S\f$ for
\f$ \alpha \rightarrow 0\f$. On the other hand, a huge value of \f$ \alpha\f$
will prevent us even from moving the spoon between two points since
it is too large. So we will never spoon up the ice-cream lying in the
inside of the convex hull of \f$ S\f$. Hence, the alpha shape becomes
the convex hull of \f$ S\f$ as \f$ \alpha \rightarrow \infty\f$.
\cgal offers 2D and 3D alpha shapes. The GUDHI library offers a
dD Alpha complex.
\section Alpha_shapes_2Definitions Definitions
We distinguish two versions of alpha shapes. Basic alpha shapes
are based on the Delaunay triangulation. Weighted alpha shapes
are based on its generalization, the regular triangulation
(cf. Section \ref Section_2D_Triangulations_Regular "Regular Triangulations"),
replacing the Euclidean distance by the power to weighted points.
There is a close connection between alpha shapes and the underlying
triangulations. More precisely, the \f$ \alpha\f$-complex of \f$ S\f$ is a
subcomplex of this triangulation of \f$ S\f$, containing the \f$ \alpha\f$-exposed
\f$ k\f$-simplices, \f$ 0 \leq k \leq d\f$. A simplex is said to be \f$ \alpha\f$-exposed, if there is an
open disk (resp. ball) of radius \f$ \sqrt{\alpha}\f$ through the vertices of the
simplex that does not contain any other point of \f$ S\f$, for the metric used in
the computation of the underlying triangulation. The corresponding
\f$ \alpha\f$-shape is defined as the underlying interior space of the
\f$ \alpha\f$-complex (see \cgalCite{em-tdas-94}).
In general, an \f$ \alpha\f$-complex is a non-connected and non-pure polytope,
meaning that one \f$ k\f$-simplex, with \f$ 0 \leq k \leq d-1\f$, is not necessarily
adjacent to a \f$ (k+1)\f$-simplex.
The \f$ \alpha\f$-shapes of \f$ S\f$ form a discrete family, even though they
are defined for all real numbers \f$ \alpha\f$ with \f$ 0 \leq \alpha
\leq \infty\f$. Thus, we can represent the entire family of \f$ \alpha\f$-shapes
of \f$ S\f$ by the underlying triangulation of \f$ S\f$. In this representation
each \f$ k\f$-simplex of the underlying triangulation is associated with an
interval that specifies for which values of \f$ \alpha\f$ the \f$ k\f$-simplex
belongs to the \f$ \alpha\f$-shape. Relying on this fact, the family of
\f$ \alpha\f$-shapes can be computed efficiently and relatively
easily. Furthermore, we can select an appropriate \f$ \alpha\f$-shape from a
finite number of different \f$ \alpha\f$-shapes and corresponding
\f$ \alpha\f$-values.
\section I1_SectAlpha_Shape_2 Functionality
The class Alpha_shape_2<Dt> represents the family of
\f$ \alpha\f$-shapes of points in a plane for all positive
\f$ \alpha\f$. It maintains the underlying triangulation Dt which
represents connectivity and order among squared radius of its faces. Each
\f$ k\f$-dimensional face of the Dt is associated with an interval
that specifies for which values of \f$ \alpha\f$ the face belongs to the
\f$ \alpha\f$-shape. There are links between the intervals and the
\f$ k\f$-dimensional faces of the triangulation.
The class Alpha_shape_2<Dt> provides functions to set and
get the current \f$ \alpha\f$-value, as well as an iterator that enumerates
the \f$ \alpha\f$-values where the \f$ \alpha\f$-shape changes.
It provides iterators to enumerate the vertices and edges that are in
the \f$ \alpha\f$-shape, and functions that allow to classify vertices,
edges, and faces with respect to the \f$ \alpha\f$-shape. They can be in
the interior of a face that belongs or does not belong to the \f$ \alpha\f$-shape.
They can be singular/regular, that is, they can be on the boundary of the \f$ \alpha\f$-shape,
but not incident/incident to a triangle of the \f$ \alpha\f$-complex.
Finally, it provides a function to determine the \f$ \alpha\f$-value
such that the \f$ \alpha\f$-shape satisfies the following two properties,
or at least the second one if there is no such \f$ \alpha\f$ that both
are satisfied:
- the number of components equals a number of your choice, and
- all data points are either on the boundary or in the interior of the regularized version of the \f$ \alpha\f$-shape (no singular edges).
The current implementation is static, that is after its construction
points cannot be inserted or removed.
\section I1_SectDtClass2D Concepts and Models
We currently do not specify concepts for the underlying triangulation
type. Models that work for a basic alpha shape are the classesDelaunay_triangulation_2, Periodic_2_Delaunay_triangulation_2, andTriangulation_hierarchy_2 templated with a Delaunay
triangulation. A model that works for a weighted alpha shape is
the class Regular_triangulation_2.
\subsection AlphaShape2D_ConceptAndModelsAlphaShapes Alpha Shapes
The triangulation needs a geometric traits class as argument.
The requirements of this class are described in the concepts AlphaShapeTraits_2
in the non-weighted case and WeightedAlphaShapeTraits_2 in the weighted case.
All \cgal kernels are models of both concepts.
The triangulation data structure of the triangulation
has to be a model of the concept TriangulationDataStructure_2,
whose vertex and face classes are models of the conceptsAlphaShapeVertex_2 and AlphaShapeFace_2, respectively.
The classes Alpha_shape_vertex_base_2<Gt, Vb> and Alpha_shape_face_base_2<Gt, Fb>
are models of these concepts and can be used for all type of alpha shapes,
provided that the template parameters Vb and Fb are appropriately chosen,
as we shall see in the following section.
\subsection AlphaShape2D_ConceptAndModelsTDS Triangulation data structure
Additional requirements are put when using weighted or
periodic triangulations as underlying triangulation:
\section Alpha_shapes_2Examples Examples
\subsection I1_SectClassicAS2D Example for Basic Alpha Shapes
The basic alpha shape requires a Delaunay triangulation as
underlying triangulation Dt. The Delaunay triangulation class is
parameterized with a geometric and a triangulation data structure traits.
For the geometric traits class we can use a \cgal kernel.
For the triangulation data structure traits, we have to
choose the vertex and face classes needed for alpha shapes,
namely Alpha_shape_vertex_base_2<Gt, Vb> and Alpha_shape_face_base_2<Gt,Fb>.
The parameter Vb and Fb must be filled by classes that are models of theTriangulationVertexBase_2 and TriangulationFaceBase_2 concepts. The classesTriangulation_vertex_base_2<Gt> and Triangulation_face_base_2<Gt> fit these
requirements.
The example below illustrates how to construct a basic alpha shape.
Note that Triangulation_vertex_base_2<Gt> and Triangulation_face_base_2<Gt>
are the default parameters for Vb and Fb in the classesAlpha_shape_vertex_base_2<Gt, Vb> and Alpha_shape_face_base_2<Gt,Fb>. They
are thus omitted in the code below.
\cgalExample{Alpha_shapes_2/ex_alpha_shapes_2.cpp}
\subsection I1_SectWeightedAS2D Example for Weighted Alpha Shapes
A weighted alpha shape requires a regular triangulation as
underlying triangulation Dt.
Here again, we can use the vertex and face Alpha_shape_vertex_base_2<Gt, Vb>
and Alpha_shape_face_base_2<Gt,Fb>, but for weighted alpha shapes, Vb and Fb
must be models of the concepts RegularTriangulationVertexBase_2 andRegularTriangulationFaceBase_2. The classes Regular_triangulation_vertex_base_2<Gt>Regular_triangulation_face_base_2<Gt> fit these requirements.
Note that there is no special weighted alpha shape class.
The example below illustrates how to construct a weighted alpha shape.
\cgalExample{Alpha_shapes_2/ex_weighted_alpha_shapes_2.cpp}
\subsection AlphaShape_2DExampleForPeriodicAlphaShapes Example for Periodic Alpha Shapes
The following example shows how to use a periodic Delaunay
triangulation as underlying triangulation for the alpha shape computation.
In order to define the original domain and to benefit from the
built-in heuristic optimizations of the periodic triangulation computation,
it is recommended to first construct the triangulation and
then construct the alpha shape from it. The alpha
shape constructor that takes a point range can be used as well but in
this case the original domain cannot be specified and the default unit
cube will be chosen and no optimizations will be used.
It is also recommended to switch the triangulation to 1-sheeted
covering if possible. Note that a periodic triangulation in 9-sheeted
covering space is degenerate. In this case, an exact constructions
kernel needs to be used to compute the alpha shapes. Otherwise, the
results will suffer from round-off problems.
\cgalExample{Alpha_shapes_2/ex_periodic_alpha_shapes_2.cpp}
/
} / namespace CGAL */
Alpha Shapes 2 Doc Alpha Shapes 2 PackageDescription (`Alpha_shapes_2/doc/Alpha_shapes_2/PackageDescription.txt`)
/// \defgroup PkgAlphaShapes2Ref Reference Manual
/// \defgroup PkgAlphaShapes2Concepts Concepts
/// \ingroup PkgAlphaShapes2Ref
/*!
\addtogroup PkgAlphaShapes2Ref
\cgalPkgDescriptionBegin{2D Alpha Shapes,PkgAlphaShapes2}
\cgalPkgPicture{alpha-detail.png}
\cgalPkgSummaryBegin
\cgalPkgAuthor{Tran Kai Frank Da}
\cgalPkgDesc{This package offers a data structure encoding the whole family of alpha-complexes related to a given 2D Delaunay or regular triangulation. In particular, the data structure allows to retrieve the alpha-complex for any alpha value, the whole spectrum of critical alpha values and a filtration on the triangulation faces (this filtration is based on the first alpha value for which each face is included on the alpha-complex).}
\cgalPkgManuals{Chapter_2D_Alpha_Shapes,PkgAlphaShapes2Ref}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{2.1}
\cgalPkgDependsOn{\ref PkgTriangulation2}
\cgalPkgBib{cgal:d-as2}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgDemo{2D Alpha Shapes,alpha_shapes_2.zip}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
This chapter presents a framework for alpha shapes. The description is based on
the articles \cgalCite{em-tdas-94}, \cgalCite{e-was-92}. Alpha shapes are
the generalization of the convex hull of a point set. Let \f$ S\f$ be a finite set of
points in \f$ \mathbb{R}^d\f$, \f$ d = 2,3\f$ and \f$ \alpha\f$ a parameter with \f$ 0 \leq \alpha \leq
\infty\f$. For \f$ \alpha = \infty\f$, the \f$ \alpha\f$-shape is the convex hull of \f$ S\f$. As
\f$ \alpha\f$ decreases, the \f$ \alpha\f$-shape shrinks and develops cavities, as soon as
a sphere of radius \f$ \sqrt{\alpha}\f$ can be put inside.
Finally, for \f$ \alpha = 0\f$, the \f$ \alpha\f$-shape is the set \f$ S\f$ itself.
We distinguish two versions of alpha shapes, one is based on the Delaunay
triangulation and the other on its generalization, the regular triangulation,
replacing the natural distance by the power to weighted points. The metric used
determines an underlying triangulation of the alpha shape and thus, the version
computed.
The basic alpha shape (cf. \ref I1_SectClassicAS2D) is associated with the Delaunay triangulation
(cf. Section \ref Section_2D_Triangulations_Delaunay "Delaunay Triangulations").
The weighted alpha shape (cf. \ref I1_SectWeightedAS2D ) is associated with the regular triangulation
(cf. Section \ref Section_2D_Triangulations_Regular "Regular Triangulations").
There is a close connection between alpha shapes and the underlying
triangulations. More precisely, the \f$ \alpha\f$-complex of \f$ S\f$ is a
subcomplex of this triangulation of \f$ S\f$, containing the \f$ \alpha\f$-exposed
\f$ k\f$-simplices, \f$ 0 \leq k \leq d\f$. A simplex is \f$ \alpha\f$-exposed, if there is an
open disk (resp. ball) of radius \f$ \sqrt{\alpha}\f$ through the vertices of the
simplex that does not contain any other point of \f$ S\f$, for the metric used in
the computation of the underlying triangulation. The corresponding
\f$ \alpha\f$-shape is defined as the underlying interior space of the
\f$ \alpha\f$-complex.
In general, an \f$ \alpha\f$-complex is a non-connected and non-pure polytope,
meaning that a \f$ k\f$-simplex, with \f$ 0 \leq k \leq d-1\f$, is not necessarily
adjacent to a \f$ (k+1)\f$-simplex.
The \f$ \alpha\f$-shapes of \f$ S\f$ form a discrete family, even though they
are defined for all real numbers \f$ \alpha\f$ with \f$ 0 \leq \alpha
\leq \infty\f$. Thus, we can represent the entire family of \f$ \alpha\f$-shapes
of \f$ S\f$ by the underlying triangulation of \f$ S\f$. In this representation
each \f$ k\f$-simplex of the underlying triangulation is associated with an
interval that specifies for which values of \f$ \alpha\f$ the \f$ k\f$-simplex
belongs to the \f$ \alpha\f$-shape. Relying on this result, the family of
\f$ \alpha\f$-shapes can be computed efficiently and relatively
easily. Furthermore, we can select an appropriate \f$ \alpha\f$-shape from a
finite number of different \f$ \alpha\f$-shapes and corresponding
\f$ \alpha\f$-values.
\cgalClassifedRefPages
\cgalCRPSection{Concepts}
AlphaShapeTraits_2WeightedAlphaShapeTraits_2AlphaShapeFace_2AlphaShapeVertex_2
\cgalCRPSection{Classes}
CGAL::Alpha_shape_2<Dt>CGAL::Alpha_shape_vertex_base_2<AlphaShapeTraits_2>CGAL::Alpha_shape_face_base_2<AlphaShapeTraits_2, TriangulationFaceBase_2>
*/
Alpha Shapes 2 Doc Alpha Shapes 2 Examples (`Alpha_shapes_2/doc/Alpha_shapes_2/examples.txt`)
/*!
\example Alpha_shapes_2/ex_alpha_shapes_2.cpp
\example Alpha_shapes_2/ex_periodic_alpha_shapes_2.cpp
\example Alpha_shapes_2/ex_weighted_alpha_shapes_2.cpp
\example Alpha_shapes_2/ex_alpha_projection_traits.cpp
*/
Alpha Shapes 3 Doc Alpha Shapes 3 Alpha Shapes 3 (`Alpha_shapes_3/doc/Alpha_shapes_3/Alpha_shapes_3.txt`)
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_3D_Alpha_Shapes
\cgalAutoToc
\authors Tran Kai Frank Da, Sébastien Loriot, and Mariette Yvinec
\image html alphashape.png
\image latex alphashape.png
Assume we are given a set \f$ S\f$ of points in 2D or 3D and we would like to
have something like "the shape formed by these points". This is
quite a vague notion and there are probably many possible
interpretations, the alpha shape being one of them. Alpha shapes
can be used for shape reconstruction from a dense unorganized set of
data points. Indeed, an alpha shape is demarcated by a frontier,
which is a linear approximation of the original shape \cgalCite{bb-srmua-97t}.
As mentioned in Edelsbrunner's and Mücke's paper \cgalCite{em-tdas-94},
one can intuitively think of an alpha shape as the
following. Imagine a huge mass of ice-cream making up the space \f$ \mathbb{R}^3\f$
and containing the points as "hard" chocolate pieces. Using one of
those sphere-formed ice-cream spoons, we carve out all parts of the
ice-cream block we can reach without bumping into chocolate pieces,
thereby even carving out holes in the inside (e.g. parts not reachable
by simply moving the spoon from the outside). We will eventually end
up with a (not necessarily convex) object bounded by caps, arcs and
points. If we now straighten all "round" faces to triangles and line
segments, we have an intuitive description of what is called the
alpha shape of \f$ S\f$. The drawing above provides an example
of this process in 2D (where our ice-cream spoon is simply a circle).
Alpha shapes depend on a parameter \f$ \alpha\f$ after which they are named.
In the ice-cream analogy above, \f$ \alpha\f$ is the squared radius of the
carving spoon. A very small value will allow us to eat up all the
ice-cream except the chocolate points themselves. Thus, we already see
that the alpha shape degenerates to the point-set \f$ S\f$ for
\f$ \alpha \rightarrow 0\f$. On the other hand, a huge value of \f$ \alpha\f$
will prevent us even from moving the spoon between two points since
it is too large and we will never spoon up the ice-cream lying in the
inside of the convex hull of \f$ S\f$. Hence, the alpha shape becomes
the convex hull of \f$ S\f$ as \f$ \alpha \rightarrow \infty\f$.
\cgal offers 2D and 3D alpha shapes. The GUDHI library offers a
dD Alpha complex.
\section Alpha_shapes_3Definitions Definitions
We distinguish two versions of alpha shapes. Basic alpha shapes
are based on the Delaunay triangulation. Weighted alpha shapes
are based on its generalization, the regular triangulation
(cf. Section \ref Triangulation3secclassRegulartriangulation "Regular Triangulations"),
replacing the Euclidean distance by the power to weighted points.
Let us consider the basic case with a Delaunay triangulation.
We first define the alpha complex of the set of points \f$ S\f$.
The alpha complex is a subcomplex
of the Delaunay triangulation.
For a given value of \f$ \alpha\f$, the alpha complex includes
all the simplices in the Delaunay triangulation which have
an empty circumscribing sphere with squared radius equal or smaller than \f$ \alpha\f$.
Here "empty" means that the open sphere does not include any points of \f$ S\f$.
The alpha shape is then simply the domain covered by the simplices
of the alpha complex (see \cgalCite{em-tdas-94}).
In general, an alpha complex is a disconnected and non-pure complex,
meaning in particular that the alpha complex may have
singular faces. For \f$ 0 \leq k \leq d-1\f$,
a \f$ k\f$-simplex of the alpha complex is said to be
singular if it is not a facet of a \f$ (k+1)\f$-simplex of the complex.
\cgal provides two versions of alpha shapes. In the general mode,
the alpha shapes correspond strictly to the above definition.
The regularized mode provides a regularized version of the alpha shapes.
It corresponds to the domain covered by a regularized version
of the alpha complex where singular faces are removed
(See \cgalFigureRef{figgenregex} for an example).
\cgalFigureBegin{figgenregex,gen-reg-ex.png}
Comparison of general and regularized alpha-shape. Left: Some points are taken on the surface of a torus, three points being taken relatively far from the surface of the torus; Middle: The general alpha-shape (for a large enough alpha value) contains the singular triangle facet of the three isolated points; Right: The regularized version (for the same value of alpha) does not contain any singular facet.
\cgalFigureEnd
The alpha shapes of a set of points
\f$ S\f$ form a discrete family, even though they
are defined for all real numbers \f$ \alpha\f$.
The entire family of alpha shapes can be represented
through the underlying triangulation of \f$ S\f$. In this representation
each \f$ k\f$-simplex of the underlying triangulation is associated with an
interval that specifies for which values of \f$ \alpha\f$ the \f$ k\f$-simplex
belongs to the alpha complex. Relying on this fact, the family of
alpha shapes can be computed efficiently and relatively
easily. Furthermore, we can select the optimal value
of \f$ \alpha\f$ to get an alpha shape including all data points
and having less than a given number of connected components.
Also, the alpha-values allows to define a filtration on the
faces of the triangulation of a set of points.
In this filtration, the faces of the triangulation are output
in increasing order of the alpha value
for which they appear
in the alpha complex. In case of equal alpha values,
lower dimensional faces are output first.
The definition is analog in the case of weighted alpha shapes.
The input set is now a set of weighted points (which can be regarded
as spheres) and the underlying triangulation
is the regular triangulation of this set.
Two spheres, or two weighted points, with centers \f$ C_1, C_2\f$
and radii \f$ r_1, r_2 \f$ are said to be orthogonal iff
\f$ C_1C_2 ^2 = r_1^2 + r_2^2\f$ and suborthogonal
iff \f$ C_1C_2 ^2 < r_1^2 + r_2^2\f$.
For a given value of \f$ \alpha\f$,
the weighted alpha complex is formed with the simplices of the
regular triangulation such that there is a sphere
orthogonal to the weighted points associated
with the vertices of the simplex and suborthogonal to all the other
input weighted points. Once again the alpha shape is then defined as
the domain covered by the alpha complex and comes in general and
regularized versions.
\section Alpha_Shape_3Functionality Functionality
\subsection AlphaShape_3DFamilyOfAlphaShapes Family of Alpha Shapes
The class Alpha_shape_3<Dt,ExactAlphaComparisonTag> represents the whole
family of alpha shapes for a given set of points.
The class includes the underlying triangulation Dt
of the set, and associates to each \f$ k\f$-face of this triangulation
an interval specifying
for which values of \f$ \alpha\f$ the face belongs to the
alpha complex.
The second template parameter, ExactAlphaComparisonTag, is a tag that,
when set to CGAL::Tag_true, triggers exact comparisons between alpha values.
The class provides functions to set and
get the current \f$ \alpha\f$-value, as well as an iterator that enumerates
the \f$ \alpha\f$ values where the alpha shape changes.
Additionally, the class has a filtration member function that, given
an output iterator with Object
as value type, outputs the faces of the triangulation
according to the
order of apparition in the alpha complex when alpha increases.
Finally, it provides a function to determine
the smallest value \f$ \alpha\f$
such that the alpha shape satisfies the following two properties:
- All data points are either on the boundary or in the interior of the regularized version of the alpha shape (no singular faces).
- The number of components is equal or less than a given number.
The current implementation is static, that is after its construction
points cannot be inserted or removed.
\subsection AlphaShape_3DAlphaShapeForAFixedAlpha Alpha Shape for a Fixed Alpha
Given a value of alpha, the class Fixed_alpha_shape_3<Dt> represents one
alpha shape for a given set of points.
The class includes the underlying triangulation Dt
of the set, and associates to each \f$ k\f$-face of this triangulation
a classification type. This class is dynamic, that is after its construction
points can be inserted or removed.
\subsection AlphaShape_3DClassificationAndIterators Classification and Iterators
Both classes provide member functions to classify for a (given) value
of \f$ \alpha\f$ the different faces of the triangulation asEXTERIOR, SINGULAR, REGULAR orINTERIOR with respect
to the alpha shape. A \f$ k\f$-face on the boundary of the alpha complex
is said to be: REGULAR if it is a subface of the alpha-complex which
is a subface of a \f$ (k+1)\f$-face of the alpha complex, and SINGULAR otherwise.
A \f$ k\f$-face of the alpha complex which is not on the boundary of the alpha complex
is said to be INTERIOR. See \cgalFigureRef{figclassif} for a 2D illustration.
\cgalFigureBegin{figclassif,classif.png} Classification of simplices, a 2D example. Left: The 2D
Delaunay triangulation of a set of points; Right:
Classification of simplices for a given alpha (the squared radius of
the red circle). INTERIOR, REGULAR and SINGULAR simplices are
depicted in black, green and blue respectively. EXTERIOR simplices
are not depicted. The vertex s and the edge tu are SINGULAR
since all higher dimension simplices they are incident to areEXTERIOR. The facet pqr is EXTERIOR because the squared radius
of its circumscribed circle is larger than alpha.
\cgalFigureEnd
The classes provide also output iterators to get for a given alpha value
the vertices, edges, facets and cells of the different types
(EXTERIOR, SINGULAR, REGULAR orINTERIOR).
\subsection AlphaShape3DIO Input/Output
It is possible to export a 3D alpha shape to a std::ostream
using the operator<<, see the documentation of the class Alpha_shape_3 for more information.
\section AlphaShape3D_ConceptAndModels Concepts and Models
We currently do not specify concepts for the underlying triangulation
type. Models that work for a family of alpha-shapes are the instantiations
of the classes Delaunay_triangulation_3 andPeriodic_3_Delaunay_triangulation_3 (see
example \ref AlphaShape_3DExampleForPeriodicAlphaShapes).
A model that works for a fixed alpha-shape are the instantiations
of the class Delaunay_triangulation_3.
Models that work for a weighted alpha-shape are the instantiations
of the classes Regular_triangulation_3 andPeriodic_3_regular_triangulation_3.
The triangulation needs a geometric traits class
and a triangulation data structure as template parameters.
\subsection AlphaShape3D_ConceptAndModelsAlphaShapes Alpha Shapes
For the class Alpha_shape_3<Dt,ExactAlphaComparisonTag>, the requirements of
the traits class are described in the concepts AlphaShapeTraits_3
in the non-weighted case and WeightedAlphaShapeTraits_3 in the weighted case.
All \cgal kernels are models of both concepts.
The triangulation data structure of the triangulation
has to be a model of the concept TriangulationDataStructure_3
whose vertex and cell classes are model of the conceptsAlphaShapeVertex_3 and AlphaShapeCell_3.
The classes Alpha_shape_vertex_base_3<Gt> and Alpha_shape_cell_base_3<Gt>
are models of these concepts and can be used for all type of alpha shapes,
provided that the template parameters Vb and Fb are appropriately chosen,
as we shall see in the following section.
\subsection AlphaShape3D_ConceptAndModelsFixedAlphaShapes Fixed Alpha Shapes
For the class Fixed_alpha_shape_3<Dt>, the requirements of
the traits class are described in the concepts FixedAlphaShapeTraits_3
in the non-weighted case and FixedWeightedAlphaShapeTraits_3 in the weighted case.
All \cgal kernels are models of both concepts.
The triangulation data structure of the triangulation
has to be a model of the concept TriangulationDataStructure_3
whose vertex and cell classes are model of the conceptsFixedAlphaShapeVertex_3 and FixedAlphaShapeCell_3.
The package provides models Fixed_alpha_shape_vertex_base_3<Gt>
and Fixed_alpha_shape_cell_base_3<Gt>, respectively.
\subsection AlphaShape3D_ConceptAndModelsTDS Triangulation data structure
Additional requirements are put when using weighted or
periodic triangulations as underlying triangulation:
\section Alpha_shapes_3AlphaShape3OrFixedAlphaShape3 Alpha_shape_3 vs. Fixed_alpha_shape_3
The class Alpha_shape_3<Dt,ExactAlphaComparisonTag> represents the whole family
of alpha shapes for a given set of points while the class Fixed_alpha_shape_3<Dt>
represents only one alpha shape (for a fixed alpha). When using the same kernel,Fixed_alpha_shape_3<Dt> is a lighter version. It is thus naturally much more efficient
when the alpha-shape is needed for a single given value of alpha.
In addition, note that the class Alpha_shape_3<Dt,ExactAlphaComparisonTag>
requires constructions (squared radius of simplices) while the
class Fixed_alpha_shape_3<Dt> uses only predicates.
This implies that a certified construction of one (several)
alpha-shape, using the Alpha_shape_3<Dt,ExactAlphaComparisonTag> requires a kernel
with exact predicates and exact constructions (or setting ExactAlphaComparisonTag to Tag_true)
while using a kernel with exact predicates is sufficient for the class Fixed_alpha_shape_3<Dt>.
This makes the class Fixed_alpha_shape_3<Dt> even more efficient in this setting.
In addition, note that the Fixed version is the only one of the
two that supports incremental insertion and removal of points.
We give the time spent while computing the alpha shape of a protein (considered
as a set of weighted points) featuring 4251 atoms (using gcc 4.3 under Linux with -O3
and -DNDEBUG flags, on a 2.27GHz Intel(R) Xeon(R) E5520 CPU):
Using Exact_predicates_inexact_constructions_kernel, building
the regular triangulation requires 0.09s, then the class Fixed_alpha_shape_3<Dt>
required 0.05s while the class Alpha_shape_3<Dt,ExactAlphaComparisonTag> requires 0.35s
if ExactAlphaComparisonTag is Tag_false (and 0.70s with Tag_true).
Using Exact_predicates_exact_constructions_kernel, building
the regular triangulation requires 0.19s and then the class Alpha_shape_3<Dt,ExactAlphaComparisonTag>
requires 0.90s.
\section Alpha_shapes_3Examples Examples
\subsection AlphaShape_3DExampleForBasicAlphaShapes Example for Basic Alpha-Shapes
This example builds a basic alpha shape using a Delaunay triangulation
as underlying triangulation.
\cgalExample{Alpha_shapes_3/ex_alpha_shapes_3.cpp}
\subsection AlphaShape_3DBuildingBasicAlphaShapesForManyPoints Building Basic Alpha Shapes for Many Points
When many points are input in the alpha shape, say more than 10 000,
it may pay off to use a Delaunay triangulation with the Fast_location
policy as underlying triangulation in order to speed up point location
queries (cf. Section \ref Triangulation3seclocpol "The Location Policy Parameter").
\cgalExample{Alpha_shapes_3/ex_alpha_shapes_with_fast_location_3.cpp}
\subsection AlphaShape_3DExampleforWeightedAlphaShapes Example for Weighted Alpha-Shapes
The following examples build a weighted alpha shape requiring a
regular triangulation as underlying triangulation.
The alpha shape is built in GENERAL mode.
\cgalExample{Alpha_shapes_3/ex_weighted_alpha_shapes_3.cpp}
\subsection AlphaShape_3DExampleForFixedWeightedAlphaShapes Example for Fixed Weighted Alpha-Shapes
Same example as previous but using a fixed value of alpha.
\cgalExample{Alpha_shapes_3/ex_fixed_weighted_alpha_shapes_3.cpp}
\subsection AlphaShape_3DBuildingAnAlphaShapesWithExactComparisonsOfAlphaValues Building an Alpha Shapes with Exact Comparisons of Alpha Values
On some platforms, the alpha shapes of the set of points of this example
cannot be computed when using a traits with inexact constructions. To be able to compute
them with a traits with inexact constructions, the tagExactAlphaComparisonTag is set to Tag_true.
\cgalExample{Alpha_shapes_3/ex_alpha_shapes_exact_alpha.cpp}
\subsection AlphaShape_3DExampleForPeriodicAlphaShapes Example for Periodic Alpha Shapes
The following example shows how to use a periodic Delaunay
triangulation (Chapter \ref Chapter_3D_Periodic_Triangulations "3D Periodic Triangulations") as underlying
triangulation for the alpha shape computation. Usage of a weighted Delaunay periodic
triangulation is presented in the example: \ref Alpha_shapes_3/ex_weighted_periodic_alpha_shapes_3.cpp "ex_weighted_periodic_alpha_shapes_3.cpp".
In order to define the original domain and to benefit from the
built-in heuristic optimizations of the periodic triangulation computation,
it is recommended to first construct the triangulation and
then construct the alpha shape from it. The alpha
shape constructor that takes a point range can be used as well but in
this case the original domain cannot be specified and the default unit
cube will be chosen and no optimizations will be used.
It is also recommended to switch the triangulation to 1-sheeted
covering if possible. Note that a periodic triangulation in 27-sheeted
covering space is degenerate. In this case, an exact constructions
kernel needs to be used to compute the alpha shapes. Otherwise, the
results will suffer from round-off problems.
\cgalExample{Alpha_shapes_3/ex_periodic_alpha_shapes_3.cpp}
/
} / namespace CGAL */
Alpha Shapes 3 Doc Alpha Shapes 3 PackageDescription (`Alpha_shapes_3/doc/Alpha_shapes_3/PackageDescription.txt`)
/// \defgroup PkgAlphaShapes3Ref Reference Manual
/// \defgroup PkgAlphaShapes3Concepts Concepts
/// \ingroup PkgAlphaShapes3Ref
/*!
\addtogroup PkgAlphaShapes3Ref
\cgalPkgDescriptionBegin{3D Alpha Shapes,PkgAlphaShapes3}
\cgalPkgPicture{alpha_shapes_3_small.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Tran Kai Frank Da, Sébastien Loriot, and Mariette Yvinec}
\cgalPkgDesc{This package offers a data structure encoding either one alpha-complex or the whole family of alpha-complexes related to a given 3D Delaunay or regular triangulation. In the latter case, the data structure allows to retrieve the alpha-complex for any alpha value, the whole spectrum of critical alpha values and a filtration on the triangulation faces (this filtration is based on the first alpha value for which each face is included on the alpha-complex). }
\cgalPkgManuals{Chapter_3D_Alpha_Shapes,PkgAlphaShapes3Ref}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{2.3}
\cgalPkgDependsOn{\ref PkgTriangulation3}
\cgalPkgBib{cgal:dy-as3}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgDemo{3D Alpha Shapes,alpha_shape_3.zip}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
Alpha shapes definition is based on an underlying
triangulation that may be a Delaunay triangulation
in case of basic alpha shapes or a regular triangulation
(cf. Section \ref Triangulation3secclassRegulartriangulation "3D Regular Triangulation")
in case of weighted alpha shapes.
Let us consider the basic case with a Delaunay triangulation.
We first define the alpha complex of the set of points \f$ S\f$.
The alpha complex is a subcomplex
of the Delaunay triangulation.
For a given value of \f$ \alpha\f$, the alpha complex includes
all the simplices in the Delaunay triangulation which have
an empty circumscribing sphere with squared radius equal or smaller than \f$ \alpha\f$.
Here "empty" means that the open sphere
do not include any points of \f$ S\f$.
The alpha shape is then simply the domain covered by the simplices
of the alpha complex (see \cgalCite{em-tdas-94}).
In general, an alpha complex is a non-connected and non-pure complex.
This means in particular that the alpha complex may have
singular faces. For \f$ 0 \leq k \leq d-1\f$,
a \f$ k\f$-simplex of the alpha complex is said to be
singular if it is not a facet of a \f$ (k+1)\f$-simplex of the complex
The alpha shapes of a set of points
\f$ S\f$ form a discrete family, even though they
are defined for all real numbers \f$ \alpha\f$.
The entire family of alpha shapes can be represented
through the underlying triangulation of \f$ S\f$. In this representation,
each \f$ k\f$-simplex of the underlying triangulation is associated with an
interval that specifies for which values of \f$ \alpha\f$ the \f$ k\f$-simplex
belongs to the alpha complex. Relying on this fact, the family of
alpha shapes can be computed efficiently and relatively
easily. Furthermore, we can select the optimal value
of \f$ \alpha\f$ to get an alpha shape including all data points
and having less than a given number of connected components.
The definition is analog in the case of weighted alpha shapes.
The input set is now a set of weighted points (which can be regarded
as spheres) and the underlying triangulation
is the regular triangulation of this set.
Two spheres, or two weighted points, with centers \f$ C_1, C_2\f$
and radii \f$ r_1, r_2 \f$ are said to be orthogonal iff
\f$ C_1C_2 ^2 = r_1^2 + r_2^2\f$ and suborthogonal
iff \f$ C_1C_2 ^2 < r_1^2 + r_2^2\f$.
For a given value of \f$ \alpha\f$
the weighted alpha complex is formed with the simplices of the
regular triangulation triangulation
such that there is a sphere orthogonal to the weighted points associated
with the vertices of the simplex and suborthogonal to all the other
input weighted points. Once again the alpha shape is then defined as
the domain covered by a the alpha complex.
CGAL provides two versions to compute alpha shapes.
The first one gives access to an explicit classification of all the simplices for a fixed alpha value.
The second one gives access to the entire family of alpha shapes of a set of points.
This latter version comes with two modes. In the general mode,
the alpha shapes correspond strictly to the definition previously made.
The regularized mode provides a regularized version of the alpha shapes
corresponding to the domain covered by a regularized version
of the alpha complex where singular faces are removed.
\cgalClassifedRefPages
\cgalCRPSection{Concepts}
AlphaShapeTraits_3WeightedAlphaShapeTraits_3AlphaShapeCell_3AlphaShapeVertex_3FixedAlphaShapeTraits_3FixedWeightedAlphaShapeTraits_3FixedAlphaShapeCell_3FixedAlphaShapeVertex_3
\cgalCRPSection{Classes}
CGAL::Alpha_status<NT>CGAL::Alpha_shape_3<Dt,ExactAlphaComparisonTag>CGAL::Alpha_shape_vertex_base_3CGAL::Alpha_shape_cell_base_3CGAL::Fixed_alpha_shape_3<Dt>CGAL::Fixed_alpha_shape_vertex_base_3CGAL::Fixed_alpha_shape_cell_base_3
*/
Alpha Shapes 3 Doc Alpha Shapes 3 Examples (`Alpha_shapes_3/doc/Alpha_shapes_3/examples.txt`)
/*!
\example Alpha_shapes_3/ex_alpha_shapes_3.cpp
\example Alpha_shapes_3/ex_alpha_shapes_exact_alpha.cpp
\example Alpha_shapes_3/ex_alpha_shapes_with_fast_location_3.cpp
\example Alpha_shapes_3/ex_fixed_weighted_alpha_shapes_3.cpp
\example Alpha_shapes_3/ex_periodic_alpha_shapes_3.cpp
\example Alpha_shapes_3/ex_weighted_alpha_shapes_3.cpp
\example Alpha_shapes_3/ex_weighted_periodic_alpha_shapes_3.cpp
\example Alpha_shapes_2/ex_alpha_projection_traits.cpp
*/
Alpha Wrap 2 Doc Alpha Wrap 2 PackageDescription (`Alpha_wrap_2/doc/Alpha_wrap_2/PackageDescription.txt`)
/// \defgroup PkgAlphaWrap2Ref Reference Manual
/// \defgroup PkgAlphaWrap2Concepts Concepts
/// \ingroup PkgAlphaWrap2Ref
/// \defgroup AW2_free_functions_grp Free Functions
/// Functions to create a wrap from point clouds, segment soups, and polygons with holes.
/// \ingroup PkgAlphaWrap2Ref
/*!
\addtogroup PkgAlphaWrap2Ref
\cgalPkgDescriptionBegin{2D Alpha Wrapping,PkgAlphaWrap2}
\cgalPkgPicture{alpha_wrap_2.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Pierre Alliez, David Cohen-Steiner, Michael Hemmer, Cédric Portaneri, and Mael Rouxel-Labbé}
\cgalPkgDesc{This component takes a polygon soup, a 2D segment soup, and/or a 2D point set as input,
and generates a valid (watertight, intersection-free and 1-manifold) multi-polygon that
strictly encloses the input. The algorithm proceeds by shrink-wrapping
and refining a 2D Delaunay triangulation starting from a loose bounding box of the input.
Two user-defined parameters, alpha and offset, offer control over the maximum size
of cavities where the shrink-wrapping process can enter, and the tightness
of the final polygon(s) to the input, respectively. Once combined, these parameters
provide a means to trade fidelity to the input for complexity of the output.}
\cgalPkgManuals{Chapter_2D_Alpha_wrapping,PkgAlphaWrap2Ref}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{6.2}
\cgalPkgDependsOn{\ref PkgTriangulation2 and \ref PkgPolygonRepair}
\cgalPkgBib{cgal:achpr-aw2}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgDemo{2D Alpha wrap demo,alpha_wrap_2.zip}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
\cgalCRPSection{Concepts}
AlphaWrapTraits_2
\cgalCRPSection{Functions}
- \link AW2_free_functions_grp
CGAL::alpha_wrap_2()\endlink
*/
Alpha Wrap 2 Doc Alpha Wrap 2 Alpha Wrap 2 (`Alpha_wrap_2/doc/Alpha_wrap_2/alpha_wrap_2.txt`)
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_2D_Alpha_wrapping
\cgalAutoToc
\authors Pierre Alliez, David Cohen-Steiner, Michael Hemmer, Cédric Portaneri, Mael Rouxel-Labbé
\section aw2_introduction Introduction
Various tasks in geometric modeling and processing require 2D objects represented as valid polygons,
where "valid" refers to polygons that are closed, intersection-free (simple), orientable, and 1-manifold.
Such representations offer well-defined notions of interior/exterior and geodesic neighborhoods.
2D data are usually acquired through measurements, possibly followed by reconstruction,
designed by humans, or generated through imperfect automated processes.
As a result, they can exhibit a wide variety of defects including gaps, missing data,
self-intersections, degeneracies such as zero-area structures, and non-manifold features.
Given the large repertoire of possible defects, many methods and data structures have been proposed
to repair specific defects (see for example the package \ref PkgPolygonRepair), usually with the goal
of guaranteeing specific properties in the repaired 2D model.
Reliably repairing all types of defects is an ill-posed problem as many valid solutions exist
for a given 2D model with defects.
In addition, the input model can be overly complex with unnecessary geometric details,
spurious topological structures, nonessential inner components, or excessively fine discretizations.
For applications such as collision avoidance, path planning, or simulation,
getting an approximation (i.e., a silhouette) of the input can be more relevant than repairing it.
Approximation herein refers to an approach capable of filtering out inner structures,
fine details and cavities, as well as wrapping the input within a user-defined offset margin.
Given an input 2D geometry, we address the problem of computing a conservative approximation,
where conservative means that the output is guaranteed to strictly enclose the input.
We seek unconditional robustness in the sense that the output polygon should be valid (oriented,
1-manifold, and without self-intersections), even for raw input with many defects
and degeneracies.
The default input is a soup of 2D segments, but the generic interface leaves the door open
to other types of finite 2D primitives such as point sets.
\cgalFigureAnchor{aw2_inputs_fig}
\cgalFigureCaptionBegin{aw2_inputs_fig}
(Left) Shrink-wrapping output from a segment soup with many intersections and gaps.
(Center & Right) Possible input degeneracies: non-manifold vertices and zero-area structures.
The algorithm handles these cases by wrapping an offset of the input.
\cgalFigureCaptionEnd
\section aw2_definition Approach
Many approaches have been devised to enclose a 2D model within a polygon, featuring different balances
between the runtime and quality (i.e., tightness) of the approximation.
Within the simplest cases, an axis-aligned or oriented bounding box clearly satisfies some desired properties;
however, the approximation error is uncontrollable and often very large.
Computing the convex hull of the input also matches some of the desired properties
and improves the quality of the result, albeit at the price of increasing the runtime.
However, the approximation remains crude, especially in the case of several components.
The convex hull is, in fact, a special case of alpha shapes (\ref PkgAlphaShapes2).
Mathematically, the alpha shape is a subcomplex of the Delaunay triangulation, with simplicies
being part of the complex depending on the size of their minimal (empty) Delaunay ball.
Intuitively, constructing 2D alpha shapes can be thought of as carving 2D space with an empty ball
of user-defined radius alpha.
Alpha shapes yield provable, good piecewise-linear approximations of a shape \cgalCite{bb-srmua-97t},
but are defined on point sets, whereas we wish to deal with more general input data, such as segment soups.
Even after sampling the segment soup, alpha shapes do not guarantee to be conservative for any alpha.
Finally, inner structures are also carved within the interior, instead of being filtered out.
Inspired by alpha shapes, we replace the above notion of carving by shrink-wrapping:
we iteratively construct a subcomplex of a 2D Delaunay triangulation by starting from
a simple 2D Delaunay triangulation enclosing the input, and then iteratively removing eligible triangles
that lie on the boundary of the complex.
In addition, the underlying triangulation---and thus the complex incidentally---is refined
as shrinking proceeds.
Thus, instead of carving from the convex hull of the input data as in alpha shapes, we construct
an entirely new polygon through a Delaunay refinement-like algorithm. The refinement algorithm inserts Steiner points
on the boundary of an offset surface, defined as a level set of the unsigned distance field to the input.
This process both prevents the creation of inner structures within the output and avoids superfluous computations.
In addition, detaching our wrap construction from the geometry and discretization of the input has several advantages:
(1) the underlying data is not restricted to a specific format (polygon soups, segment soups, point sets, etc.)
as all that is required is answering three basic geometric queries: (a) the distance between a point
and the input, (b) the projection of a query point onto the input, (c) an intersection test
between a triangle and the input, and (2) The user has more freedom to trade tightness
to the input for final polygon complexity, as constructing a conservative approximation on a large offset
of the input requires fewer polygon edges.
\subsection aw2_algorithm Algorithm
Initialization. The algorithm is initialized by inserting the four corner vertices
of a loose bounding box into a 2D Delaunay triangulation.
In the 2D Delaunay triangulation of \cgal, all edges are adjacent to two triangle faces.
Each edge of the boundary of the Delaunay triangulation, which coincides with one edge
of the convex hull of the triangulation vertices, is adjacent to a so-called infinite triangle face,
an abstract face connected to the so-called infinite vertex to ensure the aforementioned double-edge adjacency.
Initially, all infinite faces are tagged as outside, and all finite triangle faces are tagged as inside.
Shrink-wrapping. The shrink-wrapping algorithm proceeds by traversing the faces
of the Delaunay triangulation from outside to inside, flood-filling from one face to its adjacent face,
and tagging the adjacent face as outside whenever possible (the term "possible" is specified later).
Flood filling is implemented via a priority queue of Delaunay triangle edges representing
the traversal between the two adjacent faces of the edge, from outside to inside.
These edges are referred to as gates in the following.
Given an outside face and its adjacent inside face, the common edge (i.e., a gate) is said
to be alpha-traversable if its circumradius is larger than the user-defined parameter alpha,
where circumradius refers to the radius of the relating segment's Delaunay ball.
Intuitively, cavities smaller than alpha are not accessible as their gates are not alpha-traversable.
Initialized by the alpha-traversable gates on the convex hull, the priority queue contains only
alpha-traversable gates and is sorted by decreasing order of the circumradius of the gate.
Traversal can be seen as a continuous process that advances along dual Voronoi edges of the gates,
with a pencil of empty balls circumscribing the gate.
\cgalFigureAnchor{aw2_pencil_fig}
\cgalFigureCaptionBegin{aw2_pencil_fig}
(Left) Pencil of empty circles (blue) circumscribing a Delaunay edge (green) in a 2D Delaunay triangulation (black).
From the top triangle circumcenter c1 to the bottom triangle circumcenter c2, the dual Voronoi edge denoted by e (doted red) is the trace of centers of the largest circles that are empty of Delaunay vertex.
(Right) The graph corresponding to the left example. The x-axis corresponds to the position of empty circle centers located on the Voronoi edge e, from c1 to c2. The y-axis is the radius value of the corresponding empty circles. In this case, the minimum radius of this pencil of empty circle is located at the midpoint of the green Delaunay edge.
In our algorithm, a gate (green Delaunay edge) is said to be not alpha-traversable when the minimum radius of the pencil of empty circle is smaller than alpha.
\cgalFigureCaptionEnd
When traversing from an outside face \f$ f_o \f$ to an inside face \f$ f_i \f$ through an alpha-traversable edge \f$ e \f$,
two criteria are tested to prevent the wrapping process from colliding with the input:
(1) We check for an intersection between the dual Voronoi edge of \f$ e \f$, i.e., the segment between
the circumcenters of the two incident faces, and the offset surface, defined as the level set
of unsigned distance field to the input.
If one or several intersections exists, the first intersection point, along the dual Voronoi edge
oriented from outside to inside is inserted into the triangulation as a Steiner point.
(2) If the dual Voronoi edge does not intersect the offset surface but the neighboring face \f$ f_i \f$
intersects the input, we compute the projection of the circumcenter of \f$ f_i \f$
onto the offset surface, and insert it into the triangulation as a Steiner point (which destroys \f$ f_i \f$).
After each of the above Steiner point insertions, all new incident faces are tagged as inside,
and the newly alpha-traversable gates are pushed into the priority queue.
If none of the above two criteria are met, the neighboring face \f$ f_i \f$ is traversed and tagged as outside.
Alpha-Traversable edges of \f$ f_i \f$ that are separating inside from outside faces are pushed as new gates into the priority queue.
Once the queue empties---a process that is guaranteed as edges (and their circumradii) become smaller
due to the insertion of new Steiner points---the construction phase terminates.
The output (multi)polygon is extracted from the Delaunay triangulation as the set of edges
separating inside from outside faces.
The figure below depicts the steps of the algorithm.
\cgalFigureAnchor{aw2_steps_fig}
\cgalFigureCaptionBegin{aw2_steps_fig}
Steps of the shrink-wrapping algorithm in 2D.
The algorithm is initialized by inserting the corners of the loose bounding box of the input (red)
into a Delaunay triangulation, and all finite triangles are tagged inside (grey).
The current gate (green edge) popped out from the queue is alpha-traversable. The triangle adjacent
to the gate is tagged outside when it does not intersect the input, and new alpha-traversable gates
are pushed to the queue. When the adjacent triangle intersects the input, a new Steiner point (large green disc)
is computed and inserted into the triangulation, all neighboring triangles are tagged inside,
new alpha-traversable gates are pushed to the queue, and traversal is resumed.
Grey edges depict the Delaunay triangulation. Blue edges depict the Voronoi diagram.
Pink circles depict the empty circle of radius alpha. The output edges (dark blue) separate inside from outside triangles.
\cgalFigureCaptionEnd
\subsection aw2_guarantees Guarantees
The algorithm is proven to terminate and to produce a set of 1-manifold, simple polygons
that strictly enclose the input data.
The key element to the proof is that we wrap from outside to inside and never allow a face
that intersects the input to be flagged outside.
Furthermore, both criteria that lead to refinement of the triangulation insert Steiner points
that are guaranteed to break the faces in need of refinement and reduce the neighbor edges' circumradii.
Because the main refinement criterion is the insertion of an intersection between a dual Voronoi edge
and an offset of the input, or the projection of a Voronoi vertex onto the offset of the input,
the algorithm has similarities to popular meshing algorithms based on Delaunay filtering
and refinement (see \ref PkgMesh3).
\section aw2_interface Interface
Our algorithm takes as input a set of segments in 2D, provided either as a segment soup or
as a set of polygons, and two user-defined scalar parameters: the alpha and the offset values.
It proceeds by shrink-wrapping and refining a 2D Delaunay triangulation starting from a loose bounding box of the input.
The parameter alpha refers to the size of cavities or holes that cannot be traversed during wrapping,
and hence to the final level of detail, as alpha acts like a sizing field in a common Delaunay
refinement algorithm (\ref PkgMesh3).
The parameter offset refers to the distance between the vertices of the refined triangulation
and the input, so that a large offset translates into a loose enclosing of the input.
This second parameter offers a means to control the trade-off between tightness and complexity.
The main entry point of the component is the global function CGAL::alpha_wrap_2() that generates the alpha wrap;
this function takes as input a point set, a polyline soup, or a polygon soup.
There is no prerequisite on the input connectivity so that it can take inputs
with islands, self-intersections, or overlaps, as well as combinatorial or geometrical degeneracies.
The underlying traits class must be a model of the AlphaWrapTraits_2 concept. It should use
a floating point number type as inexactness is inherent to the algorithm since there is no closed
form description of new vertices on the offset surface.
The output is a multi polygon, i.e., a set of polygons, whose type is chosen by the user,
under the constraint that it must be a model of the MultipolygonWithHoles_2 concept.
A three-dimensional version of this package is also available: \ref PkgAlphaWrap3.
\section aw2_parameters Choosing Parameters
The two parameters of the algorithm impact both the level of detail and complexity of the output wrap.
\subsection aw2_alpha Alpha
The main parameter, alpha, controls whether a Delaunay edge is traversable during shrink-wrapping.
Alpha's main purpose is to control the size
of the empty balls used during wrapping, and thus to determine which features will appear in the output:
indeed, an edge is alpha-traversable if its circumradius is larger than alpha; hence, the algorithm
can only shrink-wrap through straits or holes with diameters larger than alpha.
A second, less direct consequence is that as long as an edge has a circumradius larger than alpha,
the incident inside face will be visited and possibly refined.
Therefore, when the algorithm terminates, all edges have a circumradius smaller than alpha.
This parameter thus also behaves like a sizing criterion on the edges of the output.
\cgalFigureAnchor{aw2_alpha_param_fig}
\cgalFigureCaptionBegin{aw2_alpha_param_fig}
Impact of the alpha parameter on the output.
(Left) The input segment soup, generated from an SVG file. The input
has many self-intersections, non-manifold vertices, superfluous geometric details
and spurious topological structures.
(Middle & Right) This component approximates the input conservatively and produces valid polygons
with different complexity and fidelity to the input, depending on the alpha parameter.
The smaller the alpha, the deeper the shrink-wrapping process will enter cavities.
The alpha parameter is decreasing from left to right, to respectively 1/20, 1/100, and 1/500 of the longest diagonal of the input bounding box.
A large alpha will produce an output less complex but less faithful to the input.
\cgalFigureCaptionEnd
\subsection aw2_offset Offset
The second parameter, the offset distance, controls the distance from the input and thus the definition
of the offset isosurface onto which the vertices of the output polygon are located.
This parameter controls the tightness of the result, which has, in turn, a few consequences.
Firstly, locating vertices away from the input enables the algorithm to generate
a less complex result, especially in convex areas. A trivial example of this behavior would be a very dense
circle-shaped polygon, for which an as-tight-as-possible envelope would also need to be very dense.
Secondly, the farther the isosurface is from the input, the more new points are inserted
through the first criterion (i.e., through intersection with dual Voronoi edge, see Section \ref aw2_algorithm);
thus, the quality of the output improves in terms of angles of the triangle elements.
Finally, and depending on the value of the alpha parameter, a large offset can also offer defeaturing capabilities.
However, using a small offset parameter will tend to better preserve sharp features as projection
Steiner points tend to project onto convex sharp features.
\cgalFigureAnchor{aw2_steiner_fig}
\cgalFigureCaptionBegin{aw2_steiner_fig}
Steiner points.
The projection Steiner points (green) are computed by projecting the triangle circumcenter onto the offset.
The intersection Steiner points (blue) are computed as the first intersection point between the Voronoi edge and the offset.
(Left) When the offset parameter is small, the algorithm produces more projection Steiner points,
which tends to improve the preservation of convex sharp features.
(Right) When the offset parameter is large, the algorithm produces more intersection Steiner points.
\cgalFigureCaptionEnd
By default, we recommend to set the offset parameter to a small fraction of alpha, such that alpha
becomes the main parameter that controls the final level of detail.
The image below illustrates the impact of both parameters.
\cgalFigureAnchor{aw2_param_grid_fig}
\cgalFigureCaptionBegin{aw2_param_grid_fig}
Different alpha and offset values on the bike model.
The x-axis represents the offset value equal to 1/5000, 1/500, 1/50, and 1/5 of the longest diagonal of the input bounding box, from left to right.
The y-axis represents the alpha value equal to 1/500, 1/50, and 1/5 of the longest diagonal of the input bounding box, from bottom to top.
Depending on the alpha value, an offset too small or too large will produce outputs with higher complexity.
For each alpha, the models with lower complexity can be used as a scale-space representations for collision detection, from near to far distances.
\cgalFigureCaptionEnd
\subsection aw2_two_side A Note on "Two-Sided" Wraps
The offset parameter is crucial to our approach because it guarantees that the output is a
set of 1-manifold, simple polygons.
Indeed, and even when the input is a zero-area structure such as a single segment,
the output wrap is a thin domain enclosing the said segment, see \cgalFigureRef{aw2_inputs_fig}.
Users should keep in mind that the wrapping algorithm performs using an unsigned distance field,
and has no means of determining whether it is acting on both sides of a same input segment.
It will thus produce two-sided wraps in the case of holes in the input and
values of alpha smaller than the size of the holes.
\cgalFigureAnchor{aw2_double_sided_fig}
\cgalFigureCaptionBegin{aw2_double_sided_fig}
Wrapping a Bunny in 2D, with decreasing values for alpha.
When alpha is small enough with respect the diameter of the holes, the algorithm generates a two-sided wrap.
\cgalFigureCaptionEnd
\section aw2_examples Examples
\subsection aw2_examples_polylines Wrapping a Set of Polylines
The following example illustrates how to construct the wrap of a soup of 2D segments.
Alpha is set to 1/20 of the bounding box's longest diagonal edge length,
and offset set to 1/30 of alpha (i.e., 1/600 of the bounding box diagonal edge length).
\cgalExample{Alpha_wrap_2/polyline_wrap_2.cpp}
\subsection aw2_examples_polygons Wrapping a Multipolygon
For polygons, we use multipolygons to represent input sets of polygons (possibly with holes).
The following example uses the \link IOStreamWKT WKT file format\endlink to read such input
and wrap it. Note the possible usage of the \ref PkgPolygonRepair package if a pre-emptive
repair of the polygons is desired.
\cgalExample{Alpha_wrap_2/polygon_wrap_2.cpp}
\subsection aw2_examples_points Wrapping a Point Cloud
Finally, the following example demonstrates how to wrap a 2D point set constructed from the projection
of an input 3D point cloud.
\cgalExample{Alpha_wrap_2/point_set_wrap_2.cpp}
*/
}
Alpha Wrap 2 Doc Alpha Wrap 2 Examples (`Alpha_wrap_2/doc/Alpha_wrap_2/examples.txt`)
/*!
\example Alpha_wrap_2/polygon_wrap_2.cpp
\example Alpha_wrap_2/polyline_wrap_2.cpp
\example Alpha_wrap_2/triangle_soup_wrap_2.cpp
\example Alpha_wrap_2/point_set_wrap_2.cpp
*/
Alpha Wrap 3 Doc Alpha Wrap 3 PackageDescription (`Alpha_wrap_3/doc/Alpha_wrap_3/PackageDescription.txt`)
/// \defgroup PkgAlphaWrap3Ref Reference Manual
/// \defgroup AW3_free_functions_grp Free Functions
/// Functions to create a wrap from point clouds, triangle soups, and triangle meshes.
/// \ingroup PkgAlphaWrap3Ref
/*!
\addtogroup PkgAlphaWrap3Ref
\cgalPkgDescriptionBegin{3D Alpha Wrapping,PkgAlphaWrap3}
\cgalPkgPicture{alpha_wrap_3.jpg}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Pierre Alliez, David Cohen-Steiner, Michael Hemmer, Cédric Portaneri, and Mael Rouxel-Labbé}
\cgalPkgDesc{This component takes a 3D triangle mesh, a triangle soup, or a point set as input, and generates
a valid triangulated surface mesh that strictly contains the input (watertight, intersection-free
and 2-manifold). The algorithm proceeds by shrink-wrapping
and refining a 3D Delaunay triangulation starting from a loose bounding box of the input.
Two user-defined parameters, alpha and offset, offer control over the maximum size
of cavities where the shrink-wrapping process can enter, and the tightness
of the final surface mesh to the input, respectively. Once combined, these parameters
provide a means to trade fidelity to the input for complexity of the output.}
\cgalPkgManuals{Chapter_3D_Alpha_wrapping,PkgAlphaWrap3Ref}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{5.5}
\cgalPkgDependsOn{\ref PkgTriangulation3 and \ref PkgPolygonMeshProcessing}
\cgalPkgBib{cgal:achpr-aw3}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
\cgalCRPSection{Functions}
- \link AW3_free_functions_grp
CGAL::alpha_wrap_3()\endlink
*/
Alpha Wrap 3 Doc Alpha Wrap 3 Alpha Wrap 3 (`Alpha_wrap_3/doc/Alpha_wrap_3/alpha_wrap_3.txt`)
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_3D_Alpha_wrapping
\cgalAutoToc
\authors Pierre Alliez, David Cohen-Steiner, Michael Hemmer, Cédric Portaneri, Mael Rouxel-Labbé
Note: a two-dimensional version of this package is also available: \ref PkgAlphaWrap2.
\section aw3_introduction Introduction
Various tasks in geometric modeling and processing require 3D objects represented as valid surface meshes,
where "valid" refers to meshes that are watertight, intersection-free, orientable, and 2-manifold.
Such representations offer well-defined notions of interior/exterior and geodesic neighborhoods.
3D data are usually acquired through measurements followed by reconstruction, designed by humans,
or generated through imperfect automated processes.
As a result, they can exhibit a wide variety of defects including gaps, missing data,
self-intersections, degeneracies such as zero-volume structures, and non-manifold features.
Given the large repertoire of possible defects, many methods and data structures have been proposed
to repair specific defects, usually with the goal of guaranteeing specific properties in the repaired 3D model.
Reliably repairing all types of defects is notoriously difficult and is often an ill-posed problem
as many valid solutions exist for a given 3D model with defects.
In addition, the input model can be overly complex with unnecessary geometric details,
spurious topological structures, nonessential inner components, or excessively fine discretizations.
For applications such as collision avoidance, path planning, or simulation,
getting an approximation of the input can be more relevant than repairing it.
Approximation herein refers to an approach capable of filtering out inner structures,
fine details and cavities, as well as wrapping the input within a user-defined offset margin.
Given an input 3D geometry, we address the problem of computing a conservative approximation,
where conservative means that the output is guaranteed to strictly enclose the input.
We seek unconditional robustness in the sense that the output mesh should be valid (oriented,
2-manifold, and without self-intersections), even for raw input with many defects
and degeneracies.
The default input is a soup of 3D triangles, but the generic interface leaves the door open
to other types of finite 3D primitives such as triangle soups and point sets.
\cgalFigureAnchor{aw3_input_1_fig}
\cgalFigureCaptionBegin{aw3_input_1_fig}
Shrink-wrapping output from a triangle soup, with many intersections and gaps.
From left to right, input model, output wrap, and superposition.
\cgalFigureCaptionEnd
\cgalFigureAnchor{aw3_input_2_fig}
\cgalFigureCaptionBegin{aw3_input_2_fig}
Dealing with non-manifold features and degeneracies.
From left to right, a non-manifold vertex, self-intersecting faces and two adjacent triangles
representing a zero-volume structure.
The algorithm handles these cases by wrapping an offset of the input.
\cgalFigureCaptionEnd
\section aw3_definition Approach
Many approaches have been devised to enclose a 3D model within a volume, featuring different balances
between the runtime and quality (i.e., tightness) of the approximation.
Within the simplest cases, an axis-aligned or oriented bounding box clearly satisfies some desired properties;
however, the approximation error is uncontrollable and often very large.
Computing the convex hull of the input also matches some of the desired properties
and improves the quality of the result, albeit at the price of increasing the runtime.
However, the approximation remains crude, especially in the case of several components.
The convex hull is, in fact, a special case of alpha shapes (\ref Chapter_3D_Alpha_Shapes).
Mathematically, the alpha shape is a subcomplex of the Delaunay triangulation, with simplicies
being part of the complex depending on the size of their minimal (empty) Delaunay ball.
Intuitively, constructing 3D alpha shapes can be thought of as carving 3D space with an empty ball
of user-defined radius alpha.
Alpha shapes yield provable, good piecewise-linear approximations of a shape \cgalCite{bb-srmua-97t},
but are defined on point sets, whereas we wish to deal with more general input data, such as triangle soups.
Even after sampling the triangle soup, alpha shapes do not guarantee to be conservative for any alpha.
Finally, inner structures are also carved within the volumes, instead of being filtered out.
Inspired by alpha shapes, we replace the above notion of carving by shrink-wrapping:
we iteratively construct a subcomplex of a 3D Delaunay triangulation by starting from
a simple 3D Delaunay triangulation enclosing the input, and then iteratively removing eligible tetrahedra
that lie on the boundary of the complex.
In addition, the underlying triangulation---and thus the complex incidentally---is refined
as shrinking proceeds.
Thus, instead of carving from the convex hull of the input data as in alpha shapes, we construct
an entirely new mesh through a Delaunay refinement-like algorithm. The refinement algorithm inserts Steiner points
on the boundary of an offset volume, defined as a level set of the unsigned distance field to the input.
This process both prevents the creation of inner structures within the output and avoids superfluous computations.
In addition, detaching our mesh construction from the geometry and discretization of the input has several advantages:
(1) the underlying data is not restricted to a specific format (triangle soups, polygon soups, point clouds, etc.)
as all that is required is answering three basic geometric queries: (a) the distance between a point
and the input, (b) the projection of a query point onto the input, (c) an intersection test
between a tetrahedron and the input, and (2) The user has more freedom to trade tightness
to the input for final mesh complexity, as constructing a conservative approximation on a large offset
of the input requires fewer mesh elements.
\subsection aw3_algorithm Algorithm
Initialization. The algorithm is initialized by inserting the eight corner vertices
of a loose bounding box into a 3D Delaunay triangulation.
In the 3D Delaunay triangulation of \cgal, all triangle facets are adjacent to two tetrahedron cells.
Each facet of the boundary of the Delaunay triangulation, which coincides with one facet
of the convex hull of the triangulation vertices, is adjacent to a so-called infinite tetrahedron cell,
an abstract cell connected to the so-called infinite vertex to ensure the aforementioned double-facet adjacency.
Initially, all infinite cells are tagged as outside, and all finite tetrahedron cells are tagged as inside.
Shrink-wrapping. The shrink-wrapping algorithm proceeds by traversing the cells
of the Delaunay triangulation from outside to inside, flood-filling from one cell to its adjacent cell,
and tagging the adjacent cell as outside whenever possible (the term possible is specified later).
Flood filling is implemented via a priority queue of Delaunay triangle facets representing
the traversal between the two adjacent cells of the facet, from outside to inside.
These triangle facets are referred to as gates in the following.
Given an outside cell and its adjacent inside cell, the common facet (i.e., a gate) is said
to be alpha-traversable if its circumradius is larger than the user-defined parameter alpha,
where circumradius refers to the radius of the relating triangle's Delaunay ball.
Intuitively, cavities smaller than alpha are not accessible as their gates are not alpha-traversable.
Initialized by the alpha-traversable gates on the convex hull, the priority queue contains only
alpha-traversable gates and is sorted by decreasing order
of the circumradius of the gate.
Traversal can be seen as a continuous process that advances along dual Voronoi edges of the gates,
with a pencil of empty balls circumscribing the gate.
\cgalFigureAnchor{aw3_pencil_fig}
\cgalFigureCaptionBegin{aw3_pencil_fig}
(Left) Pencil of empty circles (blue) circumscribing a Delaunay edge (green) in a 2D Delaunay triangulation (black).
From the top triangle circumcenter c1 to the bottom triangle circumcenter c2, the dual Voronoi edge denoted by e (doted red) is the trace of centers of the largest circles that are empty of Delaunay vertex.
(Right) The graph corresponding to the left example. The x-axis corresponds to the position of empty circle centers located on the Voronoi edge e, from c1 to c2. The y-axis is the radius value of the corresponding empty circles. In this case, the minimum radius of this pencil of empty circle is located at the midpoint of the green Delaunay edge.
In our algorithm, a gate (green Delaunay edge) is said to be not alpha-traversable when the minimum radius of the pencil of empty circle is smaller than alpha.
\cgalFigureCaptionEnd
When traversing from an outside cell \f$ c_o \f$ to an inside cell \f$ c_i \f$ through an alpha-traversable facet \f$ f \f$,
two criteria are tested to prevent the wrapping process from colliding with the input:
(1) We check for an intersection between the dual Voronoi edge of \f$ f \f$, i.e. the segment between
the circumcenters of the two incident cells, and the offset surface, defined as the level set
of unsigned isosurface to the input.
If one or several intersections exists, the first intersection point, along the dual Voronoi edge
oriented from outside to inside is inserted into the triangulation as a Steiner point.
(2) If the dual Voronoi edge does not intersect the offset surface but the neighboring cell \f$ c_i \f$
intersects the input, we compute the projection of the circumcenter of \f$ c_i \f$
onto the offset surface, and insert it into the triangulation as a Steiner point (which destroys \f$ c_i \f$).
After each of the above Steiner point insertions, all new incident cells are tagged as inside,
and the newly alpha-traversable gates are pushed into the priority queue.
If none of the above two criteria are met, the neighboring cell \f$ c_i \f$ is traversed and tagged as outside.
Alpha-Traversable facets of \f$ c_i \f$ that are separating inside from outside cells are pushed as new gates into the priority queue.
Once the queue empties---a process that is guaranteed as facets (and their circumradii) become smaller
due to the insertion of new Steiner points---the construction phase terminates.
The output triangle surface mesh is extracted from the Delaunay triangulation as the set of facets
separating inside from outside cells.
The figure below depicts the steps of the algorithm in 2D.
\cgalFigureAnchor{aw3_steps_fig}
\cgalFigureCaptionBegin{aw3_steps_fig}
Steps of the shrink-wrapping algorithm in 2D.
The algorithm is initialized by inserting the corners of the loose bounding box of the input (red)
into a Delaunay triangulation, and all finite triangles are tagged inside (grey).
The current gate (green edge) popped out from the queue is alpha-traversable. The triangle adjacent
to the gate is tagged outside when it does not intersect the input, and new alpha-traversable gates
are pushed to the queue. When the adjacent triangle intersects the input, a new Steiner point (large green disc)
is computed and inserted into the triangulation, all neighboring triangles are tagged inside,
new alpha-traversable gates are pushed to the queue, and traversal is resumed.
Grey edges depict the Delaunay triangulation. Blue edges depict the Voronoi diagram.
Pink circles depict the empty circle of radius alpha. The output edges (dark blue) separate inside from outside triangles.
\cgalFigureCaptionEnd
\subsection aw3_guarantees Guarantees
The algorithm is proven to terminate and to produce a 2-manifold triangulated surface mesh
that strictly encloses the input data.
The key element to the proof is that we wrap from outside to inside and never allow a cell
that intersects the input to be flagged outside.
Furthermore, both criteria that lead to refinement of the triangulation insert Steiner points
that are guaranteed to break the cells in need of refinement and reduce the neighbor facets circumradii.
Because the main refinement criterion is the insertion of an intersection between a dual Voronoi edge
and an offset of the input, or the projection of a Voronoi vertex onto the offset of the input,
the algorithm has similarities to popular meshing algorithms based on Delaunay filtering
and refinement (see \ref Chapter_3D_Mesh_Generation).
\section aw3_interface Interface
Our algorithm takes as input a set of triangles in 3D, provided either as a triangle soup or
as a triangle surface mesh, and two user-defined scalar parameters: the alpha and the offset values.
It proceeds by shrink-wrapping and refining a 3D Delaunay triangulation starting from a loose bounding box of the input.
The parameter alpha refers to the size of cavities or holes that cannot be traversed during wrapping,
and hence to the final level of detail, as alpha acts like a sizing field in a common Delaunay
refinement algorithm (\ref Chapter_3D_Mesh_Generation).
The parameter offset refers to the distance between the vertices of the refined triangulation
and the input, so that a large offset translates into a loose enclosing of the input.
This second parameter offers a means to control the trade-off between tightness and complexity.
The main entry point of the component is the global function CGAL::alpha_wrap_3() that generates the alpha wrap;
this function takes as input a polygon soup or a polygon mesh.
There is no prerequisite on the input connectivity so that it can take an arbitrary triangle soup,
with islands, self-intersections, or overlaps, as well as combinatorial or geometrical degeneracies.
The underlying traits class must be a model of the Kernel concept. It should use
a floating point number type as inexactness is inherent to the algorithm since there is no closed
form description of new vertices on the offset surface.
The output is a triangle surface mesh whose type is chosen by the user, under the constraint
that it must be a model of the MutableFaceGraph concept.
\section aw3_parameters Choosing Parameters
The two parameters of the algorithm impact both the level of detail and complexity of the output mesh.
\subsection aw3_alpha Alpha
The main parameter, alpha, controls whether a Delaunay facet is traversable during shrink-wrapping.
Alpha's main purpose is to control the size
of the empty balls used during wrapping, and thus to determine which features will appear in the output:
indeed, a facet is alpha-traversable if its circumradius is larger than alpha; hence, the algorithm
can only shrink-wrap through straits or holes with diameters larger than alpha.
A second, less direct consequence is that as long as a facet has a circumradius larger than alpha,
the incident inside cell will be visited and possibly refined.
Therefore, when the algorithm terminates, all facets have a circumradius smaller than alpha.
This parameter thus also behaves like a sizing criterion on the triangle facets of the output.
\cgalFigureAnchor{aw3_alpha_param_fig}
\cgalFigureCaptionBegin{aw3_alpha_param_fig}
Impact of the alpha parameter on the output.
(Left) The input triangle mesh, generated by surface reconstruction from a raw point cloud,
has many non-manifold edges and vertices, superfluous geometric details and spurious topological structures.
(Right) This component approximates the input conservatively and produces valid meshes with different
complexity and fidelity to the input, depending on the alpha parameter.
The smaller the alpha, the deeper the shrink-wrapping process will enter cavities.
The alpha parameter is decreasing from left to right, to respectively 1/50, 1/100 and 1/300 of the longest diagonal of the input bounding box.
A large alpha will produce an output less complex but less faithful to the input.
\cgalFigureCaptionEnd
\subsection aw3_offset Offset
The second parameter, the offset distance, controls the distance from the input and thus the definition
of the offset isosurface onto which the vertices of the output mesh are located.
This parameter controls the tightness of the result, which has, in turn, a few consequences.
Firstly, locating vertices away from the input enables the algorithm to generate
a less complex mesh, especially in convex areas. A trivial example of this behavior would be a very dense mesh
of a sphere, for which an as-tight-as-possible envelope would also be very dense.
Secondly, the farther the isosurface is from the input, the more new points are inserted
through the first criterion (i.e., through intersection with dual Voronoi edge, see Section \ref aw3_algorithm);
thus, the quality of the output improves in terms of angles of the triangle elements.
Finally, and depending on the value of the alpha parameter, a large offset can also offer defeaturing capabilities.
However, using a small offset parameter will tend to better preserve sharp features as projection
Steiner points tend to project onto convex sharp features.
\cgalFigureAnchor{aw3_offset_param_fig}
\cgalFigureCaptionBegin{aw3_offset_param_fig}
Impact of the offset parameter on the output.
(Left) Input mesh generated by meshing a NURBS CAD model in parameter space.
(Right) The smaller the offset, the closest sample points are to the input.
The offset parameter is decreasing from left to right, to respectively 1/50, 1/200 and 1/1000 of the longest diagonal of the input bounding box.
The alpha parameter is equal to 1/50 of the longest diagonal of the input bounding box for all level of details.
A larger offset will produce an output less complex with better triangle quality.
However, the sharp features (red edges) are well-preserved when the offset parameter is small.
\cgalFigureCaptionEnd
\cgalFigureAnchor{aw3_steiner_fig}
\cgalFigureCaptionBegin{aw3_steiner_fig}
Steiner points.
The projection Steiner points (green) are computed by projecting the triangle circumcenter onto the offset.
The intersection Steiner points (blue) are computed as the first intersection point between the Voronoi edge and the offset.
(Left) When the offset parameter is small, the algorithm produces more projection Steiner points,
which tends to improve the preservation of convex sharp features.
(Right) When the offset parameter is large, the algorithm produces more intersection Steiner points,
which tends to generate triangles with better quality in terms of angles, in 3D.
\cgalFigureCaptionEnd
By default, we recommend to set the offset parameter to a small fraction of alpha, such that alpha
becomes the main parameter that controls the final level of detail.
The image below illustrates the impact of both parameters.
\cgalFigureAnchor{aw3_param_grid_fig}
\cgalFigureCaptionBegin{aw3_param_grid_fig}
Different alpha and offset values on the bike model (533,000 triangles).
The x-axis represents the offset value equal to 1/5000, 1/2000, 1/500, 1/200, 1/50, 1/20 and 1/5 of the longest diagonal of the input bounding box, from left to right.
The y-axis represents the alpha value equal to 1/300, 1/100, 1/50, 1/20 and 1/5 of the longest diagonal of the input bounding box, from bottom to top.
The numbers below each level of detail represents their number of triangles.
Depending on the alpha value, an offset too small or too large will produce output mesh with higher complexity.
For each alpha, the models with lower complexity can be used as a scale-space representations for collision detection, from near to far distances.
\cgalFigureCaptionEnd
\subsection aw3_two_side A Note on "Two-Sided" Wraps
The offset parameter is crucial to our approach because it guarantees that the output is a closed,
2-manifold surface mesh.
Indeed, and even when the input is a zero-volume structure such as a single 3D triangle,
the output wrap is a thin volume enclosing the said triangle \cgalFigureRef{aw3_input_2_fig}.
Users should keep in mind that the wrapping algorithm has no means of determining whether it is acting on the inside or the outside
of the unsigned distance field, and will thus produce two-sided wraps in the case of holes in the input
and values of alpha smaller than the size of the holes.
\cgalFigureAnchor{aw3_double_sided_fig}
\cgalFigureCaptionBegin{aw3_double_sided_fig}
Two-sided wrap.
(Left) Wrapping a Bunny in 2D, with decreasing values for alpha.
(Right) Wrapping a defect-laden Bunny in 3D. The rightmost column depicts a clipped visualization
of the inside. When alpha is small enough with respect the diameter of the holes, the algorithm generates a two-sided wrap.
\cgalFigureCaptionEnd
\section aw3_performance Performance
The charts below plots the computation times of the wrapping algorithm on the Thingi10k dataset, as well as the complexity of the output triangle mesh.
\cgalFigureAnchor{aw3_bench_fig}
\cgalFigureCaptionBegin{aw3_bench_fig}
Execution times and output complexity for different values of alpha on the Thingi10k data set.
Alpha increases from 1/20 to 1/200 of the length of bounding box diagonal.
The x-axis represents the complexity of the output wrap mesh in number of triangle facets.
The y-axis represents the total computation time, in seconds.
The color and diameter of the dots represent the number of faces in the input triangle soup,
ranging from 10 (green) to 3154000 (blue).
\cgalFigureCaptionEnd
\section aw3_examples Examples
Here is an example with an input triangle mesh, with alpha set to 1/20 of the bounding box's longest diagonal edge length,
and offset set to 1/30 of alpha (i.e., 1/600 of the bounding box diagonal edge length).
\cgalExample{Alpha_wrap_3/triangle_mesh_wrap.cpp}
Some triangle soups might not be representable as a mesh due to non-manifoldness or incompatible orientations.
Such triangle soup is nevertheless a valid input for the wrapping algorithm, as illustrated
in the following example.
\cgalExample{Alpha_wrap_3/triangle_soup_wrap.cpp}
Here is an example with a point cloud.
\cgalExample{Alpha_wrap_3/point_set_wrap.cpp}
*/
}
Alpha Wrap 3 Doc Alpha Wrap 3 Examples (`Alpha_wrap_3/doc/Alpha_wrap_3/examples.txt`)
/*!
\example Alpha_wrap_3/triangle_mesh_wrap.cpp
\example Alpha_wrap_3/triangle_soup_wrap.cpp
\example Alpha_wrap_3/point_set_wrap.cpp
*/
Apollonius Graph 2 Doc Apollonius Graph 2 Apollonius Graph 2 (`Apollonius_graph_2/doc/Apollonius_graph_2/Apollonius_graph_2.txt`)
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_2D_Apollonius_Graphs
\anchor chapterapollonius2
\cgalAutoToc
\authors Menelaos Karavelas and Mariette Yvinec
This chapter describes the two-dimensional Apollonius graph
of \cgal. We start with a few definitions in
Section \ref secapollonius2definitions.
The software design of the 2D Apollonius graph package is described
in Section \ref secapollonius2design.
In Section \ref secapollonius2traits we discuss the geometric
traits of the 2D Apollonius graph package and in Section
\ref secapollonius2hierarchy the Apollonius graph hierarchy, a data
structure suitable for fast nearest neighbor queries, is briefly
described.
\section secapollonius2definitions Definitions
\cgalFigureBegin{figapollonius,apollonius_diagram.png,apollonius_graph.png}
The Apollonius diagram (left) and its dual the Apollonius graph (right).
\cgalFigureEnd
The 2D Apollonius graph class of \cgal is designed to compute the
dual of the Apollonius diagram or, as it is also known, the
Additively weighted Voronoi diagram. The algorithm that has been
implemented is dynamic, which means that we can perform insertions and
deletions online. The corresponding \cgal class is calledApollonius_graph_2<ApolloniusGraphTraits_2,ApolloniusGraphDataStructure_2>
and will be discussed in more detail in the sequel. The interested
reader may want to refer to the paper by Karavelas and Yvinec
\cgalCite{cgal:ky-dawvd-02} for the general idea as well as the details of the
algorithm implemented.
Before describing the details of the implementation we make a brief
introduction to the theory of Apollonius diagrams.
The Apollonius diagram is defined over a set of sites
\f$ P_i=(c_i,w_i)\f$, \f$ i=1,\ldots,n\f$, where \f$ c_i\f$ is the point and \f$ w_i\f$
the weight of \f$ P_i\f$. It is a subdivision of the plane into connected
regions, called cells, associated with the sites (see
\cgalFigureRef{figapollonius} (left)). The cell of a
site \f$ P_i\f$ is the locus of points on the plane that are
closer to \f$ P_i\f$ than any other site \f$ P_j\f$, \f$ j\neq i\f$.
The distance \f$ \delta(x, P_i)\f$ of a point \f$ x\f$ in the plane to a
site \f$ P_i\f$ is defined as:
\f[ \delta(x,P_i)=|x-c_i|-w_i, \f]
where \f$ |\cdot|\f$ denotes the Euclidean norm.
It can easily be seen that it is a generalization of the Voronoi
diagram for points, which can actually be obtained if all the weights
\f$ w_i\f$ are equal. Unlike the case of points, however, it is
possible that a site \f$ P_i\f$ might have an empty cell. This
can also happen in the case of the power diagram, whose dual is the
regular triangulation (see Section
\ref Section_2D_Triangulations_Regular ). If this is
the case we call the site hidden (these are the black
circles in \cgalFigureRef{figapollonius} ). A site which is not
hidden will be referred to as visible.
If all weights \f$ w_i\f$ are non-negative, the Apollonius
diagram can be viewed as the Voronoi diagram of the set of circles
\f$ {P_1,\ldots, P_n}\f$, where \f$ c_i\f$ is the center of the circle \f$ P_i\f$
and \f$ w_i\f$ its radius. If the weights are allowed to be negative,
we need to go to 3D in order to explain what the Apollonius diagram
means geometrically. We identify the 2D Euclidean plane with the
\f$ xy\f$-plane in 3D. Then the Voronoi diagram of a set of points can be
seen as the vertical projection on the \f$ xy\f$-plane of the lower
envelope of a set of 3D cones defined as follows: for each point \f$ p\f$
in the set of 2D points we have a cone \f$ C_p\f$ whose apex is the point
\f$ p\f$. The axis of
\f$ C_p\f$ is a line parallel to the \f$ z\f$-axis passing through \f$ p\f$, the
angle of \f$ C_p\f$ is \f$ 45^\circ\f$ and, finally \f$ C_p\f$ is facing in the
positive \f$ z\f$-direction (that is, \f$ C_p\f$ is contained in the positive
\f$ z\f$-halfspace).
The Apollonius diagram corresponds to shifting the
apexes of these cones in the \f$ z\f$-direction by a quantity equal to the
weight. Sites with negative weight will give rise to
cones whose apex is in the negative \f$ z\f$-halfspace and sites
with positive weight will give rise to cones whose apex is in the
positive \f$ z\f$-halfspace. In a manner analogous to the case of points,
the Apollonius diagram can then be defined as the vertical projection
on the \f$ xy\f$-plane of the lower envelope of the set of shifted cones.
Notice that when all apexes are translated along the \f$ z\f$-direction by
the same amount, the projection of the lower envelope of the set of
cones does not change. In particular, we can translate all cones by a
large enough amount so that all apexes are in the positive
\f$ z\f$-halfspace. Algebraically, this means that the Apollonius diagram
does not change if we add to all weights the same quantity, which in
particular, implies that we can assume without loss of generality that
all weights are positive. Given the observations above and in order to
simplify our discussion of Apollonius diagrams, we will, from now on,
assume that all weights are positive, and we will refer to the
sites as circles.
The Apollonius diagram is a planar graph, and so is its dual, the
Apollonius graph. There are many ways to embed it on the plane and one
such way is shown in \cgalFigureRef{figapollonius} (right).
The Apollonius graph is uniquely defined once we have
the Apollonius diagram. If the circles are in general position
(see precise definition below), then the Apollonius graph is a graph
with triangular faces away from
the convex hull of the set of circles (by triangular we mean that
every face has exactly three edges). Near the convex hull we may
have some spikes (i.e., vertices of degree 1). To unify our approach
and handling of the Apollonius graph we add to the set of (finite)
circles a fictitious circle at infinity, which we call the
site at infinity. We can then connect all vertices of the outer
face of the Apollonius graph to the site at infinity which gives us
a graph with the property that all of its faces are now
triangular. However, the Apollonius graph is not a triangulation for
two main reasons: we cannot always embed it on the plane with straight
line segments that yield a triangulation and, moreover, we may have two
faces of the graph that have two edges in common, which is not allowed
in a triangulation. Both of these particularities appear when we
consider the Apollonius graph of the set of circles in
\cgalFigureRef{figapollonius}.
We would like to finish our brief introduction to the theory of
Apollonius graphs by discussing the concept of general position. We say
that a set of circles is in general position if no two triplets of
circles have the same tritangent circle. This statement is rather
technical and it is best understood in the context of points. The
equivalent statement for points is that we have no two triplets of
points that define the same circumcircle, or equivalently that no
four points are co-circular. The statement about general position made
above is a direct generalization of the (much simpler to understand)
statement about points. On the contrary, when we have circles in
degenerate position, the Apollonius graph has faces with more than
three edges on their boundary. We can get a triangulated version of
the graph by simply triangulating the corresponding faces in an
arbitrary way. In fact the algorithm that has been implemented in
\cgal has the property that it always returns a valid
triangulated version of the Apollonius graph. By valid we mean
that it contains the actual Apollonius graph (i.e., the actual dual of
the Apollonius diagram) and whenever there are faces with more than
three faces then they are triangulated. The way that they are
triangulated depends on the order of insertion and deletion of the
circles in the diagram.
One final point has to be made about hidden circles. First of all we
would like to be more precise about our definition of hidden circles:
we say that a circle is hidden if its cell has empty interior. This
definition allows us to guarantee that all visible circles have
cells that are two-dimensional regions.
Geometrically the fact that a circle is hidden means that it is
contained in the closure of the disk of another circle (see again
\cgalFigureRef{figapollonius} ). Note that a circle contained in the union
of several disks, but not in the closure of any one of them, is not
hidden.
Hidden circles pose an additional
difficulty to our algorithm and software design. Since we allow
circles to be inserted and deleted at will, it is possible that a
circle that was hidden at some point in time, may become visible at
a later point in time; for example this can happen if we delete the
circle that hides it. For this purpose we store hidden circles and
have them reappear when they become visible. We will discuss this
issue in detail below. For the time being it suffices to say that the
user has the ability to control this behavior. More specifically it is
possible to discard the circles that become hidden. This choice is
totally natural when for example we expect to do only insertions,
since in this case a circle that becomes hidden will never
reappear. On the other hand if deletions are expected as well, then we
lose the ability to have the hidden circles reappear.
Degenerate Dimensions.
The dimension of the Apollonius graph is in general 2. The exceptions
to this rule are as follows:
\section secapollonius2design Software Design
The 2D Apollonius graph classApollonius_graph_2<ApolloniusGraphTraits_2,ApolloniusGraphDataStructure_2>
follows the design of the triangulation packages of \cgal. It is
parametrized by two arguments:
Storing Hidden Sites.
As we have already mentioned a circle is hidden if it is contained
inside some visible circle. This creates a parent-child relationship
between visible and hidden circles: the parent of a hidden circle is the
visible circle that contains it. If more than one visible circles
contain a hidden circle then the hidden circle can be assigned to any of
the visible circles arbitrarily.
To store hidden circles we assign to every visible circle a list. This
list comprises the hidden circles that are contained in the
visible circle. The user can access the hidden circles associated with
a visible circle through an iterator calledHidden_sites_iterator. This iterator is defined in theApolloniusGraphVertexBase_2 concept and is implemented by its
model, the Apollonius_graph_vertex_base_2<Gt,StoreHidden>
class. It is also possible to iterate through the entire set of hidden
sites using a homonymous iterator defined by theApollonius_graph_2<Gt,Agds> class.
Since storing hidden sites may not be of interest in some cases (e.g.,
for example this is the case if we only perform insertions in the
Apollonius graph), the user has the possibility of controlling this
behavior. More precisely, the classApollonius_graph_vertex_base_2<Gt,StoreHidden> has two template
parameters, the second of which is a Boolean value. This value is by
default true and it indicates that hidden sites should be
stored. The user can indicate that hidden sites may be discarded
by setting this value to false.
\section secapollonius2traits The Geometric Traits
The predicates required for the computation of the Apollonius graph
are rather complicated. It is not the purpose of this document to
discuss them in detail. The interested reader may refer to the papers
by Karavelas and Emiris for the details
\cgalCite{cgal:ke-ppawv-02}, \cgalCite{cgal:ke-rctac-03}. However, we would like to give a brief
overview of what they
compute. There are several predicates needed by this algorithm. We
will discuss the most important/complicated ones. It turns out that
it is much easier to describe them in terms of the Apollonius diagram,
rather than the Apollonius graph. Whenever it is applicable we will also
describe their meaning in terms of the Apollonius graph.
The first two geometric predicates are calledIs_hidden_2 and Oriented_side_of_bisector_2. The first one
involves two circles, say \f$ P_1\f$ and \f$ P_2\f$. It determines if \f$ P_1\f$ is
hidden with respect to \f$ P_2\f$; more precisely it checks whether the
circle \f$ P_1\f$ is contained in the closure of the disk defined by the
circle \f$ P_2\f$. As its name indicates, it determines if a circle is
hidden or not. The second predicate involves two circles \f$ P_1\f$ and
\f$ P_2\f$ and a point \f$ q\f$. It answers the question whether \f$ q\f$ is closer
to \f$ P_1\f$ or \f$ P_2\f$. Its name stems from the fact that answering the
aforementioned question is equivalent to determining the oriented
side of the bisector of \f$ P_1\f$ and \f$ P_2\f$ that contains the query point
\f$ q\f$. This predicate is used by the algorithm for closest neighbor
queries for points.
The next geometric predicate is called Vertex_conflict_2 and it
involves four circles \f$ P_1\f$, \f$ P_2\f$, \f$ P_3\f$, and \f$ P_4\f$ (see
\cgalFigureRef{figag2vc} ). The first three (red circles in
\cgalFigureRef{figag2vc} ) define a tritangent circle (yellow
circle in \cgalFigureRef{figag2vc} ). What we want to determine is
the sign of the distance of the green circle from the yellow
circle. The distance between two circles \f$ K_1=(c_1,r_1)\f$ and
\f$ K_2=(c_2, r_2)\f$ is defined as the distance of their centers minus
their radii:
\f[ \delta(K_1, K_2) = |c_1-c_2|-r_1-r_2. \f]
This predicate determines if a vertex in the Apollonius diagram
(the center of the yellow circle) is destroyed when a new circle is
inserted in the diagram (the green circle). In the Apollonius graph
it tells us if a triangular face of the diagram is to be destroyed or
not.
\cgalFigureBegin{figag2vc,apollonius-vertex_conflict-false.png,apollonius-vertex_conflict-true.png}
The Vertex_conflict_2 predicate. The left-most, bottom-most and top-most circles define the tritangent circle in the middle. We want to determine the sign of the distance of the left-most circle from the one in the middle. The almost horizontal curve is the bisector of the top-most and bottom-most circles. Left: the predicate returns NEGATIVE. Right: the predicate returns POSITIVE.
\cgalFigureEnd
What we essentially want to compute when we construct incrementally a
Voronoi diagram, is whether the object to be inserted destroys an edge
of the Voronoi diagram or not. In the case of points this is really
easy and it amounts to the well-known incircle test.
In the case
of circles the situation is more complicated. We can have six possible
outcomes as to what portion of an edge of the Apollonius diagram the
new circle destroys (see \cgalFigureRef{figag2edgeconflict} ). The first
two can be answered directly by the Vertex_conflict_2 predicate
evaluated for the two endpoints of the Apollonius diagram edge. This
is due to the fact that the value of the Vertex_conflict_2
predicate is different for the two endpoints. If the two values are
the same then we need an additional test which determines if the interior
of the Apollonius diagram edge is destroyed by the new circle. This is
what the Finite_edge_interior_conflict_2 andInfinite_edge_interior_conflict_2 predicates do. In essence, it
is the same predicate (same idea) applied to two different types of
edges in the Apollonius diagram: a finite or an infinite edge. An edge
is infinite if its dual edge in the Apollonius graph connects the
site at infinity with the vertex corresponding to a (finite) circle;
otherwise it is a finite edge.
\cgalFigureAnchor{figag2edgeconflict}
| \image html ./apollonius-left_vertex.png
\image latex ./apollonius-left_vertex.png | \image html ./apollonius-right_vertex.png
\image latex ./apollonius-right_vertex.png |
| --- | --- |
| \image html ./apollonius-no_conflict.png
\image latex ./apollonius-no_conflict.png | \image html ./apollonius-entire_edge.png
\image latex ./apollonius-entire_edge.png |
| \image html ./apollonius-interior.png
\image latex ./apollonius-interior.png | \image html ./apollonius-both_vertices.png
\image latex ./apollonius-both_vertices.png |
\cgalFigureCaptionBegin{figag2edgeconflict}
The 6 possible outcomes of theFinite_edge_interior_conflict_2 predicate. Top left: only a
neighborhood around the left-most endpoint of the edge will be
destroyed. Top right: only a neighborhood around the right-most
endpoint of the edge will be destroyed. Middle left: no portion of the
edge is destroyed. Middle right: the entire edge will be
destroyed. Bottom left: a neighborhood in the interior of the edge
will be destroyed; the regions near the endpoints remain
unaffected. Bottom right: The neighborhood around the two endpoints
will be destroyed, but an interval in the interior of the edge will
remain in the new diagram.
\cgalFigureCaptionEnd
The last predicate that we want to discuss is calledIs_degenerate_edge_2. It tells us whether an edge in the
Apollonius diagram is degenerate, that is if its two endpoints
coincide. In the Apollonius graph such an edge corresponds to one of
the additional edges that we use to triangulate the non-triangular
faces.
The aforementioned predicates are part of theApolloniusGraphTraits_2 concept of \cgal. \cgal also provides
a model for this concept, theApollonius_graph_traits_2<K,Method_tag> class. The first
template parameter of this class must be a model of the Kernel
concept. The second template parameter is a tag that indicates what
operations are allowed in the computations that take place within the
traits class.
The two possible values of the Method_tag parameter areIntegral_domain_without_division_tag and Field_with_sqrt_tag. WhenIntegral_domain_without_division_tag is used, only ring operations are used during the
evaluation of the predicates, whereas if Field_with_sqrt_tag is
chosen, all four field operations, as well as square roots, are used
during the predicate evaluation.
The Apollonius_graph_traits_2<K,Method_tag> class provides exact
predicates if the number type in the kernel K is an exact number
type. This is to be associated with the type of operations allowed for
the predicate evaluation. For example MP_Float as number
type, with Integral_domain_without_division_tag as tag will give exact predicates,
whereas MP_Float with Field_with_sqrt_tag will give
inexact predicates.
Although exact number types provide exact predicates and constructions,
their use often results in unacceptably large runtimes.
The class Apollonius_graph_filtered_traits_2<CK,CM,EK,EM,FK,FM>
aims to paliate this shortcoming. Similar to a filtered kernel, it takes a
constructions kernel CK, a filtering kernel FK and an
exact kernel EK, as well as the corresponding tags
(CM, FM and EM, respectively).
Predicates are evaluated by first using the filtering kernel, and
if this fails the evaluation is performed using the exact kernel,
thus yielding exact predicates at a generally much cheaper cost
than directly using an exact number type. The constructions
are done using the kernel CK, which means that
they are not necessarily exact. All template parameters exceptCK have default values, which are explained in the reference
manual.
\section secapollonius2hierarchy The Apollonius Graph Hierarchy
The Apollonius_graph_hierarchy_2<ApolloniusGraphTraits_2,ApolloniusGraphDataStructure_2> class is nothing but the equivalent of the Triangulation_hierarchy_2
class, applied to the Apollonius graph. It consists of a series of
Apollonius graphs constructed in a manner analogous to the Delaunay
hierarchy by Devillers \cgalCite{d-iirdt-98}. The classApollonius_graph_hierarchy_2<ApolloniusGraphTraits_2,ApolloniusGraphDataStructure_2>
has exactly the same interface and functionality as theApollonius_graph_2<ApolloniusGraphTraits_2,ApolloniusGraphDataStructure_2>
class. Using the Apollonius graph hierarchy involves an additional
cost in space and time for maintaining the hierarchy. Our experiments
have shown that it usually pays off to use the hierarchy for inputs
consisting of more than 1,000 circles. This threshold holds for both
the construction of the Apollonius diagram itself, as well as for
nearest neighbor queries.
\section secapollonius2examples Examples
\subsection Apollonius_graph_2FirstExample First Example
\cgalExample{Apollonius_graph_2/ag2_exact_traits.cpp}
\subsection Apollonius_graph_2SecondExample Second Example
\cgalExample{Apollonius_graph_2/ag2_exact_traits_sqrt.cpp}
\subsection Apollonius_graph_2ThirdExample Third Example
\cgalExample{Apollonius_graph_2/ag2_filtered_traits_no_hidden.cpp}
\subsection Apollonius_graph_2FourthExample Fourth Example
\cgalExample{Apollonius_graph_2/ag2_hierarchy.cpp}
/
} / namespace CGAL */
Apollonius Graph 2 Doc Apollonius Graph 2 PackageDescription (`Apollonius_graph_2/doc/Apollonius_graph_2/PackageDescription.txt`)
/// \defgroup PkgApolloniusGraph2Ref Reference Manual
/// \defgroup PkgApolloniusGraph2Concepts Concepts
/// \ingroup PkgApolloniusGraph2Ref
/*!
\addtogroup PkgApolloniusGraph2Ref
\cgalPkgDescriptionBegin{2D Apollonius Graphs (Delaunay Graphs of Disks),PkgApolloniusGraph2}
\cgalPkgPicture{CircleVoronoi.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Menelaos Karavelas and Mariette Yvinec}
\cgalPkgDesc{Algorithms for computing the Apollonius graph in two dimensions. The Apollonius graph is the dual of the Apollonius diagram, also known as the additively weighted Voronoi diagram. The latter can be thought of as the Voronoi diagram of a set of disks under the Euclidean metric, and it is a generalization of the standard Voronoi diagram for points. The algorithms provided are dynamic.}
\cgalPkgManuals{Chapter_2D_Apollonius_Graphs,PkgApolloniusGraph2Ref}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{3.0}
\cgalPkgDependsOn{\ref PkgTDS2}
\cgalPkgBib{cgal:ky-ag2}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgDemo{2D Apollonius Graph,apollonius_graph_2.zip}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
An Apollonius graph is the dual of the Apollonius diagram, also known
as the additively weighted Voronoi diagram. It is essentially the
Voronoi diagram of a set of disks, where the distance of a
point of the plane from a disk is defined as the Euclidean
distance of the point and the center of the circle, minus the radius
of the disk.
\cgal provides the class CGAL::Apollonius_graph_2<Gt,Agds> for
computing the 2D Apollonius graph. The two template parameters must be
models of the ApolloniusGraphTraits_2 andApolloniusGraphDataStructure_2 concepts. The first concept is
related to the geometric objects and predicates associated with
Apollonius graphs, whereas the second concept refers to the data
structure used to represent the Apollonius graph. The classesApollonius_graph_traits_2<K,Method_tag> andTriangulation_data_structure_2<Vb,Fb> are models of the
aforementioned concepts.
\cgalClassifedRefPages
\cgalCRPSection{Concepts}
ApolloniusSite_2ApolloniusGraphTraits_2ApolloniusGraphDataStructure_2ApolloniusGraphVertexBase_2ApolloniusGraphHierarchyVertexBase_2
\cgalCRPSection{Classes}
CGAL::Apollonius_graph_2<Gt,Agds>CGAL::Apollonius_site_2<K>CGAL::Apollonius_graph_traits_2<K,Method_tag>CGAL::Apollonius_graph_filtered_traits_2<CK,CM,EK,EM,FK,FM>CGAL::Apollonius_graph_vertex_base_2<Gt,StoreHidden>CGAL::Apollonius_graph_hierarchy_2<Gt,Agds>CGAL::Apollonius_graph_hierarchy_vertex_base_2<Agvb>
*/
Apollonius Graph 2 Doc Apollonius Graph 2 Examples (`Apollonius_graph_2/doc/Apollonius_graph_2/examples.txt`)
/*!
\example Apollonius_graph_2/ag2_exact_traits.cpp
\example Apollonius_graph_2/ag2_exact_traits_sqrt.cpp
\example Apollonius_graph_2/ag2_filtered_traits_no_hidden.cpp
\example Apollonius_graph_2/ag2_hierarchy.cpp
*/
Arrangement On Surface 2 Doc Arrangement On Surface 2 Arrangement On Surface 2 (`Arrangement_on_surface_2/doc/Arrangement_on_surface_2/Arrangement_on_surface_2.txt`)
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_2D_Arrangements
\anchor chapterArrangement_on_surface_2
\cgalAutoToc
\authors Ron Wein, Eric Berberich, Efi Fogel, Dan Halperin,
Michael Hemmer, Oren Salzman, and Baruch Zukerman
\section aos_sec-intro Introduction
Geometric arrangements, or arrangements for short, are subdivisions of
some space induced by geometric objects.
\cgalFigureRef{aos_fig-simple_arr} shows an arrangement of two curves
\f$c_1\f$ and \f$c_2\f$ in the plane. It has three faces—two
bounded faces \f$f_1\f$ and \f$f_2\f$ (filled with diagonal-stripe
patterns) and an unbounded face. The arrangement has seven
vertices—four represent the endpoints of \f$c_1\f$ and \f$c_2\f$
(drawn as small discs), and three represent the intersection points of
the two curves (drawn as small rings). The arrangement also has eight
edges, each of which is a maximal portion of one curve not
intersecting the other.
\cgalFigureBegin{aos_fig-simple_arr,simple_arr.png}
\cgalFigureEnd
Arrangements are not restricted to curves in the plane. There are
useful arrangements in three and higher dimensions (these are not so
easy to visualize) and they can be induced by geometric objects of any
type, such as spheres, simplices, polytopes, or Bézier
surfaces. This package provides a data structure that represents a
two-dimensional arrangement of curves embedded in an orientable
parametric surface in three dimensional space, such as a plane, a
cylinder, a sphere, a torus, or a surface homeomorphic to them. This
package also provides operations that construct and manipulate such
arrangements. Arrangements are ubiquitous in the
computational-geometry literature and have many applications; see,
e.g., \cgalCite{as-aa-00}, \cgalCite{cgal:bfhks-apsca-10},
\cgalCite{cgal:bfhmw-apsgf-10}, and \cgalCite{cgal:h-a-04}.
\subsection aos_ssec-intro-sep Separation of Topology and Geometry
The use of the generic programming paradigm enables a convenient
separation of the topology and the geometry of data
structures.\cgalFootnote{In this context, we sometimes say
combinatorics instead of topology, and say algebra or
numerics instead of geometry. We always mean the same
thing---the separation of the abstract, graph-like structure (the
topology) from the actual embedding in the plane (the geometry).}
This is a key aspect in the design of geometric software, and is put
into practice, for example, in the design of \cgal polyhedra, \cgal
triangulations, and our \cgal arrangements. This separation allows the
convenient abstraction of algorithms and data structures in
combinatorial and topological terms, regardless of the specific
geometry of the objects at hand. This abstraction is realized through
class and function templates that represent specific data structures
and algorithmic frameworks, respectively. Consider the class template
\code{.cpp}
template <typename GeometryTraits, typename Dcel>
class Arrangement_2 { ... };
\endcode
An instance of this template represents an arrangement embedded in the
plane. When the template is instantiated, the GeometryTraits
parameter must be substituted by a type that defines a set of
geometric-object types, such as point and curve, and a set of
operations on objects of these types (see Section \ref
aos_sec-geom_traits); the Dcel parameter must be substituted by a
type that represents a doubly-connected edge list (\dcel) data
structure. It defines types of topological objects, such as vertices,
edges, and faces, and the operations required to maintain the
incidence relations among objects of these types (see Section \ref
aos_ssec-basic-dcel).
The class template Arrangement_2 derives from the following class
template:
\code{.cpp}
template <typename GeometryTraits, typename TopologyTraits>
class Arrangement_on_surface_2 { ... };
\endcode
An instance of this template represents a two-dimensional arrangement
embedded in a surface in three dimensional space. When the template is
instantiated, the GeometryTraits parameter must be substituted as
described above; the TopologyTraits parameter must be substituted
by a type that deals with the topology of the surface (see Section
\ref aos_sec-topol_traits). In particular, it maintains a
representation of the arrangement graph embedded in the surface using
a doubly-connected edge list (\dcel) data-structure suitable for
the particular topology.
Several member functions and nested types defined in theArrangement_2 derived class-template inherit their definitions from
the base class-template Arrangement_on_surface_2; their semantics is
equivalent in both class templates. The names of these member
functions and nested types typically appear in the manual without any
scope, as each of these class templates can serve as their scope. (As
a matter of fact, the package provides additional class templates that
represent two-dimensional arrangements, such as theArrangement_with_history_2 class template, which derives from the
class template Arrangement_2; these additional class templates also
contain inherited definitions of the aforementioned member functions
and nested types.)
An immediate advantage of the separation of the topology and the
geometry of data structures is that users with limited expertise in
computational geometry can employ the data structure with their own
special type of objects. They must, however, supply the relevant
traits class, which mainly involves algebraic computations. A traits
class also encapsulates the number types used to represent coordinates
of geometric objects and to carry out algebraic operations on them. It
encapsulates the type of coordinate system used (e.g., %Cartesian and
homogeneous), and the geometric or algebraic computation methods
themselves. The precise minimal sets of requirements the actual traits
classes must conform to are organized as a hierarchy of concepts; see
Section \ref aos_sec-geom_traits.
\subsection aos_ssec-intro-well_behaved Well-Behaved Curves
What constitutes valid curves that can be handled by the \ref
PkgArrangementOnSurface2 package is discussed in detail in Section
\ref aos_sec-geom_traits, where the models of the traits classes are
described. However, when we cite combinatorial complexity bounds or
bounds on the resources (i.e., running time and storage space)
required by algorithms, we often postulate stricter assumptions on the
input curves. The prevalent term in use is that the curves are
well behaved, which may have different interpretations in
different settings. If we are concerned with combinatorial complexity
bounds for curves embedded in a two-dimensional surface, then the
standard assumptions are that (i) each curve is non-self-intersecting
(a so-called Jordan arc) and (ii) every pair of curves intersects in
at most some constant number of points. For algorithmic purposes we
need to require more since we assume that any operation on a small
constant number of curves takes unit time. In this sense arcs of
algebraic curves of degree bounded by a constant (namely the zero set
of bivariate polynomials of constant maximum total degree) are well
behaved. Naturally, what are typically considered well-behaved
surfaces in \f$\mathbb{R}^3\f$ is even more complicated to state.
Remarks
- From the complexity-bound perspective, most of the arrangements that we can deal with can be regarded as defined by well-behaved curves. Even though the package allows for self-intersecting curves, for most types each curve can be decomposed into a constant number of well-behaved curves, thus having no effect on the asymptotic bounds that we state.
- One type of curves that we deal with is special in this sense: *polylines*, namely concatenations of an unlimited number of line segments; see Section \ref arr_sssectr_polylines. A polyline is not well behaved, as it cannot be decomposed into a constant number of constant-descriptive complexity subcurves. Informative bounds for arrangements of polylines are expressed by other parameters in addition to the number of polylines, for example, the total number of segments in all the polylines together. The same holds for the more general type *polycurve*, which are piecewise curves that are not necessarily linear; see Section \ref arr_sssectr_polycurves.
\subsection aos_ssec-intro-outline Outline
In Section \ref aos_sec-basic we provide the minimum material you need
to know in order to use \cgal 2D arrangements in the plane. In Section
\ref aos_sec-curved_surfaces we provide additional material you need
to know in order use \cgal 2D arrangements embedded in curved
surfaces. Most of the succeeding material is oblivious to the type of
the embedding surface. In Section \ref arr_secqueries we show how
queries on an arrangement can be issued. In Section \ref
arr_secgl_funcs we review some useful free (global) functions that
operate on arrangements, the most important ones being the free
insertion-functions. In Section \ref aos_sec-unbounded we explain how
to construct and manipulate arrangements of unbounded curves. Section
\ref aos_sec-geom_traits contains detailed descriptions of the
geometric traits concept hierarchy and the various geometric traits
classes included in the \ref PkgArrangementOnSurface2 package. The
different traits classes enables the construction and manipulation of
arrangements of different families of curves. Naturally, here, the
embedding surface plays a significant role. In Section \ref
arr_ssecnotif we review the notification mechanism that allows
external classes to keep track of the changes that an arrangement
instance goes through. Section \ref arr_ssecex_dcel explains how to
extend the \dcel records, to store extra data with them, and to
efficiently update this data. In Section \ref arr_ssecoverlay we
introduce the fundamental operation of overlaying two arrangements.
Section \ref arr_ssecarr_with_hist describes a class-template that
extends the arrangement by storing additional history records with its
curves. In Section \ref aos_sec-io we review the arrangement
input/output functions. In Section \ref aos_sec-bgl we describes how
to apply the graph algorithms implemented in the Boost Graph Library
to arrangement types. Finally, in Section \ref aos_sec-tips we provide
some tips that can be applied to expedite computation.
\section aos_sec-basic Basic Arrangements
We start with a formal definition of two-dimensional arrangements, and
proceed with an introduction to the data structure used to represent
the incidence relations among features of two-dimensional
arrangements, namely, the doubly-connected edge list, or
\dcel for short. In Section \ref aos_ssec-basic-arr_class we
describe a central component in the \ref PkgArrangementOnSurface2
package, namely, the Arrangement_2 class-template, which can be used
to represent arrangements in the plane.
\subsection aos_ssec-basic-dcel Representation of Arrangements: The Dcel
Given a set \f$\mathcal{C}\f$ of curves embedded in a two-dimensional
surface, the arrangement \f$\mathcal{A}(\mathcal{C})\f$ is
the subdivision of the surface into zero-dimensional, one-dimensional
and two-dimensional cells,\cgalFootnote{We use the term cell
to describe the various dimensional entities in the induced
subdivision. Sometimes, the term face is used for this
purpose in the literature. However, we use the term face to
describe a two-dimensional cell.} called vertices,
edges and faces, respectively, induced by the curves
in \f$\mathcal{C}\f$.
In our implementation we use a definition that slightly deviates from
the standard definition above for practical reasons. The curves in
\f$\mathcal{C}\f$ can intersect each other (a single curve may also be
self-intersecting or may comprise several disconnected branches) and
are not necessarily \f$x\f$-monotone.\cgalFootnote{A continuous planar
curve \f$c\f$ is \f$x\f$-monotone if every vertical line
intersects it at most once. For example, a non-vertical line segment
is always \f$x\f$-monotone and so is the graph of any continuous
function \f$y = f(x)\f$. For convenience, we treat vertical line
segments as weakly \f$x\f$-monotone, as there exists a single
vertical line that overlaps them. A circle of radius \f$r\f$ centered
at \f$(x_0, y_0)\f$ is not \f$x\f$-monotone, as the vertical line \f$x
= x_0\f$ intersects it at \f$(x_0, y_0 - r)\f$ and at \f$(x_0, y_0 +
r)\f$.} We construct a collection \f$\mathcal{C}''\f$ of
\f$x\f$-monotone subcurves that are pairwise disjoint in their
interiors in two steps as follows. First, we decompose each curve in
\f$\mathcal{C}\f$ into maximal \f$x\f$-monotone subcurves and possibly
isolated points, obtaining the collection \f$\mathcal{C}'\f$. Note
that an \f$x\f$-monotone curve cannot be self-intersecting. Then, we
decompose each curve in \f$\mathcal{C}'\f$ into maximal connected
subcurves not intersecting any other curve (or point) in
\f$\mathcal{C}'\f$ in its interior. The collection \f$\mathcal{C}''\f$
contains isolated points, if the collection \f$\mathcal{C}'\f$
contains such points. The arrangement induced by the collection
\f$\mathcal{C}''\f$ can be conveniently embedded as a planar graph,
the vertices of which are associated with curve endpoints or with
isolated points, and the edges of which are associated with
subcurves. It is easy to see that the faces of
\f$\mathcal{A}(\mathcal{C})\f$ are the same as the faces of
\f$\mathcal{A}(\mathcal{C}'')\f$. There are possibly more vertices in
\f$\mathcal{A}(\mathcal{C}'')\f$ than in
\f$\mathcal{A}(\mathcal{C})\f$---the vertices where curves were cut
into \f$x\f$-monotone (non-intersecting) pieces; accordingly there may
also be more edges in \f$\mathcal{A}(\mathcal{C}'')\f$. This graph can
be represented using a doubly-connected edge list
data-structure (\dcel), which consists of containers of vertices,
edges and faces and maintains the incidence relations among these
cells. It is one of a family of combinatorial data structures called
halfedge data structures (Hds),
which are edge-centered data structures capable of maintaining
incidence relations among cells of, for example, planar subdivisions,
polyhedra, or other orientable, two-dimensional surfaces embedded in a
space of arbitrary dimension. Geometric interpretation is added by
classes built on top of the halfedge data structure.
\cgalAdvancedBegin
The \f$x\f$-monotone curves of an arrangement are embedded in a
rectangular two-dimensional area called the parameter space. The
parameter space is defined as \f$X \times Y\f$, where \f$X\f$ and
\f$Y\f$ are open, half-open, or closed intervals with endpoints in the
compactified real line \f$\mathbb{R} \cup {-\infty,+\infty}\f$. Let
\f$x_{\rm min}\f$, \f$x_{\rm max}\f$, \f$y_{\rm min}\f$, and
\f$y_{\rm max}\f$ denote the endpoints of \f$X\f$ and \f$Y\f$,
respectively. We typically refer to these values as the left, right,
bottom, and top sides of the boundary of the parameter space. If the
parameter space is, for example, the entire compactified plane, as in
the case of arrangements in the plane,
\f$x_{\rm min} = y_{\rm min} = -\infty\f$ and
\f$x_{\rm max} = y_{\rm max} = +\infty\f$; see Section \ref
aos_sec-curved_surfaces for more details.
\cgalAdvancedEnd
The \dcel data-structure represents each edge using a pair of
directed halfedges, one going from the
\f$xy\f$-lexicographically smaller (left) endpoint of the curve
towards the \f$xy\f$-lexicographically larger (right) endpoint, and
the other, known as its twin halfedge, going in the opposite
direction. As each halfedge is directed, it has a source
vertex and a target vertex. Halfedges are used to separate
faces, and to connect vertices, with the exception of isolated
vertices (representing isolated points), which are disconnected.
If a vertex \f$v\f$ is the target of a halfedge \f$e\f$, we say that
\f$v\f$ and \f$e\f$ are incident to each other. The halfedges
incident to a vertex \f$v\f$ form a circular list sorted in a
clockwise order around this vertex. (An isolated vertex has no
incident halfedges.)
An edge of an arrangement is a maximal portion of a curve
between two vertices of the arrangement. Each edge is represented in
the \dcel by a pair of twin halfedges. Each halfedge \f$e\f$
stores a pointer to its incident face, which is the face lying
to its left. Moreover, every halfedge is followed by another halfedge
sharing the same incident face, such that the target vertex of the
halfedge is the same as the source vertex of the next halfedge. The
halfedges around faces form circular chains, such that all halfedges
of a chain are incident to the same face and wind along its boundary.
We call such a chain a connected component of the boundary, or
CCB for short.
The unique CCB of halfedges winding in a counterclockwise orientation
along a face boundary is referred to as the outer CCB of the
face. For the time being let us consider only (i) arrangements of
bounded curves, such that exactly one unbounded face exists in every
arrangement, and (ii) arrangements embedded in the plane, such that
every face has at most one outer CCB. The unbounded face does not have
an outer boundary. Any other connected component of the boundary of
the face is called a hole, or inner CCB, and can be
represented as a circular chain of halfedges winding in a clockwise
orientation around it. Note that a hole does not necessarily
correspond to a face at all, as it may have no area, or alternatively
it may contain several faces. Every face can have several inner CCBs
in its interior, or may not contain inner CCBs at all. In addition,
every face may contain isolated vertices in its interior. We
distinguish between isolated vertices and holes even though,
theoretically, the former are degenerate holes. See
\cgalFigureRef{aos_fig-arr_segs} for an illustration of the various
\dcel features. For more details on the \dcel data structure see
\cgalCite{bkos-cgaa-00} Chapter 2.
\cgalFigureBegin{aos_fig-arr_segs,arr_segs.png}
An arrangement of interior-disjoint line segments with some of the
\dcel records that represent it. The unbounded face \f$f_0\f$ has
a single connected component that forms a hole inside it, and this
hole comprises of several faces. The halfedge \f$e\f$ is directed
from its source vertex \f$v_1\f$ to its target vertex \f$v_2\f$.
This edge, together with its twin \f$e'\f$, correspond to a
line segment that connects the points associated with \f$v_1\f$ and
\f$v_2\f$ and separates the face \f$f_1\f$ from \f$f_2\f$. The
predecessor \f$e_{\rm prev}\f$ and successor \f$e_{\rm next}\f$ of
\f$e\f$ are part of the chain that form the outer boundary of the
face \f$f_2\f$. The face \f$f_1\f$ has a more complicated structure
as it contains two holes in its interior: One hole consists of two
adjacent faces \f$f_3\f$ and \f$f_4\f$, while the other hole
comprises of two edges. \f$f_1\f$ also contains two isolated vertices
\f$u_1\f$ and \f$u_2\f$ in its interior.
\cgalFigureEnd
\subsection aos_ssec-basic-arr_class The Arrangement Class Template
One of the main components of the \ref PkgArrangementOnSurface2
package is the Arrangement_2 class template. An
instance of this template is used to represent an arrangement embedded
in the plane. The class template provides the interface needed to
construct such arrangements, traverse them, and maintain them.
The design of the \ref PkgArrangementOnSurface2 package is guided by
two aspects of modularity as follows:
The latter separation is exhibited by the two template parameters of
the Arrangement_2 class template; their description follows.
In this section we always use Arr_non_caching_segment_traits_2 as
our traits-class model in order to construct arrangements of line
segments. In succeeding sections we also use Arr_segment_traits_2
as our traits-class model. These two traits trade computation time
and storage space. The latter stores the underlying line of every
segment of the arrangement to expedite certain operations on the
arrangement segments. In Section \ref aos_sec-unbounded we use
Arr_linear_traits_2 to construct arrangements of linear curves
(i.e., lines, rays, and line segments). The \ref
PkgArrangementOnSurface2 package contains several other traits
classes that can handle other types of curves, such as polylines
(continuous piecewise-linear curves), conic arcs, and arcs of
rational functions. We exemplify the usage of these traits classes
in Section \ref aos_sec-geom_traits.
The function template print_arrangement_size() listed below, and
defined in the header file Arr_print.h, prints out quantitative
measures of a given arrangement. While in what follows it is used only
by examples, it demonstrates well the use of the member functions
\link Arrangement_on_surface_2::number_of_vertices()number_of_vertices()\endlink, \link
Arrangement_on_surface_2::number_of_edges()number_of_edges()\endlink, and \link
Arrangement_on_surface_2::number_of_faces()number_of_faces()\endlink, which return the number of vertices,
edges, and faces of an arrangement, respectively.
\anchor lst_paz
\code{.cpp}
template
void print_arrangement_size(const Arrangement& arr) {
std::cout << "The arrangement size:\n"
<< " |V| = " << arr.number_of_vertices()
<< ", |E| = " << arr.number_of_edges()
<< ", |F| = " << arr.number_of_faces() << std::endl;
}
\endcode
You can also obtain the number of halfedges of an arrangement using
the member function \link Arrangement_on_surface_2::number_of_halfedges()number_of_halfedges()\endlink. Recall that the number of halfedges is
always twice the number of edges.
\image html triangle.png
\image latex triangle.png
The simple program listed below constructs an arrangement of three
connected line segments forming a triangle. It uses the
Cartesian kernel with an integral-number type to instantiate
the Arr_non_caching_segment_traits_2<Kernel> class template. The
resulting arrangement consists of two faces, a bounded triangular face
and the unbounded face. Constructing and maintaining arrangements
using limited-precision numbers, such as int, works properly only
under severe restrictions, which in many cases render the program not
very useful. In this example, however, the points are far apart, and
constructions of new geometric objects do not occur. Thus, it is safe
to use int after all. The program constructs an arrangement induced
by three line segments that are pairwise disjoint in their interior,
prints out the number of faces, and ends. It uses the
\link insert(Arrangement_2&, InputIterator, InputIterator) insert()\endlink
free-function, which inserts the segments into the
arrangement; see Section \ref arr_secgl_funcs. It uses the member
function \link Arrangement_on_surface_2::number_of_facesnumber_of_faces()\endlink to obtain the number of faces (two in this
case). We give more elaborate examples in the rest of this
chapter. The programs in those examples rely on computing with numbers
of arbitrary precision, which guarantees robust execution and correct
results.
\code{.cpp}
#include <CGAL/Cartesian.h>
#include <CGAL/Arr_non_caching_segment_traits_2.h>
#include <CGAL/Arrangement_2.h>
using Number_type = int;
using Kernel = CGAL::Cartesian
using Traits = CGAL::Arr_non_caching_segment_traits_2
using Point = Traits_2::Point_2;
using Segment = Traits_2::X_monotone_curve_2;
using Arrangement = CGAL::Arrangement_2;
int main() {
Arrangement arr;
Point p1(1, 1), p2(1, 2), p3(2, 1);
Segment cv[] = {Segment(p1, p2), Segment(p2, p3), Segment(p3, p1)};
CGAL::insert(arr, &cv[0], &cv[sizeof(cv)/sizeof(Segment)]);
return 0;
}
\endcode
\subsection arr_ssectraverse Traversing the Arrangement
The simplest and most fundamental arrangement operations are the
various traversal methods, which allow users to systematically go over
the relevant features of the arrangement at hand.
As mentioned above, the arrangement is represented as a \dcel,
which stores three containers of vertices, halfedges and faces; thus,
the Arrangement_2 class template supplies iterator types for these
containers, respectively. For example, if arr is an Arrangement_2
object, the calls \link Arrangement_on_surface_2::vertices_begin()arr.vertices_begin()\endlink and \link
Arrangement_on_surface_2::vertices_end arr.vertices_end()\endlink
return iterators of the nested \link
Arrangement_on_surface_2::Vertex_iterator Vertex_iterator\endlink
type that define the valid range of vertices of the arrangementarr. The value type of this iterator is \link
Arrangement_on_surface_2::Vertex Vertex\endlink. Moreover, the
vertex-iterator type is convertible to \link
Arrangement_on_surface_2::Vertex_handle Vertex_handle\endlink, which
serves as a pointer to a vertex. As we show next, all functions
related to arrangement features accept handle types as input
parameters and return handle types as their output.
See Chapter \ref Chapter_Handles_Ranges_and_Circulators
"Handles and Circulators" for more information on \cgal handles.
In addition to the iterators for arrangement vertices, halfedges, and
faces, the arrangement class also provides an iterator for edges,
namely \link Arrangement_on_surface_2::Edge_iteratorEdge_iterator\endlink. The value type of this iterator is \link
Arrangement_on_surface_2::Halfedge Halfedge\endlink, which is the
same as the value type of \link
Arrangement_on_surface_2::Halfedge_iteratorHalfedge_iterator\endlink. The calls \link
Arrangement_on_surface_2::edges_begin() arr.edges_begin()\endlink
and \link Arrangement_on_surface_2::edges_end()arr.edges_end()\endlink return iterators that define the valid range
of arrangement edges. This range consists of half the number of
halfedges of the arrangement, as every twin halfedges has one
representative in this range.
All iterator, circulator\cgalFootnote{A circulator is used to
traverse a circular list, such as the list of halfedges incident to a
vertex.} and handle types also have non-mutable (const)
counterparts. These non-mutable iterator types are useful for
traversing an arrangement with the promise to keep it unchanged. For
example, the arrangement has a non-constant member function calledArrangement_on_surface_2::vertices_begin() that returns a \link
Arrangement_on_surface_2::Vertex_iterator Vertex_iterator\endlink
object and another const member function that returns a \link
Arrangement_on_surface_2::Vertex_const_iteratorVertex_const_iterator\endlink object. In fact, all methods listed in
this section that return an iterator, a circulator, or a handle have
non-mutable counterparts. It should be noted that, for example, \link
Arrangement_on_surface_2::Vertex_handle Vertex_handle\endlink can be
readily converted into a \link
Arrangement_on_surface_2::Vertex_const_handleVertex_const_handle\endlink, but not the other way around.
Conversions of non-mutable handles to the corresponding mutable
handles are nevertheless possible. They can be performed using the
overloaded member function \link
Arrangement_on_surface_2::non_const_handle()non_const_handle()\endlink. There are three variants that accept a
non-mutable handle to a vertex, a halfedge, or a face, respectively.
The call \link Arrangement_on_surface_2::non_const_handle()non_const_handle()\endlink can be issued only if the arrangement
object arr is mutable; see, e.g., Section \ref arr_ssecpl.
\subsubsection arr_sssectr_vertex Traversal Methods for an Arrangement Vertex
A vertex \f$v\f$ of an arrangement induced by bounded curves is always
associated with a geometric entity, namely with an \link
Arrangement_on_surface_2::Point_2 Point_2\endlink object, which can
be obtained by \link Arrangement_on_surface_2::Vertex::point()v->point()\endlink, where v identifies a handle to \f$v\f$.
The call \link Arrangement_on_surface_2::Vertex::is_isolated()v->is_isolated()\endlink determines whether the vertex \f$v\f$ is
isolated or not. Recall that the halfedges incident to a non-isolated
vertex, namely, the halfedges that share a common target vertex, form
a circular list around this vertex. The call \link
Arrangement_on_surface_2::Vertex::incident_halfedges()v->incident_halfedges()\endlink returns a circulator of the nested
type \link Arrangement_on_surface_2::Halfedge_around_vertex_circulatorHalfedge_around_vertex_circulator\endlink that enables the traversal
of this circular list around a given vertex \f$v\f$ in a clockwise
order. The value type of this circulator is \link
Arrangement_on_surface_2::Halfedge Halfedge\endlink. By convention,
the target of the halfedge is \f$v\f$. The call \link
Arrangement_on_surface_2::Vertex::degree() v->degree()\endlink
evaluates to the number of the halfedges incident to \f$v\f$.
The function below prints all the halfedges incident to a given
arrangement vertex (assuming that objects of the \link
Arrangement_on_surface_2::Point_2 Point_2\endlink type can be
inserted into the standard output using the << operator). The
arrangement type is the same as in the simple example above.
\code
template
void print_incident_halfedges(typename Arrangement::Vertex_const_handle v) {
if (v->is_isolated()) {
std::cout << "The vertex (" << v->point() << ") is isolated\n";
return;
}
typename Arrangement::Halfedge_around_vertex_const_circulator first, curr;
first = curr = v->incident_halfedges();
std::cout << "The neighbors of the vertex (" << v->point() << ") are:";
do std::cout << " (" << curr->source()->point() << ")";
while (++curr != first);
std::cout << std::endl;
}
\endcode
If \f$v\f$ is an isolated vertex, the call \link
Arrangement_on_surface_2::Vertex::face() v->face()\endlink can be used to
obtain the face that contains \f$v\f$.
\subsubsection arr_sssectr_halfedge Traversal Methods for an Arrangement Halfedge
A halfedge \f$e\f$ of an arrangement induced by bounded curves is
associated with an \link Arrangement_on_surface_2::X_monotone_curve_2X_monotone_curve_2\endlink object, which can be obtained by
\link Arrangement_on_surface_2::Halfedge::curve()e->curve()\endlink, where e identifies a handle to \f$e\f$.
The calls \link Arrangement_on_surface_2::Halfedge::source()e->source()\endlink and \link
Arrangement_on_surface_2::Halfedge::target() e->target()\endlink
return handles to the halfedge's source-vertex and target-vertex,
respectively. You can obtain a handle to the twin halfedge using \link
Arrangement_on_surface_2::Halfedge::twin() e->twin()\endlink. Note
that from the definition of halfedges in the Dcel structure, the
following invariants always hold:
Every halfedge has an incident face that lies to its left, which can
be obtained by \link Arrangement_on_surface_2::Halfedge::face()e->face()\endlink. Recall that a halfedge is always one link in a
connected chain (CCB) of halfedges that share the same incident
face. The \link Arrangement_on_surface_2::Halfedge::prev()e->prev()\endlink and \link
Arrangement_on_surface_2::Halfedge::next() e->next()\endlink calls
return handles to the previous and next halfedges in the CCB,
respectively.
As the CCB is a circular list of halfedges, it is only natural to
traverse it using a circulator. Indeed \link
Arrangement_on_surface_2::Halfedge::ccb() e->ccb()\endlink returns
an \link Arrangement_on_surface_2::Ccb_halfedge_circulatorCcb_halfedge_circulator\endlink object for traversing all halfedges
along the connected component of \f$e\f$. The value type of this
circulator is \link Arrangement_on_surface_2::HalfedgeHalfedge\endlink.
The function template print_ccb() listed below prints all
\f$x\f$-monotone curves along a given CCB (assuming that objects of
the \link Arrangement_on_surface_2::Point_2 Point_2\endlink and the
\link Arrangement_on_surface_2::X_monotone_curve_2X_monotone_curve_2\endlink types can be inserted into the standard
output using the << operator).
\code
template
void print_ccb(typename Arrangement::Ccb_halfedge_const_circulator circ) {
Ccb_halfedge_const_circulator curr = circ;
std::cout << "(" << curr->source()->point() << ")";
do {
typename Arrangement::Halfedge_const_handle he = curr->handle();
std::cout << " [" << e->curve() << "] "
<< "(" << e->target()->point() << ")";
} while (++curr != circ);
std::cout << std::endl;
}
\endcode
\subsubsection arr_sssectr_face Traversal Methods for an Arrangement Face
An Arrangement_2 object arr that identifies an arrangement of
bounded curves always has a single unbounded face. The call \link
Arrangement_on_surface_2::unbounded_face()arr.unbounded_face()\endlink returns a handle to this face. Note
that an empty arrangement contains nothing but the unbounded
face.
Given a handle to a face \f$f\f$, you can use the call \link
Arrangement_on_surface_2::Face::is_unbounded()f->is_unbounded()\endlink to determine whether the face \f$f\f$ is
unbounded. Bounded faces have an outer CCB, and the \link
Arrangement_on_surface_2::Face::outer_ccb() outer_ccb()\endlink
method returns a circulator for the halfedges along this CCB. Note
that the halfedges along this CCB wind in a counterclockwise order
around the outer boundary of the face.
A face can also contain disconnected components in its interior,
namely, holes and isolated vertices. You can access these components
as follows:
The function print_face() listed below prints the outer and inner
boundaries of a given face. It uses the function templateprint_ccb() listed above.
\code
template
void print_face(typename Arrangement::Face_const_handle f) {
// Print the outer boundary.
if (f->is_unbounded()) std::cout << "Unbounded face.\n";
else {
std::cout << "Outer boundary: ";
print_ccb(f->outer_ccb());
}
// Print the boundary of each of the holes.
std::size_t index = 1;
for (auto hi = f->holes_begin(); hi != f->holes_end(); ++hi) {
std::cout << " Hole #" << index++ << ": ";
print_ccb(*hi);
}
// Print the isolated vertices.
index = 1;
for (auto iv = f->isolated_vertices_begin();
iv != f->isolated_vertices_end(); ++iv)
{
std::cout << " Isolated vertex #" << index++ << ": "
<< "(" << iv->point() << ")\n";
}
}
\endcode
The function print_arrangement() listed below prints the features of
a given arrangement. The file arr_print.h, which can be found under
the examples folder, includes the definitions of this function, as
well as the definitions of all other functions listed in this
section. This concludes the preview of the various traversal methods.
\code
void print_arrangement (const Arrangement_2& arr) {
// Print the arrangement vertices.
std::cout << arr.number_of_vertices() << " vertices:\n";
for (auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit) {
std::cout << "(" << vit->point() << ")";
if (vit->is_isolated()) std::cout << " - Isolated.\n";
else std::cout << " - degree " << vit->degree() << std::endl;
}
// Print the arrangement edges.
std::cout << arr.number_of_edges() << " edges:\n";
for (auto eit = arr.edges_begin(); eit != arr.edges_end(); ++eit)
std::cout << "[" << eit->curve() << "]\n";
// Print the arrangement faces.
std::cout << arr.number_of_faces() << " faces:\n";
for (auto fit = arr.faces_begin(); fit != arr.faces_end(); ++fit)
print_face(fit);
}
\endcode
\subsection arr_ssecmodify Modifying the Arrangement
In this section we review the various member functions of theArrangement_2 class that allow users to modify the topological
structure of the arrangement through the introduction of new edges or
vertices, or the modification or removal of existing edges and
vertices.
The arrangement member-functions that insert new \f$x\f$-monotone
curves into the arrangement, thus enabling the construction of a
two-dimensional surface subdivision, are rather specialized, as they
assume that the interior of the inserted curve is disjoint from all
existing arrangement vertices and edges, and in addition require
apriori knowledge of the location of the inserted curve. Indeed, for
most purposes it is more convenient to construct an arrangement using
the free (global) insertion functions, which relax these
restrictions. However, as these free functions are implemented in
terms of the specialized insertion functions, we start by describing
the fundamental functionality of the arrangement class, and describe
the operation of the free functions in Section \ref arr_secgl_funcs.
\subsection arr_sssecmf_insert_cv Inserting Pairwise Disjoint x-Monotone Curves
The most trivial functions that allow users to modify the arrangement
are the specialized functions for the insertion of an \f$x\f$-monotone
curve the interior of which is disjoint from the interior of all other
curves in the existing arrangement and does not contain any point of
the arrangement. In addition, these functions require that the
location of the curve in the arrangement be known.
The rather harsh restrictions on the inserted curves enable an
efficient implementation. While inserting an \f$x\f$-monotone curve,
the interior of which is disjoint from all curves in the existing
arrangement, is quite straightforward, as we show next, (efficiently)
inserting a curve that intersects with the curves already in the
arrangement is much more complicated and requires the application of
nontrivial geometric algorithms. The decoupling of the topological
arrangement representation from the various algorithms that operate on
it dictates that the general insertion operations be implemented as
free functions that operate on the arrangement and the inserted
curve(s); see Section \ref arr_secgl_funcs for more details and
examples.
\cgalFigureBegin{aos_fig-insert,insert.png}
Illustrations of the various specialized insertion procedures. The
inserted \f$x\f$-monotone curve is drawn as a dashed line, surrounded
by two solid arrows that represent the pair of twin halfedges added to
the \dcel. Existing vertices are shown as red discs, while new
vertices are shown as blue discs. Existing halfedges that are
affected by the insertion operations are drawn as dashed arrows. (a)
Inserting a curve as a new hole inside the face \f$f\f$. (b)
Inserting a curve from an existing vertex \f$u\f$ that corresponds to
one of its endpoints. (c) Inserting an \f$x\f$-monotone curve, the
endpoints of which correspond to existing vertices \f$u_1\f$ and
\f$u_2\f$. In this case, the new pair of halfedges closes a new face
\f$f'\f$. The hole \f$h_1\f$, which belonged to \f$f\f$ before
the insertion, becomes a hole in this new face.
\cgalFigureEnd
When an \f$x\f$-monotone curve is inserted into an existing
arrangement, such that the interior of this curve is disjoint from the
interiors of all curves in the arrangement, only the following three
scenarios are possible, depending on the status of the endpoints of
the inserted curve:
- Neither curve endpoints correspond to any existing arrangement vertex. In this case we have to create two new vertices that correspond to the curve endpoints, respectively, and connect them using a pair of twin halfedges. This halfedge pair forms a new hole inside the face that contains the curve in its interior.
- Exactly one endpoint corresponds to an existing arrangement vertex. (We distinguish between a vertex that corresponds to the left endpoint of the inserted curve and a vertex that corresponds to its right endpoint.) In this case we have to create a new vertex that corresponds to the other endpoint of the curve and to connect the two vertices by a pair of twin halfedges that form an "antenna" emanating from the boundary of an existing connected component. (Note that if the existing vertex is isolated, we need to form a new hole inside the face that contains this vertex, essentially falling back to the handling of the previous case, naturally, skipping the creation of the existing vertex.)
- Both endpoints correspond to existing arrangement vertices. In this case we connect these vertices using a pair of twin halfedges. (If one or both vertices are isolated, we fall back to the handling of the first or second case, respectively, naturally, skipping the creation of the existing vertices.) The two following subcases may occur:
- Two disconnected components are merged into a single connected component (as is the case with the segment \f$s_1\f$ in the figure below).
- A new face, which is split from an existing arrangement face, is created (as is the case with the segment \f$s_2\f$ in the figure below). In this case we also have to examine the holes and isolated vertices in the existing face and move the relevant ones inside the new face.
\image html connect_comp.png
\image latex connect_comp.png
The Arrangement_2 class offers insertion functions that perform the
special insertion procedures listed above, namely, \link
Arrangement_on_surface_2::insert_in_face_interior()insert_in_face_interior()\endlink, \link
Arrangement_on_surface_2::insert_from_left_vertex()insert_from_left_vertex()\endlink, \link
Arrangement_on_surface_2::insert_from_right_vertex()insert_from_right_vertex()\endlink and \link
Arrangement_on_surface_2::insert_at_vertices()insert_at_vertices()\endlink. The first function accepts an
\f$x\f$-monotone curve \f$c\f$ and an arrangement face \f$f\f$ that
contains this curve in its interior. The other functions accept an
\f$x\f$-monotone curve \f$c\f$ and handles to the existing vertices
that correspond to the curve endpoint(s). Each of the four functions
returns a handle to one of the twin halfedges that have been created;
more precisely:
\cgalFigureBegin{aos_fig-edge_insertion,edge_insertion.png}
The arrangement of the line segments \f$s_1, \ldots, s_5\f$
constructed in \ref Arrangement_on_surface_2/edge_insertion.cpp.
The arrows mark the direction of the halfedges returned from the
various insertion functions.
\cgalFigureEnd
The program below demonstrates the usage of the four specialized
insertion functions. It creates an arrangement of five line segments
\f$s_1, \ldots, s_5\f$, as depicted in
\cgalFigureRef{aos_fig-edge_insertion}. \cgalFootnote{Notice that in
all figures in the rest of this chapter the coordinate axes are drawn
only for illustrative purposes and are not part of the
arrangement.} The first line segment \f$s_1\f$ is inserted in the
interior of the unbounded face, while the four succeeding line
segments \f$s_2, \ldots, s_5\f$ are inserted using the vertices
created by the insertion of preceding segments. The arrows in the
figure mark the direction of the halfedges \f$e_1, \ldots, e_5\f$
returned from the insertion functions. The resulting arrangement
consists of three faces, where the two bounded faces form together a
hole in the unbounded face.
Two header files are included in the code in order to make this and
the following examples more compact. The filearr_inexact_construction_segments.h is listed
immediately after the program. The file arr_print.h is
introduced in Section \ref arr_ssectraverse.
\cgalExample{Arrangement_on_surface_2/edge_insertion.cpp}
The statements below define the types for arrangements of line
segments common to all examples that do not construct new geometric
objects. They are kept in the header filearr_inexact_construction_segments.h. In these examples the Traits
parameter of the Arrangement_2 class template is
substituted by an instance of theArr_non_caching_segment_traits_2<Kernel> class template. TheArr_non_caching_segment_traits_2 class template is instantiated with
the predefined kernel that evaluates predicates in an exact manner,
but constructs geometric objects in an inexact manner, as none of
these examples construct new geometric objects. In the remaining
examples the traits class-template is instantiated with a kernel that
evaluates predicates and constructs geometric objects, both in an
exact manner.
\code{.cpp}
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Arr_non_caching_segment_traits_2.h>
#include <CGAL/Arrangement_2.h>
using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
using Number_type = Kernel::FT;
using Traits = CGAL::Arr_non_caching_segment_traits_2
using Point = Traits::Point_2;
using Segment = Traits::X_monotone_curve_2;
using Arrangement = CGAL::Arrangement_2;
using Vertex_handle = Arrangement::Vertex_handle;
using Halfedge_handle = Arrangement::Halfedge_handle;
using Face_handle = Arrangement::Face_handle;
\endcode
\subsection arr_sssecmf_iso_verts Manipulating Isolated Vertices
Isolated points are simpler geometric entities than curves, and indeed
the member functions that manipulate them are easier to understand.
The call \link Arrangement_on_surface_2::insert_in_face_interior()arr.insert_in_face_interior(p, f)\endlink inserts an isolated point
\f$p\f$, located in the interior of a given face \f$f\f$, into the
arrangement and returns a handle to the arrangement vertex associated
with \f$p\f$ it has created. Naturally, this function has a
precondition that \f$p\f$ is really an isolated point; namely it does
not coincide with any existing arrangement vertex and does not lie on
any edge. As mentioned in Section \ref arr_ssectraverse, it is
possible to obtain the face containing an isolated vertex calling the
member function Arrangement_on_surface_2::Vertex::face(). The member
function \link Arrangement_on_surface_2::remove_isolated_vertex()Arrangement_on_surface_2::remove_isolated_vertex(Vertex_handle v)\endlink
accepts a handle to an isolated vertex and removes it from the
arrangement.
\cgalFigureBegin{aos_fig-isolated_vertices,isolated_vertices.png}
An arrangement of line segments containing three isolated vertices,
as constructed in \ref Arrangement_on_surface_2/isolated_vertices.cpp.
The vertices \f$u_2\f$ and \f$u_3\f$ are eventually removed from the
arrangement.
\cgalFigureEnd
The following program demonstrates the usage of the arrangement
member-functions for manipulating isolated vertices. It first inserts
three isolated vertices, \f$u_1\f$, \f$u_2\f$, and \f$u_3\f$, located
inside the unbounded face of the arrangement. Then, it inserts four
line segments \f$s_1, \ldots, s_4\f$, that form a square hole inside
the unbounded face (see \cgalFigureRef{aos_fig-edge_insertion} for an
illustration). Finally, it traverses the vertices and removes those
isolated vertices that are still contained in the unbounded face
(\f$u_2\f$ and \f$u_3\f$ in this case):
\cgalExample{Arrangement_on_surface_2/isolated_vertices.cpp}
\subsection arr_sssecmf_halfedges Manipulating Halfedges
While reading the previous subsection you learned how to insert new
points that induce isolated vertices into the arrangement. You may
wonder now how you can insert a new point that lies on an
\f$x\f$-monotone curve that is associated with existing arrangement
edge.
The introduction of a vertex, the geometric mapping of which is a
point \f$p\f$ that lies on an \f$x\f$-monotone curve, requires the
splitting of the curve in its interior at \f$p\f$. The two resulting
subcurves induce two new edges, respectively. In general, theArrangement_2 class template relies on the geometry traits to
perform such a split. As a matter of fact, it relies on the geometry
traits to perform all geometric operations. To insert a point \f$p\f$
that lies on an \f$x\f$-monotone curve associated with an existing
edge \f$e\f$ into the arrangement \f$\mathcal{A}\f$, you must first
construct the two curves \f$c_1\f$ and \f$c_2\f$, which are the two
subcurves that result from splitting the \f$x\f$-monotone curve
associated with the edge \f$e\f$ at \f$p\f$. Then, you have to issue
the call \link Arrangement_on_surface_2::split_edge()arr.split_edge(he, c1, c2)\endlink, where arr identifies the
arrangement \f$\mathcal{A}\f$ and he is a handle to one of the two
halfedges that represent the edge \f$e\f$. The function splits the two
halfedges that represent \f$e\f$ into two pairs of halfedges,
respectively. Two new halfedges are incident to the new vertex \f$v\f$
associated with \f$p\f$. The function returns a handle to the new
halfedge, the source of which is the source vertex of the halfedge
handled by he, and the target of which is the new vertex \f$v\f$.
The reverse operation is also possible. Consider a vertex \f$v\f$ of
degree \f$2\f$ that has two incident edges \f$e_1\f$ and \f$e_2\f$
associated with two curves \f$c_1\f$ and \f$c_2\f$, respectively, such
that the union of \f$c_1\f$ and \f$c_2\f$ results in a single
continuous \f$x\f$-monotone curve \f$c\f$ of the type supported by the
traits class in use. To merge the edges \f$e_1\f$ and \f$e_2\f$ into a
single edge associated with the curve \f$c\f$, essentially removing
the vertex \f$v\f$ from the arrangement identified by arr, you need
to issue the call \link Arrangement_2.merge_edge() arr.merge_edge(he1, he2, c)\endlink, where he1 and he2 are handles to halfedges
representing \f$e_1\f$ and \f$e_2\f$, respectively.
Finally, the call \link Arrangement_2.remove_edge()remove_edge(he)\endlink removes the edge \f$e\f$ from the
arrangement, where he is a handle to one of the two halfedges that
represents \f$e\f$. Note that this operation is the reverse of an
insertion operation, so it may cause a connected component to split
into two, or two faces to merge into one, or a hole to disappear. By
default, if the removal of \f$e\f$ causes one of its end vertices to
become isolated, this vertex is removed as well. However, you can
control this behavior and choose to keep the isolated vertices by
supplying additional Boolean flags to \link
Arrangement_on_surface_2::remove_edge remove_edge()\endlink
indicating whether the source or the target vertices are to be removed
should they become isolated.
\cgalFigureBegin{aos_fig-edge_manipulation,edge_manipulation.png}
The three steps of the example program \ref
Arrangement_on_surface_2/edge_manipulation.cpp. In Step (a) it
constructs an arrangement of four line segments. In Step (b) the edges
\f$e_1\f$ and \f$e_2\f$ are split, and the split points are connected
with a new segment \f$s\f$ that is inserted into the arrangement. This
operation is undone in Step (c), where \f$e\f$ is removed from the
arrangement, rendering its end vertices \f$u_1\f$ and \f$u_2\f$
redundant. We therefore remove these vertices by merging their
incident edges and go back to the arrangement depicted in (a).
\cgalFigureEnd
The following example program shows how the edge-manipulation
functions can be used. The program works in three steps, as
demonstrated in \cgalFigureRef{aos_fig-isolated_vertices}. Note that
the program uses the fact that \link
Arrangement_on_surface_2::split_edge() split_edge()\endlink returns
one of the new halfedges (after the split) that has the same direction
as the original halfedge (the first parameter of the function) and is
directed towards the split point. Thus, it is easy to identify the
vertices \f$u_1\f$ and \f$u_2\f$ associated with the split points.
\cgalExample{Arrangement_on_surface_2/edge_manipulation.cpp}
The member functions \link Arrangement_on_surface_2::modify_vertex()modify_vertex()\endlink and \link
Arrangement_on_surface_2::modify_edge() modify_edge()\endlink modify
the geometric mappings of existing features of the arrangement. The
call \link Arrangement_on_surface_2::modify_vertexarr.modify_vertex(v, p)\endlink accepts a handle to a vertex \f$v\f$
and a reference to a point \f$p\f$, and sets \f$p\f$ to be the point
associated with the vertex \f$v\f$. The call \link
Arrangement_on_surface_2::modify_edge() arr.modify_edge(he, c)\endlink accepts a handle to one of the two halfedges that
represent an edge \f$e\f$ and a reference to a curve \f$c\f$, and sets
\f$c\f$ to be the \f$x\f$-monotone curve associated with
\f$e\f$. (Note that both halfedges are modified; that is, both
expressions e->curve() and e->twin()->curve() evaluate to
\f$c\f$ after the modification.) These functions have preconditions
that \f$p\f$ is geometrically equivalent to v->point() and \f$c\f$
is equivalent to e->curve(), respectively.\cgalFootnote{Roughly
speaking, two curves are equivalent iff they have the same graph. In
Section \ref aos_sssec-geom_traits-concepts_basic we give a formal
definition of curves and curve equivalence.} If these preconditions
are not met, the corresponding operation may invalidate the structure
of the arrangement. At first glance it may seem as if these two
functions are of little use. However, you should keep in mind that
there may be extraneous data (probably non-geometric) associated with
the point objects or with the curve objects, as defined by the traits
class. With these two functions you can modify this data; see more
details in Section \ref arr_sssec_tr_data_decorators. In addition, you can use
these functions to replace a geometric object (a point or a curve)
with an equivalent object that has a more compact representation. For
example, if we use some simple rational-number type to represent the
point coordinates, we can replace the point \f$(\frac{20}{40},
\frac{99}{33})\f$ associated with some vertex \f$v\f$ with an
equivalent point with normalized coordinates, namely \f$(\frac{1}{2},
3)\f$.
\subsection arr_sssecadv_insert Advanced Insertion Functions
\cgalAdvancedBegin
| \image html pred_around_vertex.png
\image latex pred_around_vertex.png | Assume that the specialized insertion function \link
Arrangement_on_surface_2::insert_from_left_vertex()insert_from_left_vertex(c, v)\endlink is given a curve \f$c\f$, the
left endpoint of which is already associated with a non-isolated
vertex \f$v\f$. Namely, \f$v\f$ has already several incident
halfedges. It is necessary in this case to locate the exact place for
the new halfedge mapped to the inserted new curve \f$c\f$ in the
circular list of halfedges incident to \f$v\f$. More precisely, in
order to complete the insertion, it is necessary to locate the
halfedge \f$e_{\mathrm{pred}}\f$ directed toward \f$v\f$, such that
\f$c\f$ is located between the curves associated with
\f$e_{\mathrm{pred}}\f$ and the next halfedge in the clockwise order
in the circular list of halfedges around \f$v\f$; see
\cgalFigureRef{aos_fig-insert}. This search may take \cgalBigO{d} time,
where \f$d\f$ is the degree of the vertex \f$v\f$. \cgalFootnote{We
can store the handles to the halfedges incident to \f$v\f$ in an efficient
search structure to obtain \cgalBigO{\log d} access time. However, as
\f$d\f$ is usually very small, this may lead to a waste of storage
space without a meaningful improvement in running time in practice.}
However, if the halfedge \f$e_{\mathrm{pred}}\f$ is known in advance,
the insertion can be carried out in constant time, and without
performing any geometric comparisons. |
| --- | --- |
The Arrangement_2 class provides advanced versions of the
specialized insertion functions for a curve \f$c\f$, namely, \link
Arrangement_on_surface_2::insert_from_left_vertex()insert_from_left_vertex(c, he_pred)\endlink and \link
Arrangement_on_surface_2::insert_from_right_vertex()insert_from_right_vertex(c, he_pred)\endlink. These functions
accept a halfedge \f$e_{\mathrm{pred}}\f$ as specified above, instead
of a handle to a vertex \f$v\f$. They are more efficient, as they take
constant time and do not perform any geometric operations. Thus, you
should use them when the halfedge \f$e_{\mathrm{pred}}\f$ is known. In
cases where the vertex \f$v\f$ is isolated or the predecessor halfedge
for the newly inserted curve is not known, the simpler versions of
these insertion functions should be used. Similarly, the member
function \link Arrangement_on_surface_2::insert_at_vertices()insert_at_vertices()\endlink is overloaded with two additional
versions as follows. One accepts two handles to the two predecessor
halfedges around the two vertices \f$v_1\f$ and \f$v_2\f$,
respectively, that correspond to the curve endpoints. The other one
accepts a handle to one vertex and a handle to the predecessor
halfedge around the other vertex.
\cgalFigureBegin{aos_fig-special_edge_insertion,special_edge_insertion.png}
An arrangement of line segments, as constructed in
\ref Arrangement_on_surface_2/special_edge_insertion.cpp. Note that
\f$p_0\f$ is initially inserted as an isolated point and later on
connected to the other four vertices.
\cgalFigureEnd
The following program shows how to construct the arrangement
depicted in \cgalFigureRef{aos_fig-special_edge_insertion} using the specialized
insertion functions that accept predecessor halfedges:
\cgalExample{Arrangement_on_surface_2/special_edge_insertion.cpp}
It is possible to perform even more refined operations on anArrangement_2 object given specific topological information.
As most of these operations are very fragile and perform no precondition
testing on their input in order to gain efficiency, they are not included
in the public interface of the arrangement class. Instead, theArr_accessor<Arrangement> class allows access to these internal
arrangement operations; see more details in the Reference Manual.
\cgalAdvancedEnd
\section arr_secqueries Issuing Queries on an Arrangement
One of the most useful query types defined on arrangements is the
point-location query: Given a point, find the arrangement
cell that contains it. Typically, the result of a point-location query
is one of the arrangement faces, but in degenerate situations the
query point can lie on an edge, or it may coincide with a vertex.
Point-location queries are common in many applications, and also
play an important role in the incremental construction of arrangements
(and more specifically in the free insertion-functions described in
Section \ref arr_secgl_funcs). Therefore, it is crucial to have the
ability to answer such queries effectively.
\subsection arr_ssecpl Point-Location Queries
Recall that the arrangement representation is decoupled from the
geometric algorithms that operate on it. Thus, the Arrangement_2
class template does not support point-location queries directly.
Instead, the \ref PkgArrangementOnSurface2 package provides a set of
class templates that are capable of answering such queries; all are
models of the concept AosPointLocation_2. Each model employs
a different algorithm or strategy for answering queries. A
model of this concept must define the \link
AosPointLocation_2::locate() locate()\endlink member
function, which accepts an input query-point and returns a polymorphic
object representing the arrangement cell that contains this point.
The returned object is of typeArr_point_location_result<Arrangement_2>::%Type, which is a
discriminated union container of the bounded types \link
Arrangement_on_surface_2::Vertex_const_handleVertex_const_handle\endlink, \link
Arrangement_on_surface_2::Halfedge_const_handleHalfedge_const_handle\endlink, or \link
Arrangement_on_surface_2::Face_const_handleFace_const_handle\endlink. Depending on whether the query point is
located inside a face, lies on an edge, or coincides with a vertex,
the appropriate handle can be obtained with value retrieval
by std::get as demonstrated in the example below.
Note that the handles returned by the \link
AosPointLocation_2::locate() locate()\endlink functions are
non-mutable (const). If necessary, such handles may be cast to
mutable handles using theArrangement_on_surface_2::non_const_handle() methods.
An object pl of any point-location class must be attached to anArrangement_2 object arr before it is used to answer
point-location queries on arr. This attachment can be performed
when pl is constructed or at a later time using thepl.init(arr) call.
The function template locate_point() listed below accepts a
point-location object, the type of which is a model of theAosPointLocation_2 concept, and a query point. The function
template issues a point-location query for the given point, and prints
out the result. It is defined in the header filepoint_location_utils.h.
\anchor lst_pl
\code
template
void locate_point(const PointLocation& pl,
const typename PointLocation::Arrangement_2::Point_2& q) {
using Point_location = PointLocation;
using Arrangement_2 = typename Point_location::Arrangement_2;
typename CGAL::Arr_point_location_result
pl.locate(q);
// Print the result.
print_point_location
}
\endcode
The function template locate_point() calls an instance of the
function template print_point_location(), which inserts the
result of the query into the standard output-stream. It is listed
below, and defined in the header file point_location_utils.h.
Observe how the function std::get() is used to cast the
resulting object into a handle to an arrangement feature. The
point-location object pl is assumed to be already attached
to an arrangement.
\code
template
void print_point_location
(const typename PointLocation::Arrangement_::Point_2& q
typename CGAL::Arr_point_location_result
using Arrangement_2 = Arrangement_;
using Vertex_const_handle = typename Arrangement_2::Vertex_const_handle;
using Halfedge_const_handle = typename Arrangement_2::Halfedge_const_handle;
using Face_const_handle = typename Arrangement_2::Face_const_handle;
const Vertex_const_handle* v;
const Halfedge_const_handle* e;
const Face_const_handle* f;
std::cout << "The point (" << q << ") is located ";
if (f = std::get_if
std::cout << "inside "
<< (((*f)->is_unbounded()) ? "the unbounded" : "a bounded")
<< " face.\n";
else if (e = std::get_if
std::cout << "on an edge: " << (*e)->curve() << std::endl;
else if (v = std::get_if
std::cout << "on " << (((*v)->is_isolated()) ? "an isolated" : "a")
<< " vertex: " << (*v)->point() << std::endl;
else CGAL_error_msg("Invalid object.");
}
\endcode
\subsection aos_sssec-pl_strategy Choosing a Point-Location Strategy
Each of the various point-location class templates employs a different
algorithm or strategy\cgalFootnote{The term strategy
is borrowed from the design-pattern taxonomy \cgalCite{cgal:ghjv-dpero-95},
Chapter 5. A strategy provides the means to define a family of
algorithms, each implemented by a separate class. All classes that implement
the various algorithms are made interchangeable, letting the algorithm in use
vary according to the user choice.} for answering queries:
There are various ways to select the landmark set in the
arrangement. The selection is governed by the Generator
template parameter. The default generator class, namely
Arr_landmarks_vertices_generator, selects all the vertices of
the attached arrangement as landmarks. Additional generators that
select the set in other ways, such as by sampling random
points or choosing points on a grid, are also available; see the
Reference Manual for more details.
The arrangement attached to the landmark strategy must be either (i)
an instance of the Arrangement_2<GeomTraits,Dcel> class template,
where the GeomTraits parameter is substituted by a geometry-traits
class that models the AosLandmarkTraits_2 concept, or (ii) an
instance of the Arrangement_on_surface_2<GeomTraits,TopolTraits>
class template, where the GeomTraits is similarly substituted;
see Section \ref aos_sssec-tr_landmarks_concept for details about
this concept. Most traits classes included in the \ref
PkgArrangementOnSurface2 package are models of this refined concept.
The first two strategies do not require any extra data. The class
templates that implement them store a pointer to an arrangement object
and operate directly on it. Attaching such point-location objects to
an existing arrangement has virtually no running-time cost at all, but
the query time is linear in the size of the arrangement (the
performance of the walk strategy is much better in practice, but its
worst-case performance is linear). Using these strategies is therefore
recommended only when a relatively small number of point-location
queries are issued by the application, or when the arrangement is
constantly changing (That is, changes in the arrangement structure are
more frequent than point-location queries). On the other hand, the
landmark and the trapezoid RIC strategies require auxiliary data
structures on top of the arrangement structure, which they need to
construct once they are attached to an arrangement object and need to
keep up-to-date as this arrangement changes.
As mentioned above, the triangulation strategy is provided only for
educational purposes, and thus we do not elaborate on this strategy.
The data structure needed by the landmark and the trapezoidal map RIC
strategies can be constructed in \cgalBigO{N \log N} time, where \f$N\f$
is the overall number of edges in the arrangement, but the constant
hidden in the \cgalBigO{~} notation for the trapezoidal map RIC strategy
is much larger. Thus, construction needed by the landmark algorithm is
in practice significantly faster than the construction needed by the
trapezoidal map RIC strategy. In addition, although both resulting
data structures are asymptotically linear in size, the actual amount
of memory consumed by the landmark algorithm is typically smaller than
to the amount used by the trapezoidal map RIC algorithm, due to the
space-efficient Kd-tree used by the
landmark algorithm as the nearest-neighbor search-structure. The
trapezoidal map RIC algorithm has expected logarithmic query time,
while the query time for the landmark algorithm may be as large as
linear. In practice however, the query times of both strategies are
competitive. For a detailed experimental comparison see
\cgalCite{cgal:hh-eplca-05}.
Updating the auxiliary data structures of the trapezoidal map RIC
algorithm is done very efficiently. On the other hand, updating the
nearest-neighbor search-structure of the landmark algorithm may
consume significant time when the arrangement changes frequently,
especially when a Kd-tree is used, as it
must be rebuilt each time the arrangement changes. It is therefore
recommended that the Arr_landmarks_point_location class template be
used when the application frequently issues point-location queries on
an arrangement that only seldom changes. If the arrangement is more
dynamic and is frequently going through changes, theArr_trapezoid_ric_point_location class template should be the
selected point-location strategy.
\cgalFigureBegin{aos_fig-point_location,point_location.png}
The arrangement of line segments, as constructed in \ref
Arrangement_on_surface_2/point_location.cpp, \ref
Arrangement_on_surface_2/vertical_ray_shooting.cpp, and \ref
Arrangement_on_surface_2/batched_point_location.cpp. The arrangement
vertices are drawn as small rings, while the query points \f$q_1,
\ldots, q_6\f$ are drawn as crosses.
\cgalFigureEnd
The program listed below constructs a simple arrangement of five line
segments that form a pentagonal face, with a single isolated
vertex in its interior, as depicted in \cgalFigureRef{aos_fig-point_location}.
Notice that we use the same arrangement structure in the next
three example programs. The arrangement construction is performed by
the function construct_segment_arr() defined in the header filepoint_location_utils.h. (Its listing is omitted here.) The
program employs the naive and the landmark strategies to issue
several point-location queries on this arrangement.
\cgalExample{Arrangement_on_surface_2/point_location.cpp}
Note that the program uses the locate_point() function template
to locate a point and nicely print the result of each query; see
\ref lst_pl "code example" in Section \ref arr_ssecpl.
\subsection arr_ssecray_shoot Vertical Ray Shooting
Another query frequently issued on arrangements is the vertical
ray-shooting query: Given a query point, which arrangement cell
is encounter by a vertical ray shot upward (or downward) from this
point? In the general case the ray hits an edge, but it is possible
that it hits a vertex, or that the arrangement does not have any
vertex or edge lying directly above (or below) the query point.
All point-location classes listed in the previous section are also
models of the AosVerticalRayShoot_2 concept. That is, they
all have member functions called ray_shoot_up(q) andray_shoot_down(q) that accept a query point \f$q\f$. These functions
output a polymorphic object of typeArr_point_location_result<Arrangement_2>::%Type, which is a
discriminated union container of the bounded types \link
Arrangement_on_surface_2::Vertex_const_handleVertex_const_handle\endlink, \link
Arrangement_on_surface_2::Halfedge_const_handleHalfedge_const_handle\endlink, or \link
Arrangement_on_surface_2::Face_const_handleFace_const_handle\endlink. The latter type is used for the unbounded
face of the arrangement, in case there is no edge or vertex lying
directly above (or below) \f$q\f$.
The function template vertical_ray_shooting_query() listed
below accepts a vertical ray-shooting object, the type of which
models the AosVerticalRayShoot_2 concept. It exports
the result of the upward vertical ray-shooting operation from a
given query point to the standard output-stream. The ray-shooting
object vrs is assumed to be already attached to an arrangement.
The function template is defined in the header filepoint_location_utils.h.
\code
template
void shoot_vertical_ray(const RayShoot& vrs,
const typename
VerticalRayShooting::Arrangement_2::Point_2& q) {
using Vertical_ray_shooting = VerticalRayShooting;
// Perform the point-location query.
typename Vertical_ray_shooting::result_type obj = vrs.ray_shoot_up(q);
// Print the result.
using Arrangement_2 = typename Vertical_ray_shooting::Arrangement_2;
using Vertex_const_handle = typename Arrangement_2::Vertex_const_handle;
using Halfedge_const_handle = typename Arrangement_2::Halfedge_const_handle;
using Face_const_handle = typename Arrangement_2::Face_const_handle;
const Vertex_const_handle* v;
const Halfedge_const_handle* e;
const Face_const_handle* f;
std::cout << "Shooting up from (" << q << ") : ";
if (v = std::get_if
std::cout << "hit " << (((*v)->is_isolated()) ? "an isolated" : "a")
<< " vertex: " << (*v)->point() << std::endl;
else if (e = std::get_if
std::cout << "hit an edge: " << (*e)->curve() << std::endl;
else if (f = std::get_if
CGAL_assertion((*f)->is_unbounded());
std::cout << "hit nothing.\n";
}
else CGAL_error();
}
\endcode
The program below uses the function template listed above to
perform vertical ray-shooting queries on an arrangement. The
arrangement and the query points are exactly the same as in
\ref Arrangement_on_surface_2/point_location.cpp; see
\cgalFigureRef{aos_fig-point_location}.
\cgalExample{Arrangement_on_surface_2/vertical_ray_shooting.cpp}
\subsection arr_ssecbatched_pl Batched Point-Location
Suppose that at a given moment our application has to issue a
relatively large number \f$m\f$ of point-location queries on a
specific arrangement object. Naturally, It is possible to define a
point-location object and use it to issue separate queries on the
arrangement. However, as explained in Section \ref arr_ssecpl choosing
a simple point-location strategy (either the naive or the walk
strategy) means inefficient queries, while the more sophisticated
strategies need to construct auxiliary structures that incur
considerable overhead in running time.
Alternatively, the 2D Arrangement package includes a freelocate() function that accepts an arrangement and a range of query
points as its input and sweeps through the arrangement to locate all
query points in one pass. The function outputs the query results as
pairs, where each pair consists of a query point and a discriminated
union container, which represents the cell containing the point; see
Section \ref arr_ssecpl. The output pairs are sorted in increasing
\f$xy\f$-lexicographical order of the query point.
The batched point-location operation is carried out by sweeping the
arrangement. Thus, it takes \cgalBigO{(m+N)\log{(m+N)}} time, where
\f$N\f$ is the number of edges in the arrangement. Issuing separate
queries exploiting a point-location strategy with logarithmic query
time per query, such as the trapezoidal map RIC strategy (see Section
\ref aos_sssec-pl_strategy), is asymptotically more efficient. However,
experiments show that when the number \f$m\f$ of point-location
queries is of the same order of magnitude as \f$N\f$, the batched
point-location operation is more efficient in practice. One of the
reasons for the inferior performance of the alternative
(asymptotically faster) procedures is the necessity to construct and
maintain complex additional data structures.
The program below issues a batched point-location query, which
is essentially equivalent to the six separate queries performed in
\ref Arrangement_on_surface_2/point_location.cpp; see Section
\ref arr_ssecpl.
\cgalExample{Arrangement_on_surface_2/batched_point_location.cpp}
\section arr_secgl_funcs Free Functions
The Arrangement_on_surface_2 class template is used to represent
subdivisions of two-dimensional surfaces induced by curves that lie on
such surfaces. Its interface is minimal in the sense that the member
functions hardly perform any geometric operations. In this section we
explain how to utilize the free functions that enhance that set of
operations on arrangements. The implementation of these operations
typically require non-trivial geometric algorithms, and occasionally
incurs additional requirements on the geometry traits class; the
implementation of many of the operations is based on two frameworks,
namely the surface sweep and the zone construction.
These operations accepts \f$x\f$-monotone curves; thus, the
geometry-traits class used by the arrangements passed as input to, or
obtained as output from, these operations must be a model of theAosXMonotoneTraits_2 concept; see Section \ref
aos_sec-geom_traits for the precise definition of this concept. It
defines the minimal set of geometric primitives, among other things,
required to perform the algorithms of the surface-sweep and
zone-construction frameworks.
\subsection arr_ssec_zone The Zone Construction Algorithm
Given an arrangement of curves \f$\mathcal{A} =
\mathcal{A}(\mathcal{C})\f$ embedded in a two-dimensional surface, the
zone of an additional curve \f$\gamma \notin \mathcal{C}\f$
in \f$\mathcal{A}\f$ is the union of the features of
\f$\mathcal{A}\f$, whose closure is intersected by \f$\gamma\f$. The
complexity of the zone is defined as the sum of the complexities of
its constituents. (Notice that some vertices are counted multiple
times.) The zone of a curve \f$\gamma\f$ is computed by locating the
left endpoint of \f$\gamma\f$ in the arrangement and then "walking"'
along the curve towards the right endpoint, keeping track of the
vertices, edges, and faces crossed on the way. The \ref
PkgArrangementOnSurface2 package offers a generic implementation of an
algorithm that computes the zone. It is used to implement a set of
operations that incrementally construct arrangements induced by sets
of curves that lie in two-dimensional surfaces. For simplicity,
however, we continue to consider arrangements embedded in the plane.
Section \ref aos_ssec-basic-arr_class explains how to construct
arrangements of \f$x\f$-monotone curves that are pairwise disjoint in
their interior when the location of the segment endpoints in the
arrangement is known. Here we relax this constraint, and allow the
location of the inserted \f$x\f$-monotone curve endpoints to be
unknown at the time of insertion.
\subsubsection arr_sssecinsert_non_x Inserting Pairwise Disjoint Curves
We retain, for the moment, the requirement that the interior of the
inserted curve is disjoint from all existing arrangement edges and
vertices.
The call \link CGAL::insert_non_intersecting_curve<>insert_non_intersecting_curve(arr, c, pl)\endlink inserts the
\f$x\f$-monotone curve \f$c\f$ into the arrangement arr, with the
precondition that the interior of \f$c\f$ is disjoint from all
existing edges and vertices of arr. The third argument pl is a
point-location object attached to the arrangement; it is used to
locate both endpoints of \f$c\f$ in the arrangement. Each endpoint is
expected to either coincide with an existing vertex or lie inside a
face. It is possible to invoke one of the specialized insertion
functions (see Section \ref aos_ssec-basic-arr_class), based on the
query results, and insert \f$c\f$ at its proper
location.\cgalFootnote{The \cgalFootnoteCode{CGAL::insert_non_intersecting_curve<>()}
function template, as all other functions reviewed in this section, is
parameterized by an arrangement type and a point-location type (The
latter must be substituted by a model of theAosPointLocation_2 concept).} The insertion operation thus
hardly requires any geometric operations on top of the ones needed to
answer the point-location queries. Moreover, it is sufficient that the
traits class that substitutes the Traits template parameter of theArrangement_2 class template when the latter is
instantiated models the concept AosBasicTraits_2 concept
(and the concept AosLandmarkTraits_2 if the landmark
point-location strategy is used), and does not have to support the
computation of intersection points between curves. This implies that
using a kernel that provides exact geometric predicates, but
potentially inexact geometric constructions due to round-off errors, is
still sufficient.
The free-function template CGAL::insert_non_intersecting_curve<>(arr, c, pl) is overloaded. There is a variant that instead of accepting a
user-defined point-location object, it constructs a local object of
the walk point-location type, namely, an instance of theArr_walk_along_line_point_location class template, and uses it to
insert the curve.
\subsubsection arr_sssecinsert_x_mon Inserting X-monotone Curves
The time it takes to insert a curve \f$c\f$ using theinsert_non_intersecting_curve() function template is the sum of the
time is takes to locate the two endpoints of \f$c\f$ and the time is
takes to find the exact place for the new two halfedges mapped to
\f$c\f$ in the circular list of halfedges incident to the two vertices
mapped to the endpoints of \f$c\f$, respectively. This makes the
function relatively efficient; however, its preconditions on the input
curves are still rather restricting. Let us assume that the traits
class that substitutes the Traits template parameter of theArrangement_2 class template models the refinedAosXMonotoneTraits_2 concept and supports curve intersection
computations; see Section \ref aos_sec-geom_traits for the exact
details. Given an \f$x\f$-monotone curve, it is sufficient to locate
its left endpoint in the arrangement and to trace its zone
(see Section \ref arr_ssec_zone) until the right endpoint is
reached. Each time the new curve \f$c\f$ crosses an existing vertex or
edge, the curve is split into subcurves (in the latter case, we have
to split the curve associated with the existing halfedge as well) and
new edges are associated with the resulting subcurves. Recall that an
edge is represented by a pair of twin halfedges, so we split it into
two halfedge pairs.
The call \link CGAL::insert<>() insert(arr, c, pl)\endlink performs
this insertion operation. The CGAL::insert<>() function template
accepts an \f$x\f$-monotone curve \f$c\f$, which may intersect some of
the curves already in the arrangement arr, and inserts it into the
arrangement by computing its zone. Users may supply a point-location
object pl or use the default walk point-location strategy (namely,
the variant CGAL::insert<>(arr, c) is also available). The
running-time of this insertion function is proportional to the
complexity of the zone of the curve \f$c\f$.
\cgalAdvancedBegin In some cases users may have a prior knowledge of
the location of the left endpoint of the \f$x\f$-monotone curve
\f$c\f$ they wish to insert, so they can perform the insertion without
issuing any point-location queries. This can be done by calling the
free function template CGAL::insert<>(arr, c, obj), where obj is a
polymorphic object of typeArr_point_location_result<Arrangement_2>::%Type that represents the
location of \f$c\f$s left endpoint in the arrangement. It is a
discriminated union container of the bounded types \link
Arrangement_on_surface_2::Vertex_const_handleVertex_const_handle\endlink, \link
Arrangement_on_surface_2::Halfedge_const_handleHalfedge_const_handle\endlink, or \link
Arrangement_on_surface_2::Face_const_handleFace_const_handle\endlink; see also Section \ref arr_ssecpl.
\cgalAdvancedEnd
\subsubsection aos_ssec-insert_gen Inserting General Curves
So far, all the examples have constructed arrangements of line
segments, where the Arrangement_2 template was instantiated with an
instance of the Arr_segment_traits_2 class template. In this case,
the restriction that CGAL::insert<>() requires an \f$x\f$-monotone
is irrelevant, as all line segments are \f$x\f$-monotone. (Note that
we always deal with weakly \f$x\f$-monotone curves, and we
consider vertical line-segments to be weakly \f$x\f$-monotone).
Consider an arrangement of circles. A circle is obviously not
\f$x\f$-monotone, so CGAL::insert<>() cannot be used in this
case.\cgalFootnote{A key operation performed by \cgalFootnoteCode{CGAL::insert<>()} is
to locate the left endpoint of the curve in the arrangement. A circle,
however, does not have any endpoints!} , it is necessary to subdivide
each circle into two \f$x\f$-monotone circular arcs, namely, its upper
half and its lower half, and to insert the two individual
\f$x\f$-monotone arcs.
The free function template CGAL::insert<>() is overloaded. It is
possible to another version of this function and pass a curve that is
not necessarily \f$x\f$-monotone, but this is subject to an important
condition. Consider the call \link CGAL::insert<>() insert(arr, c, pl)\endlink, where \f$c\f$ is not necessarily \f$x\f$-monotone. In
this case the type of arr must be an instance of theArrangement_2 class template, where the Traits
template parameter is substituted by a traits class that models the
concept AosTraits_2, which refines theAosXMonotoneTraits_2 concept. It has to define an additional
\link AosTraits_2::Curve_2 Curve_2\endlink type, which may
differ from the \link AosBasicTraits_2::X_monotone_curve_2X_monotone_curve_2\endlink type. It also has to support the
subdivision of curves of this new type into \f$x\f$-monotone curves
and possibly singular points; see the exact details in Section \ref
aos_sec-geom_traits. The CGAL::insert<>(arr, c, pl) function
performs the insertion of the curve \f$c\f$ that does not need to be
\f$x\f$-monotone, into the arrangement by subdividing it into
\f$x\f$-monotone subcurves and inserting all individual
\f$x\f$-monotone subcurves. Users may supply a point-location objectpl, or use the default walk point-location strategy by callingCGAL::insert<>(arr, c).
\subsubsection arr_sssecinsert_point Inserting Points
The Arrangement_2 class template has a member function that inserts
a point as an isolated vertex in a given face. The free function
template CGAL::insert_point<>(arr, p, pl) inserts a vertex that
corresponds to the point \f$p\f$ into arr at an arbitrary
location. It uses the point-location object pl to locate the point
in the arrangement (by default, the walk point-location strategy is
used), and acts according to the result as follows:
In all cases, the function returns a handle to the vertex
associated with \f$p\f$.
The type of arr must be and instance of the Arrangement_2 class
template instantiated with a traits class that models theAosXMonotoneTraits_2 concept, as the insertion operation may
involve the splitting of curves.
\subsubsection arr_sssecinsert_ex Inserting Intersecting Line Segments (code example)
\cgalFigureBegin{aos_fig-incremental_insertion,incremental_insertion.png}
An arrangement of five intersecting line segments, as constructed in
\ref Arrangement_on_surface_2/incremental_insertion.cpp and
\ref Arrangement_on_surface_2/aggregated_insertion.cpp. The
segment endpoints are marked by black disks and the arrangement
vertices that correspond to intersection points are marked by
circles. The query point \f$q\f$ is marked with a cross and the face
that contains it is shaded.
\cgalFigureEnd
The program below constructs an arrangement of five intersecting
line-segments \f$s_1, \ldots, s_5\f$. It is known that \f$s_1\f$ and
\f$s_2\f$ do not intersect, soCGAL::insert_non_intersecting_curve<>() is used to insert them into
the empty arrangement. The rest of the segments are inserted usingCGAL::insert<>(). Using a kernel that constructs geometric objects
in an inexact manner (due to round-off errors) may yield a program that
computes incorrect results and crashes from time to time. This is
avoided by using a kernel that provides exact geometric-object
constructions as well as exact geometric-predicate evaluations. The
header file arr_exact_construction_segments.h, just like the header
file arr_inexact_construction_segments.h, contains the definitions
for arrangements of line segments. Unlike the latter, it uses a
kernel suitable for arrangements induced by curves that intersect each
other, namely a kernel that is exact always. Note that we alternately
use the naive point-location strategy, given explicitly to the
insertion functions, and the default walk point-location strategy.
The resulting arrangement is depicted in
\cgalFigureRef{aos_fig-incremental_insertion}, where the vertices that
correspond to segment endpoints are drawn as dark discs, and the
vertices that correspond to intersection points are drawn as
circles. It consists of 13 vertices, 16 edges, and 5 faces. We also
perform a point-location query on the resulting arrangement. The query
point \f$q\f$ is drawn as a plus sign. The face that contains it is
drawn with a shaded texture. The program calls an instance of the
function template print_arrangement_size(), which prints
quantitative measures of the arrangement; see \ref lst_paz
"code example" for its listing in Section \ref aos_ssec-basic-arr_class.
\cgalExample{Arrangement_on_surface_2/incremental_insertion.cpp}
\subsection arr_ssseczone Other Zone Related Functions
In this section we have described so far free functions that insert
curves and points into a given arrangement. Now we describe functions
that do not change the arrangement at all; nevertheless, they are
closely related to the incremental insertion functions, as they also
use the zone framework.
The free function template %CGAL::do_intersect<>(arr, c, pl) checks
whether the given query curve \f$c\f$ intersects the curves and points
of an existing arrangement arr. If \f$c\f$ is not \f$x\f$-monotone
(that is, it is of type \link AosTraits_2::Curve_2Curve_2\endlink) the curve is subdivided into \f$x\f$-monotone
subcurves and isolated points. Each \f$x\f$-monotone curve (or point)
is checked for intersection in turn using the zone framework. For
points we simply apply point-location. Given an \f$x\f$-monotone
curve, first its left endpoint is located; then, its zone is computed
starting from its left endpoint location. The zone computation
terminates when an intersection with an arrangement curve or point is
found or when the right endpoint is reached. A given point-location
object is used for locating the left endpoint of the curve in the
existing arrangement. There is a variant that instead of accepting a
user-defined point-location object, it constructs a local object of
the walk point-location type, namely, an instance of theArr_walk_along_line_point_location class template, and uses it to
locate the endpoint. If the given curve is \f$x\f$-monotone then the
traits type must model the AosXMonotoneTraits_2 concept. If
the curve is not \f$x\f$-monotone then the traits type must model theAosTraits_2 concept.
The CGAL::zone<>(arr, c, oi, pl) function template computes the zone
of a given \f$x\f$-monotone curve in a given arrangement. More
precisely, it outputs all the arrangement cells (namely, vertices,
edges, and faces) that the input \f$x\f$-monotone curve \f$C\f$
intersects in the order they are discovered when traversing the curve
from left to right. The function uses a given point-location object
to locate the left endpoint of the given curve. There is a variant
that instead of accepting a user-defined point-location object, it
constructs a local object of the walk point-location type, namely, an
instance of the Arr_walk_along_line_point_location class template,
and uses it to locate the endpoint. The traits type must model theAosXMonotoneTraits_2 concept.
\subsection arr_ssec_sweep The Surface-Sweep Algorithm
The famous plane-sweep algorithm introduced by Bentley and Ottmann was
originally formulated for sets of line segments in the plane. The \ref
PkgSurfaceSweep2 package offers a generic implementation of a
generalized version of the original plane-sweep algorithm. It (i)
operates in two-dimensional surfaces (not restricted to the plane),
(ii) accepts various families of \f$x\f$-monotone curves (not only
line segments), and (iii) handles overlaps. (Observe that the original
algorithm did not handle overlaps. Handling overlaps is difficult,
especially for polyline, as two polylines may overlap in more than one
connected component.) The generic implementation serves as the
foundation of a family of concrete operations described in the rest of
this section, such as aggregately constructing an arrangement induced
by a set of curves that lie in a two-dimensional surface and outputting
the overlay of two arrangements.
Given a set of \f$n\f$ input curves, you can insert the curves in the
set into an arrangement incrementally one by one. However, the \ref
PkgArrangementOnSurface2 package also provides a couple of free
(overloaded) functions that aggregately insert a range of curves into
an arrangement using the surface-Sweep framework.
We distinguish between two cases:
(i) The given arrangement arr is empty (has only an unbounded face),
so it must be construct from scratch.
(ii) The given arrangement arr is not empty.
In the first case, we sweep over the input curves, compute their
intersection points, and construct the \dcel that represents their
arrangement. This process is performed in \cgalBigO{(n + k)\log
n} time, where \f$k\f$ is the total number of intersection
points. The running time is asymptotically better than the time needed
for incremental insertion if the arrangement is relatively sparse
(when \f$k\f$ is \cgalBigO{\frac{n^2}{\log n}}), but it is recommended
that this aggregate construction process be used even for dense
arrangements, since the plane-sweep algorithm performs fewer geometric
operations compared to the incremental insertion algorithms, and hence
typically runs much faster in practice.
Another important advantage of the aggregated insertion functions is
that they do not issue point-location queries. Thus, no point-location
object needs to be attached to the arrangement. As explained in
Section \ref arr_ssecpl, there is a trade-off between construction
time and query time in each of the point-location strategies, which
affects the running times of the incremental insertion
process. Naturally, this trade-off is absent in the case of aggregated
insertion.
The example below shows how to construct the same arrangement of five
line segments built incrementally in \ref
Arrangement_on_surface_2/incremental_insertion.cpp depicted in
\cgalFigureRef{aos_fig-incremental_insertion} using the aggregate
insertion function \link CGAL::insert<>() insert()\endlink. Note
that no point-location object needs to be defined and attached to the
arrangement.
\cgalExample{Arrangement_on_surface_2/aggregated_insertion.cpp}
Next we handle the case where we have to insert a set of \f$n\f$
curves into an existing arrangement. Let \f$N\f$ denote the number of
edges in the arrangement. If \f$n\f$ is very small compared to
\f$N\f$ (in theory, we would say that if \f$n = o(\sqrt{N})\f$), we
insert the curves one by one. For larger input sets, we use the
aggregate insertion procedures.
\cgalFigureBegin{aos_fig-global_insertion,global_insertion.png} An
arrangement of intersecting line segments, as constructed in \ref
Arrangement_on_surface_2/global_insertion.cpp. The segments of
\f$\mathcal{S}_1\f$ are drawn in solid lines and the segments of
\f$\mathcal{S}_2\f$ are drawn in dark dashed lines. Note that the
segment \f$s\f$ (light dashed line) overlaps one of the segments in
\f$\mathcal{S}_1\f$. \cgalFigureEnd
The program below aggregately construct an arrangement of a set
\f$\mathcal{S}_1\f$ containing five line segments (drawn as solid
lines). Then, it inserts a single segment \f$s\f$ (drawn as a dotted
line) using the incremental insertion function. Finally, it adds a set
\f$\mathcal{S}_2\f$ with five more line segments (drawn as dashed
lines) in an aggregate fashion. Notice that the line segments of
\f$\mathcal{S}_1\f$ are pairwise interior-disjoint, soinsert_non_intersecting_curves() is safely used. \f$\mathcal{S}_2\f$
also contains pairwise interior-disjoint segments, but as they
intersect the existing arrangement, \link
insert(Arrangement_2&, InputIterator, InputIterator)insert()\endlink must be used to insert them. Also note that the
single segment \f$s\f$ inserted incrementally partially overlaps an
existing arrangement curve; the overlapped portion is drawn as a
dash-dotted line.
\cgalExample{Arrangement_on_surface_2/global_insertion.cpp}
We summarize the three levels of arrangement types
based on curve monotonicity and intersection properties in the table
below. The three levels, starting from the most restrictive, are
(i) \f$x\f$-monotone curves that are pairwise disjoint in their
interior,
(ii) \f$x\f$-monotone curves (which are possibly intersecting one
another), and
(iii) general curves. We list the single-curve insertion functions.
The insertion function template %insert() is overloaded to
(i) incrementally insert a single \f$x\f$-monotone curve,
(ii) incrementally insert a single general curve,
(iii) aggregately insert a set of \f$x\f$-monotone curves, and
(iv) aggregately insert a set of general curves.
The CGAL::insert_non_intersecting_curves<>() function template
aggregately inserts a set of \f$x\f$-monotone pairwise interior-disjoint
curves into an arrangement.
\subsection arr_ssecgl_remove Removing Vertices and Edges
The free functions remove_vertex() and remove_edge() handle the
removal of vertices and edges from an arrangement, respectively. The
difference between these functions and the corresponding member
functions of the Arrangement_2 class template (see Section \ref
arr_sssecmf_iso_verts and Section \ref arr_sssecmf_halfedges) has to do
with the ability to merge two curves associated with adjacent edges to
form a single curve associated with a single edge. An attempt to
remove a vertex or an edge from an arrangement object arr using the
free functions above requires that the traits class used to
instantiate the arrangement type of arr models the conceptAosXMonotoneTraits_2, which refines theAosBasicTraits_2 concept; see Section \ref
aos_sec-geom_traits.
The function template CGAL::remove_vertex<>(arr, v) removes the
vertex \f$v\f$ from the given arrangement arr, where \f$v\f$ is
either an isolated vertex or is a redundant vertex. Namely,
it has exactly two incident edges that are associated with two curves
that can be merged to form a single \f$x\f$-monotone curve. If
neither of the two cases apply, the function returns an indication
that it has failed to remove the vertex.
The function CGAL::remove_edge<>(arr, e) removes the edge \f$e\f$
from the arrangement by simply calling arr.remove_edge(e); see
Section \ref arr_ssecmodify. In addition, if either of the end
vertices of \f$e\f$ becomes isolated or redundant after the removal of
the edge, it is removed as well.
\image html global_removal.png
\image latex global_removal.png
The following example demonstrates the usage of the free removal
functions. It creates an arrangement of four line segment \f$s_1,
\ldots, s_4\f$ forming an H-shape with two horizontal edges induced by
\f$s_1\f$ and \f$s_2\f$ (drawn as dashed lines). Then it removes the
two horizontal edges and clears all redundant vertices (drawn as
lightly shaded discs), such that the final arrangement consists of
just two edges associated with the vertical line segments \f$s_3\f$
and \f$s_4\f$.
\cgalExample{Arrangement_on_surface_2/global_removal.cpp}
\subsection arr_ssec_decompose Vertical Decomposition
As you have already seen, an arrangement face may have a fairly
complicated structure; its outer boundary may be very large, and it
may contain numerous holes. For many practical applications, it is
more convenient to analyze the faces by decomposing each into a finite
number of cells (preferably convex when dealing with arrangements of
linear curves) of constant complexity. Vertical decomposition is a
generic way to achieve such a simplification of the arrangement.
Given an arrangement, we consider each arrangement vertex \f$v\f$, and
locate the feature lying vertically below the vertex and the feature
lying vertically above it, or the unbounded face in case there is no
such feature. (Such a feature is also the result of the vertical
ray-shooting operation from the vertex \f$v\f$, as described in
Section \ref arr_ssecray_shoot.) It is now possible to construct two
vertical segments connecting \f$v\f$ to the feature above it and to
the feature below it, possibly extending the vertical segments to
infinity. The collection of the vertical segments and rays computed
for all arrangement vertices induces a subdivision of the arrangement
into simple cells. There are two types of bounded two-dimensional
cells, as follows:
\cgalFigureBegin{aos_fig-bounded_vd,bounded_vertical_decomposition.png}
An arrangement of four line segments and its vertical decomposition
into pseudo trapezoids, as constructed in \ref
Arrangement_on_surface_2/bounded_vertical_decomposition.cpp. The
segments of the arrangement are drawn in solid blue lines and the
segments of the vertical decomposition are drawn in dark dotted lines.
\cgalFigureEnd
In the case of an arrangement of line segments, two-dimensional cells
of the former type are trapezoids (as they have a pair of parallel
vertical edges), while two-dimensional cells of the latter type are
triangles, which can be viewed as degenerate trapezoids. The unbounded
cells can be similarly categorized into two types. Observe that the
boundary of an unbounded cell contains only one original edge or none
and it may contain only non-vertical edges. The resulting cells are
therefore referred to as pseudo trapezoids. The
\cgalFigureRef{aos_fig-bounded_vd} depicts the vertical decomposition
of an arrangement induced by four line segments, as constructed in
\ref Arrangement_on_surface_2/bounded_vertical_decomposition.cpp and
listed below. The decomposition consists of 14 pseudo trapezoids, out
of which two are bounded triangles, two are bounded trapezoids, and 10
are unbounded trapezoids.
The function template decompose() accepts an arrangement
\f$\mathcal{A}\f$ and outputs for each vertex \f$v\f$ of
\f$\mathcal{A}\f$ a pair of features---one that directly lies below
\f$v\f$ and another that directly lies above \f$v\f$. It is
implemented as a plane-sweep algorithm, which aggregately computes the
set of pairs of features. Let \f$v\f$ be a vertex of
\f$\mathcal{A}\f$. The feature above (respectively below) \f$v\f$ may
be one of the following:
\cgalExample{Arrangement_on_surface_2/bounded_vertical_decomposition.cpp}
As you might have noticed, the code above contains a call to an
instance of a the function template read_objects(). It reads the
description of geometric objects from a file and constructs them. It
accepts the name of an input file that contains the plain-text
description of the geometric objects and an output iterator for
storing the newly constructed objects. When the function is
instantiated, the first template parameter, namely Type, must be
substituted by the type of objects to read. It is assumed that an
extractor operator (>>) that extracts objects of the given type from
the input stream is available. The listing of the function template,
which is defined in the file read_objects.h, is omitted here
\cgalAdvancedBegin
In Section \ref aos_sec-unbounded you will learn that we also support
arrangements induced by unbounded curves, referred to as
unbounded arrangements. For such an arrangement we maintain an
imaginary rectangle with left, right, bottom, and top boundaries,
which bound the concrete vertices and edges of the
arrangement. Halfedges that represent the boundaries of the rectangle
are fictitious. The decompose() function
template handles unbounded arrangements. If \f$v\f$ is a vertex of an
unbounded arrangement, and \f$v\f$ is incident to an unbounded face
and there is no curve lying above (respectively below) it, the feature
above (respectively below) \f$v\f$ returned by the function templatedecompose() is a fictitious edge.
\cgalAdvancedEnd
\section aos_sec-unbounded Arrangements of Unbounded Curves
All the arrangements constructed and manipulated in previous chapters
were induced only by line segments, which are, in particular, bounded
curves. Such arrangements always have one unbounded face that contains
all other arrangement features. In this section we explain how to
construct arrangements of unbounded curves. For simplicity of
exposition, we stay with linear objects and restrict our examples in
this section to lines and rays. However, the discussion in this
section, as well as the software described, apply more generally to
arbitrary curves in two-dimensional surfaces.
\subsection aos_ssec-unbounded-rep Representing Arrangements of Unbounded Curves
Given a set \f$\mathcal{C}\f$ of unbounded curves, a simple approach
for representing the arrangement induced by \f$\mathcal{C}\f$ would be
to clip the unbounded curves using an axis-parallel rectangle that
contains all finite curve endpoints and intersection points between
curves in \f$\mathcal{C}\f$. This process would result in a set of
bounded curves (line segments if \f$\mathcal{C}\f$ consists of lines
and rays), and it would be straightforward to compute the arrangement
induced by this set. However, we would like to operate directly on
the unbounded curves without having to preprocess them. Moreover, if
we are not given all the curves inducing the arrangement in advance,
then the choice of a good bounding rectangle may change as more curves
are introduced.
\cgalFigureBegin{aos_fig-unb_dcel,unb_dcel.png}
A \dcel representing an arrangement of four lines. Halfedges are
drawn as thin arrows. The vertices \f$v_1, \ldots, v_8\f$ lie at
infinity, and are not associated with valid points. The halfedges that
connect them are fictitious, and are not associated with concrete
curves. The face denoted \f$f_0\f$ (lightly shaded) is the fictitious
"unbounded face" which lies outside the bounding rectangle (dashed)
that bounds the actual arrangement. The four fictitious vertices
\f$v_{\rm bl}, v_{\rm tl}, v_{\rm br}\f$ and \f$v_{\rm tr}\f$
represent the four corners of the imaginary bounding rectangle.
\cgalFigureEnd
Instead of an explicit approach, we use an implicit bounding rectangle
embedded in the \dcel structure. \cgalFigureRef{aos_fig-unb_dcel}
shows the arrangement of four lines that subdivide the plane into
eight unbounded faces and two bounded ones. Notice that in this case
portions of the unbounded faces now have outer boundaries (those
portions inside the bounding rectangle), and the halfedges along these
outer CCBs are drawn as arrows. The bounding rectangle is drawn
dashed. The vertices \f$v_1,v_2,\ldots,v_8\f$, which lie on the
bounding rectangle, represent the unbounded ends of the four lines
that approach infinity. The halfedges connecting them, which overlap
with the bounding rectangle, are not associated with geometric curves.
Thus, we refer to them as fictitious. Note that the outer
CCBs of the unbounded faces contain fictitious halfedges. The twins of
these halfedges form together one connected component that corresponds
to the entire imaginary rectangle. It forms a single hole in the face
\f$\tilde{f}\f$. We refer to \f$\tilde{f}\f$ as fictitious,
since it does not correspond to a real two-dimensional cell of the
arrangement. Finally, there are four additional vertices denoted by
\f$v_{\rm bl}, v_{\rm tl}, v_{\rm br}\f$, and \f$v_{\rm tr}\f$, which
coincide with the bottom-left, top-left, bottom-right, and top-right
corners of the bounding rectangle, respectively. They do not lie on
any curve, and are referred to as fictitious as well. These
four vertices identify each of the fictitious edges as lying on the
top, the bottom, the left, and the right edge of the imaginary
bounding rectangle. The four fictitious vertices exist even when the
arrangement is empty: In this case they are connected by four pairs of
fictitious halfedges that define a single unbounded face (which
represents the entire \f$\mathbb{R}^2\f$ plane) lying inside the
imaginary bounding rectangle and a fictitious face lying outside.
In summary, there are four types of arrangement vertices, which differ
from one another by their location with respect to the imaginary
bounding rectangle as follows:
- \anchor type-normal A vertex, associated with a point in \f$\mathbb{R}^2\f$. Such a vertex always lies inside the bounding rectangle and has bounded coordinates..
- \anchor type-unbounded A vertex that represents an unbounded end of an \f$x\f$-monotone curve that approaches \f$x = -\infty\f$ or at \f$x = \infty\f$. In case of a horizontal line or a curve with a horizontal asymptote, the \f$y\f$-coordinate of the curve end may be finite (see, for example, the vertices \f$v_2\f$ and \f$v_7\f$ in \cgalFigureRef{aos_fig-unb_dcel}), but in general the curve end also approaches \f$y = \pm\infty\f$; see for instance the vertices \f$v_1\f$, \f$v_3\f$, \f$v_6\f$ and \f$v_8\f$ in \cgalFigureRef{aos_fig-unb_dcel}). For convenience, we always take a "tall" enough bounding rectangle and treat such vertices as lying on either the left or the right rectangle edges (that is, if a curve approaches \f$x = -\infty\f$, its left end will be represented by a vertex on the left edge of the bounding rectangle, and if it approaches \f$x = \infty\f$, its right end will be represented by a vertex on the right edge).
- \anchor typeunboundedvertical A vertex that represents the unbounded end of a vertical linear curve (line or ray) or of a curve with a vertical asymptote (finite \f$x\f$-coordinate and an unbounded \f$y\f$-coordinate). Such a vertex always lies on one of the horizontal edges of the bounding rectangle (either the bottom one if \f$y = -\infty\f$, or the top one if \f$y = \infty\f$). The vertices \f$v_4\f$ and \f$v_5\f$ in \cgalFigureRef{aos_fig-unb_dcel} are of this type.
- \anchor typefictitious The fictitious vertices that represent the four corners of the bounding rectangle.
\cgalFigureBegin{aos_fig-unb_asymptote,unb_asymptote.png}
The portions of a horizontal line, a vertical line, and two rectangular
hyperbolas with horizontal and vertical asymptotes confined to the
imaginary bounding rectangle.
\cgalFigureEnd
A vertex (at infinity) of Type 2 or Type 3 above always has three
incident edges---one concrete edge that is associated with an
unbounded portion of an \f$x\f$-monotone curve, and two fictitious
edges connecting the vertex to its neighboring vertices at infinity or
the corners of the bounding rectangle; see
\cgalFigureRef{aos_fig-unb_asymptote}. Fictitious vertices (of type 4
above) have exactly two incident fictitious edges. See Section \ref
aos_sec-geom_traits for an explanation on how the traits-class
interface helps impose the fact that a vertex at infinity is incident
to a single non-fictitious edge.
The traits class that substitutes the Traits parameter of theArrangement_2 class template when the latter is
instantiated specifies whether the arrangement instance supports
unbounded curves through the definitions of some tags nested in the
traits class; see Section \ref aos_sssec-geom_traits-concepts_basic
for details. Every arrangement that supports unbounded curves supports
bounded curves as well, but not vice versa. Maintaining an arrangement
that supports unbounded curves incurs an overhead due to the necessity
to manage the imaginary bounding rectangle. If you know beforehand
that all input curves that induce a particular arrangement are
bounded, define your arrangement accordingly. That is, use a traits
class that does not support unbounded curves.
\subsubsection arr_sssecunb_basic Basic Manipulation and Traversal Methods
The types \link Arrangement_on_surface_2::Vertex Vertex\endlink,
\link Arrangement_on_surface_2::Halfedge Halfedge\endlink, and \link
Arrangement_on_surface_2::Face Face\endlink nested in theArrangement_2 class template support the methods described below in
addition to the ones listed in Section \ref arr_ssectraverse. Let
\f$v\f$, \f$e\f$, and \f$f\f$ be handles to a vertex of type \link
Arrangement_on_surface_2::Vertex Vertex\endlink, a halfedge of type
\link Arrangement_on_surface_2::Halfedge Halfedge\endlink, and a
face of type \link Arrangement_on_surface_2::Face Face\endlink,
respectively.
The nested \link Arrangement_on_surface_2::Vertex Vertex\endlink
type also provides the Boolean predicate \link
Arrangement_on_surface_2::Vertex::is_at_open_boundary()
is_at_open_boundary()\endlink. If the call \link
Arrangement_on_surface_2::Vertex::is_at_open_boundary()
v->is_at_open_boundary()\endlink predicates evaluates to false,
you can access the point associated with \f$v\f$. Otherwise, \f$v\f$
is not associated with a \link Arrangement_on_surface_2::Point_2
Point_2\endlink object, as it represents the unbounded end of a
curve that approaches infinity.
Let \f$p_l = (x_l,y_l)\f$ and \f$p_r = (x_r,y_r)\f$ be the left and
right endpoints of a bounded \f$x\f$-monotone curve \f$c\f$,
respectively. We say that a point \f$p = (x_p,y_p)\f$ lies in the
\f$x\f$-range of \f$c\f$ if \f$x_l \leq x_p \leq x_r\f$. Let \f$p_l =
(x_l,y_l)\f$ be the left endpoint of an \f$x\f$-monotone curve \f$c\f$
unbounded from the right. A point \f$p = (x_p,y_p)\f$ lies in the
\f$x\f$-range of \f$c\f$ if \f$x_l \leq x_p\f$. Similarly, A point
\f$p = (x_p,y_p)\f$ lies in the \f$x\f$-range of an \f$x\f$-monotone
curve unbounded from the left if \f$x_p \leq x_r\f$. Naturally, every
point \f$p \in \mathbb{R}^2\f$ is in the \f$x\f$-range of an
\f$x\f$-monotone curve unbounded from the left and from the right.
\cgalAdvancedBegin
The function template is_in_x_range() listed below, and defined in
the file is_in_x_range.h, checks whether a given point \f$p\f$ is in
the \f$x\f$-range of the curve associated with a given halfedge
\f$e\f$. The function template also exemplifies how some of the above
functions can be used. The traits functor \link
AosBasicTraits_2::Compare_x_2 Compare_x_2\endlink used in
the code is described in Section \ref
aos_sssec-geom_traits-concepts_basic.
\code
// Check whether the given point is in the x-range of the curve associated
// with the given halfedge.
template
bool is_in_x_range(typename Arrangement::Halfedge_const_handle he,
const typename Arrangement::Point_2& p,
const typename Arrangement::Traits_2& traits)
{
auto cmp = traits.compare_x_2_object();
// Compare p with the source vertex (which may lie at x = +/- oo).
CGAL::Arr_parameter_space src_px = e->source()->parameter_space_in_x();
CGAL::Comparison_result res_s =
(src_px == CGAL::ARR_LEFT_BOUNDARY) ? CGAL::SMALLER :
((src_px == CGAL::ARR_RIGHT_BOUNDARY) ? CGAL::LARGER :
cmp(e->source()->point(), p));
if (res_s == CGAL::EQUAL) return true;
// Compare p with the target vertex (which may lie at x = +/- oo).
CGAL::Arr_parameter_space trg_px = e->target()->parameter_space_in_x();
CGAL::Comparison_result res_t =
(trg_px == CGAL::ARR_LEFT_BOUNDARY) ? CGAL::SMALLER :
((trg_px == CGAL::ARR_RIGHT_BOUNDARY) ? CGAL::LARGER :
cmp(e->target()->point(), p));
// p lies in the x-range of the halfedge iff its source and target lie
// at opposite x-positions.
return (res_s != res_t);
}
\endcode
\cgalAdvancedEnd
It is important to observe that the call \link
Arrangement_on_surface_2::number_of_vertices()arr.number_of_vertices()\endlink does not count the vertices at
infinity in the arrangement arr. To find out this number you need to
issue the call \link
Arrangement_on_surface_2::number_of_vertices_at_infinity()arr.number_of_vertices_at_infinity()\endlink. Similarly, \link
Arrangement_on_surface_2::number_of_edges()arr.number_of_edges()\endlink does not count the fictitious edges
(whose number is always \link
Arrangement_on_surface_2::number_of_vertices_at_infinity()arr.number_of_vertices_at_infinity()\endlink + 4) and \link
Arrangement_on_surface_2::number_of_faces()arr.number_of_faces()\endlink does not count the fictitious
faces. The vertex, halfedge, edge, and face iterators defined by theArrangement_2 class template only go over true features of the
arrangement; namely, vertices at infinity and fictitious halfedges and
fictitious faces are skipped. On the other hand, theCcb_halfedge_circulator of the outer boundary of an unbounded face
or the Halfedge_around_vertex_circulator of a vertex at infinity do
traverse fictitious halfedges. While an arrangement induced by bounded
curves has a single unbounded face, an arrangement induced by
unbounded curves may have several unbounded faces. The call \link
Arrangement_on_surface_2::number_of_unbounded_faces()arr.number_of_unbounded_faces()\endlink returns the number of
unbounded arrangement faces (Thus, \link
Arrangement_on_surface_2::number_of_faces()arr.number_of_faces()\endlink - \link
Arrangement_on_surface_2::number_of_unbounded_faces()arr.number_of_unbounded_faces()\endlink is the number of bounded
faces). The calls \link
Arrangement_on_surface_2::unbounded_faces_begin()arr.unbounded_faces_begin()\endlink and \link
Arrangement_on_surface_2::unbounded_faces_end()arr.unbounded_faces_end()\endlink return iterators of the typeArrangement_on_surface_2::Unbounded_face_iterator (or its
non-mutable, const, counterpart type) that define the range of the
arrangement unbounded faces. Naturally, the value-type of this
iterator is \link Arrangement_on_surface_2::Face Face\endlink.
There is no way to directly obtain the fictitious vertices that
represent the four corners of the imaginary bounding
rectangle. However, you can obtain the fictitious face through the
call \link Arrangement_on_surface_2::fictitious_face()arr.fictitious_face()\endlink, and then iterate over the boundary of
its single hole, which represents the imaginary bounding
rectangle. Recall that an arrangement of bounded curves does not have
a fictitious face. In this case the call above returns a null handle.
The example below exhibits the difference between an arrangement
induced by bounded curves, which has a single unbounded face, and an
arrangement induced by unbounded curves, which may have several
unbounded faces. It also demonstrates the usage of the insertion
function for pairwise interior-disjoint unbounded curves. In this case
we use an instance of the traits class-templateArr_linear_traits_2<Kernel> to substitute the Traits parameter of
the Arrangement_2 class template when instantiated.
This traits class-template is capable of representing line segments as
well as unbounded linear curves, namely, lines and rays. Observe that
objects of the \link Arr_linear_traits_2::X_monotone_curve_2X_monotone_curve_2\endlink type nested in this traits class-template
are constructible from objects of types \link
Arr_linear_traits_2Line_2\endlink, \link
Arr_linear_traits_2Ray_2\endlink, and \link
Arr_linear_traits_2Segment_2\endlink also nested
in the traits class-template. These three types and the \link
Arr_linear_traits_2Point_2\endlink type are
defined by the traits class-template Arr_linear_traits_2<Kernel> to
be the corresponding types of the kernel used to instantiate the
traits class-template; see Paragraph \ref arr_sssectr_linear.
\cgalFigureBegin{aos_fig-unbounded_non_intersecting,unbounded_non_intersecting.png}
An arrangement of unbounded linear objects, as constructed in
\ref Arrangement_on_surface_2/unbounded_non_intersecting.cpp.
\cgalFigureEnd
The first three curves, \f$c_1\f$, \f$c_2\f$, and \f$c_3\f$, are
inserted using the specialized insertion functions for
\f$x\f$-monotone curves whose location in the arrangement is
known. Notice that inserting an unbounded curve in the interior of an
unbounded face, or from an existing vertex that represents an bounded
end of the curve, may cause an unbounded face to split. (This is never
the case when inserting a bounded curve---compare with Section \ref
arr_sssecmf_insert_cv.) Three additional rays are then inserted
incrementally using the insertion function for \f$x\f$-monotone curves,
the interior of which is disjoint from all arrangement features; see
the illustration in
\cgalFigureRef{aos_fig-unbounded_non_intersecting}). Finally, the
program prints the size of the arrangement using the function templateprint_unbounded_arrangement_size() defined in the header filearr_print.h. (Its listing is omitted here.) The program also
traverses the outer boundaries of its six unbounded faces and prints
the curves along these boundaries. The header file arr_linear.h,
which contains the definitions used by the example program, is listed
immediately after the listing of the main function.
\cgalExample{Arrangement_on_surface_2/unbounded_non_intersecting.cpp}
The type definitions used by the example below, as well as by other
examples that use the Arr_linear_traits_2 class template, are listed
next. These types are defined in the header file arr_linear.h.
\code
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Arr_linear_traits_2.h>
#include <CGAL/Arrangement_2.h>
using Kernel = CGAL::Exact_predicates_exact_constructions_kernel;
using Number_type = Kernel::FT;
using Traits = CGAL::Arr_linear_traits_2
using Point = Traits::Point_2;
using Segment = Traits::Segment_2;
using Ray = Traits::Ray_2;
using Line = Traits::Line_2;
using X_monotone_curve = Traits::X_monotone_curve_2;
using Arrangement = CGAL::Arrangement_2;
using Vertex_handle = Arrangement::Vertex_handle;
using Halfedge_handle = Arrangement::Halfedge_handle;
using Face_handle = Arrangement::Face_handle;
\endcode
\subsubsection arr_sssec-unb_global Free Functions
All the free functions that operate on arrangements of bounded curves
(see Section \ref arr_secgl_funcs) can also be applied to arrangements
of unbounded curves. For example, consider a container of linear
curves that has to be inserted into an arrangement object, the type of
which is an instance of the Arrangement_2 class
template, where the Traits parameter is substituted by the traits
class that handles linear curves; see Section \ref
arr_sssectr_linear. You can do it incrementally; namely, insert the
curves one by one as follows:
\code
for (auto it = curves.begin(); it != curves.end(); ++it) insert(arr, *it);
\endcode
Alternatively, the curves can be inserted aggregately using a single
call as follows:
\code
insert(arr, curves.begin(), curves.end());
\endcode
It is also possible to issue point-location queries and vertical
ray-shooting queries (see also Section \ref arr_secqueries) on
arrangements of lines, where the only restriction is that the query
point has finite coordinates. Note that all the point-location
strategies mentioned above, except the trapezoidal map strategy, are
capable of handling arrangements of unbounded curves.
\subsection arr_ssec-unb_duality Point-Line Duality
In the following example we show how an arrangement of unbounded lines
is utilized to solve the following problem: Given a set of points,
does the set contain at least three collinear points? In this example
a set of input points is read from a file. The file points.dat is
used by default. It contains definitions of \f$100\f$ points randomly
selected on the grid \f$[-10000,10000]\times[-10000,10000]\f$. We
construct an arrangement of the dual lines, where the line \f$p^{}\f$
dual to the point \f$p = (x_p, y_p)\f$ is given by the equation \f$y =
x_px - y_p\f$, and check whether three (or more) of the dual lines
intersect at a common point, by searching for a (dual) vertex, whose
degree is greater than \f$4\f$. If such a vertex exists, then there
are at least three dual lines that intersect at a common point, which
implies that there are at least three collinear points.
\cgalExample{Arrangement_on_surface_2/dual_lines.cpp}
Note that there are no three collinear points among the points defined
in the input file points.dat. In the second part of the example the
existence of a collinear triple is forced and verified as follows. A
line dual to the midpoint of two randomly selected points is
introduced, and inserted into the arrangement. This operation is
followed by a test that verifies that a vertex of degree greater than
\f$4\f$ exists.
\section aos_sec-curved_surfaces Arrangements on Curved Surfaces
We are given a surface \f$S\f$ in \f$\mathbb{R}^3\f$ and a set
\f$\mathcal{C}\f$ of curves embedded in this surface. The curves
subdivide \f$S\f$ into cells of dimension 0 (vertices), 1
(edges), and 2 (faces). This subdivision is the
arrangement \f$\mathcal{A}(\mathcal{C})\f$ induced by
\f$\mathcal{C}\f$ on \f$S\f$. Arrangements embedded in curved surfaces
in \f$\mathbb{R}^3\f$ are generalizations of arrangements embedded in
the plane. In this section we explain how to construct, maintain, and
manipulate two-dimensional arrangements embedded in orientable
parametric surfaces in three dimensions, such as planes, cylinders,
spheres, and tori, and surfaces homeomorphic to them; see
\cgalFigureRef{aos_fig-surfaces}. Such arrangements have many
theoretical and practical applications; see, e.g.,
\cgalCite{as-aa-00}, \cgalCite{cgal:bfhks-apsca-10},
\cgalCite{cgal:bfhmw-apsgf-10}, \cgalCite{cgal:fhktww-a-07}, and
\cgalCite{cgal:h-a-04}.
\cgalFigureAnchor{aos_fig-surfaces}
| \image html sphere.png | \image html cylinder.png | \image html cone.png | \image html ellipsoid.png | \image html torus.png | \image html paraboloid.png | (a) Sphere | (b) Cylinder | (c) Cone | (d) Ellipsoid | (e) Torus | (f) Paraboloid |
|---|
\cgalFigureCaptionBegin{aos_fig-surfaces}
Various two-dimensional parametric surfaces, arrangements on which
are supported by the framework.
\cgalFigureCaptionEnd
\subsection aos_ssec-curved_surfaces-parametric Parametric Surfaces
We use \f$\overline{\mathbb{R}}\f$ to denote the compactified real line
\f$\mathbb{R} \cup {-\infty,+\infty}\f$. The mapping
\f$x \mapsto x/(1 - x^2)\f$ is a homeomorphism between
\f$(-1,+1)\f$ and \f$\mathbb{R}\f$ and between \f$[-1,+1]\f$ and
\f$\overline{\mathbb{R}}\f$. So you may also think of finite intervals
instead of the (compactified) real line in what follows.
A parametric surface \f$S\f$ is given by a continuous function
\f$\phi_S: \Phi \rightarrow \mathbb{R}^3\f$, where the domain \f$\Phi
= X \times Y\f$ is a rectangular two-dimensional parameter space and
\f$S = \phi_S(\Phi)\f$. The sets \f$X\f$ and \f$Y\f$ are open,
half-open, or closed intervals with endpoints in
\f$\overline{\mathbb{R}}\f$. We use \f$x_{\rm min}\f$, \f$x_{\rm
max}\f$, \f$y_{\rm min}\f$, and \f$y_{\rm max}\f$ to denote the
endpoints of \f$X\f$ and \f$Y\f$, respectively.
A side of the domain is contracted if \f$\phi_S\f$ is constant on it.
The image of the side is called a contraction point.
The bottom and top sides of the domain (similarly for the left and
right sides) are identified if
\f$\phi_S(x,y_{\rm min}) = \phi_S(x, y_{\rm max})\f$ for all
\f$x \in X \f$. The curve \f$x \mapsto \phi_S(x,y_{\rm min})\f$ is
called an identification curve.
Rectangles, strips, quadrants, halfplanes, and planes can be modeled
with \f$\phi_S\f$ being the identity mapping. For example,
\f$\Phi_S(x, y) = (x, y, 0)\f$ with \f$X = Y =(-\infty, +\infty)\f$
parameterizes a plane. Surfaces such as paraboloids can be
modeled through continuous and bijective parameterizations, for
example, \f$\phi_S(x,y) = (x,y,x^2 + y^2)\f$, where
\f$U = V =(-\infty, +\infty)\f$, defines a paraboloid of revolution.
Cylinders, tori, spheres, and surfaces homeomorphic to them, require
more general parameterizations. For example, the unit sphere is
commonly parameterized as
\f$\phi_S(x, y) = (\cos x \cos y, \sin x \cos y, \sin y)\f$, where
\f$\Phi = [-\pi, \pi] \times [-\frac{\pi}{2}, \frac{\pi}{2}]\f$. With
respect to this parameterization, the north and the south pole and
all points on the opposite Prime (Greenwich) Meridian are
non-regular. The north pole \f$(0,0,1)\f$ has infinitely many
pre-images \f$(x, \pi/2)\f$ with \f$-\pi < x < \pi\f$ and so does the
south pole \f$(0,0,-1)\f$. The points on the opposite Prime Meridian
have two pre-images each, namely \f$(-\pi,y)\f$ and \f$(\pi,y)\f$
with \f$-\pi/2 \leq y \leq \pi/2\f$. We say that the top and bottom
side of the domain are contracted and the left and right sides
are identified. These are exactly the kinds of non-injectivity
that we allow.
We give more examples. A triangle with corners
\f$(a_1, b_1), (a_2, b_2)\f$, and \f$(a_3, b_3)\f$ can be
parameterized via \f$\Phi = [0,1] \times [0,1]\f$ with
\f$\phi_S(x,y) = (a_1 + x(a_2 - a_1) + xy(a_3-a_2), b_1 +
x(b_2 - b_1) + xy(b_3-b_2), 0)\f$. The left side of the rectangular
domain contracts to a point. An open or closed cylinder is
modeled by identifying the vertical sides and having \f$Y\f$ open or
closed, respectively. A torus is modeled by identifying the
vertical sides and the horizontal sides. A paraboloid or
half-cone may be modeled by identifying the vertical sides
and contracting one of the horizontal sides to a point. More elegantly,
they are modeled by a bijective parameterization as given above. A
sphere is modeled by identifying the vertical sides and
contracting both horizontal sides. A croissant, a torus with
one pinch point, is modeled by identifying the vertical and
identifying the horizontal sides and, in addition, contracting one of
the pairs. However, the croissant is excluded by our definitions. All
surfaces supported by our framework are locally homeomorphic to a
disk, and hence an \dcel data-structure suffices for representing
arrangements on these surfaces. The croissant is, at the pinch point,
not locally homeomorphic to a disk, and hence a more general
cell-tuple data structure than an \dcel would be needed.
A curve \f$\gamma\f$ is a continuous function \f$\gamma: I \rightarrow
\Phi\f$, where \f$I\f$ is an open, half-open, or closed interval with
endpoints 0 and 1, and \f$\gamma\f$ is injective except at a finite
number of points. If \f$0 \not\in I\f$, the limit \f$\lim_{t
\rightarrow 0+} \gamma(t)\f$ exists (in the closure of \f$\Phi\f$) and
lies in an open side of the boundary. Similarly, if \f$1 \not\in I\f$,
then \f$\lim_{t \rightarrow 1-} \gamma(t)\f$ exists and lies in an
open side of the boundary. A curve \f$c\f$ in \f$S\f$ is the image of
a curve \f$\gamma\f$ in the domain.
A curve is closed in the domain if \f$\gamma(0) =
\gamma(1)\f$; in particular, \f$0 \in I\f$ and \f$1 \in I\f$. A curve
is closed in the surface \f$S\f$ (or simply closed) if
\f$\phi_S(\gamma(0)) = \phi_S(\gamma(1))\f$. A curve \f$\gamma\f$ has
two ends, the 0-end \f$\langle \gamma,0 \rangle\f$ and the
1-end \f$\langle \gamma,1 \rangle\f$. If \f$d \in I\f$, the
\f$d\f$-end has a geometric interpretation. It is a point in
\f$\Phi\f$. If \f$d \not\in I\f$, the \f$d\f$-end has no geometric
interpretation. You may think of it as a point on an open side of the
domain or an initial or terminal segment of \f$\gamma\f$. If \f$d
\not\in I\f$, we say that the \f$d\f$-end of the curve is open. The
equator curve on the sphere in standard parameterization is given by
\f$\gamma(t) = (\pi(2t - 1),0)\f$ for \f$t \in [0,1]\f$. The
\f$0\f$-end of \f$\gamma\f$ is the point \f$(-\pi,0)\f$ in \f$\Phi\f$
and a point on the equator of the sphere. It is closed on the sphere,
but not closed in \f$\Phi\f$. The diagonal \f$(u,u)\f$ in the plane
is, for example, given by \f$\gamma(t) = (x(t),y(t))\f$ and \f$x(t) =
y(t) = (t - 1/2)/(t(1-t))\f$. Both ends of this curve are open. The
\f$d\f$-end of a curve \f$\gamma\f$ is incident to the left side if
either \f$d \in I\f$ and \f$\gamma(d)\f$ lies on the left side or \f$d
\not\in I\f$ and \f$\lim_{t \rightarrow d} \gamma(t)\f$ lies on the
left side, which is then an open side. Similarly for the other sides.
A strongly \f$x\f$-monotone curve is the image of a curve
\f$\gamma\f$, such that if \f$t_1 < t_2\f$ for \f$t_1, t_2 \in I\f$,
then \f$x(t_1) < x(t_2)\f$. A vertical curve is the image of
a curve \f$\gamma\f$, such that \f$x(t) = C\f$ for all \f$t \in I\f$
and some \f$C \in X\f$ and \f$y(t_1) < y(t_2)\f$ for \f$t_1 <
t_2\f$. For instance, every Meridian curve of a sphere parameterized
as above is vertical. An \f$x\f$-monotone curve is either
vertical or strongly \f$x\f$-monotone.
\subsection aos_ssec-curved_surfaces-aos_class The Arrangement on Surface Class Template
The class template Arrangement_on_surface_2<GeomTraits, TopolTraits>
can be used to represent a 2D arrangement embedded in a 3D
surface. The template is parameterized by template parameters
geometry traits and topology traits. The
topology-traits type deals with the topology of the parametric
surface; see Section \ref aos_sec-topol_traits. As explained in the
previous section, a parametric surface \f$S = \phi_S(\Phi)\f$ is given
by a continuous function \f$\phi_S: \Phi \rightarrow \mathbb{R}^3\f$,
where the domain \f$\Phi = X \times Y\f$ is a rectangular
two-dimensional parameter space. \f$X\f$ and \f$Y\f$ are open,
half-open, or closed intervals with endpoints in
\f$\overline{\mathbb{R}}\f$.
The geometry-traits type introduces the type names of the basic
geometric objects (i.e., point, curve, and \f$x\f$-monotone curve)
and the set of operations on objects of these types required to
construct and maintain the arrangement and to operate on it. The
traits-concept hierarchy described in Section \ref aos_sec-geom_traits
accurately defines the requirements imposed on a model of a geometry
traits class. Recall that requirements apply to the parameter space
\f$\Phi = X \times Y\f$; thus, they are defined in terms of \f$x\f$
and \f$y\f$ coordinates. Most requirements apply to all type of
arrangements regardless of the embedding surface. However, several
refined concepts apply only to arrangements on surfaces that are not
in the plane, that is, modeled with \f$\phi_S\f$ not being the
identity mapping. They differ according to the type of the side
boundaries of the parameter space being open, closed, contracted, or
identified.
At this point only one type of arrangements on non planar surfaces is
supported, namely, arrangements embedded in the sphere and induced by
arcs of great circles, also known as geodesic arcs.
\cgalFigureRef{aos_fig-voronoi} shows various Voronoi diagrams the
bisectors of which are geodesic arcs. They are represented as
arrangements induced by geodesic arcs embedded in the sphere.
\cgalFigureAnchor{aos_fig-voronoi}
| \image html voronoi.jpg | \image html degenerate_voronoi.jpg | \image html power_diagram.jpg | \image html degenerate_power_diagram.jpg | (a) | (b) | (c) | (d) |
|---|
\cgalFigureCaptionBegin{aos_fig-voronoi}
Voronoi diagrams on the sphere.
All diagram edges are geodesic arcs.
Sites are drawn in red and Voronoi edges are drawn in blue.
(a) The Voronoi diagram of 32 random points.
(b) A highly degenerate case of Voronoi diagram of 30 point sites on the sphere.
(c) The power diagram of 10 random circles.
(d) A degenerate power diagram of 14 sites on the sphere.
\cgalFigureCaptionEnd
\subsection aos_ssec-curved_surfaces-basic Basic Manipulation and Traversal Methods
The types \link Arrangement_on_surface_2::Vertex Vertex\endlink,
\link Arrangement_on_surface_2::Halfedge Halfedge\endlink, and \link
Arrangement_on_surface_2::Face Face\endlink nested in theArrangement_on_surface_2 class template support the methods listed
in Section \ref arr_ssectraverse and in Section \ref
arr_sssecunb_basic. Additional methods supported by these types are
described next. Let \f$v\f$, \f$e\f$, and \f$f\f$ be handles to a
vertex of type \link Arrangement_on_surface_2::VertexVertex\endlink, a halfedge of type \link
Arrangement_on_surface_2::Halfedge Halfedge\endlink, and a face of
type \link Arrangement_on_surface_2::Face Face\endlink,
respectively.
If you want to determine whether the pre-image of a point \f$p\f$
lies on identified sides, you need to employ one of the two traits
functors \link
AosIdentifiedHorizontalTraits_2::Is_on_x_identification_2
Is_on_x_identification_2\endlink or \link
AosIdentifiedVerticalTraits_2::Is_on_y_identification_2
Is_on_y_identification_2\endlink; see Section \ref
aos_ssec-traits-curved. If the pre-image of an \f$x\f$-monotone
curve \f$c\f$ does not entirely lie on identified sides, you can
determine the location of the pre-image of an endpoint of \f$c\f$
employing either the \link
AosVerticalSideTraits_2::Parameter_space_in_x_2
Parameter_space_in_x_2\endlink functor or the \link
AosHorizontalSideTraits_2::Parameter_space_in_y_2
Parameter_space_in_y_2\endlink functors; see Section \ref
aos_ssec-traits-curved. The operations in both functors accept an
enumeration that indicates the curve end, that is,
CGAL::ARR_MIN_END or CGAL::ARR_MAX_END, in addition to the curve
itself.
\cgalAdvancedBegin
The function template is_in_x_range() listed below, and defined in
the file spherical_is_in_x_range.h checks whether a given point
\f$p\f$, in the interior of the parameter space, is in the
\f$x\f$-range of an \f$x\f$-monotone curve \f$c\f$ that represents a
great-circle arc. It assumes that the left and right sides of the
parameter space are identified and the bottom and top sides are
contracted, which is the settings used by the traits class templateArr_geodesic_arc_on_sphere_traits_2<Kernel,X,Y>; see Section \ref
arr_ssectr_spherical. The traits functors \link
AosBasicTraits_2::Construct_min_vertex_2Construct_min_vertex_2\endlink, \link
AosBasicTraits_2::Construct_max_vertex_2Construct_min_vertex_2\endlink, and \link
AosBasicTraits_2::Compare_x_2 Compare_x_2\endlink used in
the code are described is Section \ref
aos_sssec-geom_traits-concepts_basic. The traits functors \link
AosVerticalSideTraits_2::Parameter_space_in_x_2Parameter_space_in_x_2\endlink, \link
AosHorizontalSideTraits_2::Parameter_space_in_y_2Parameter_space_in_y_2\endlink, \link
AosIdentifiedVerticalTraits_2::Is_on_y_identification_2Is_on_y_identification_2\endlink, and \link
AosHorizontalSideTraits_2::Compare_x_on_boundary_2Compare_x_on_boundary_2\endlink, are described is Section \ref
aos_ssec-traits-curved.
\code
template
bool is_in_x_range(const typename GeometryTraits::X_monotone_curve_2& c,
const typename GeometryTraits::Point_2& p,
const GeometryTraits& traits) {
CGAL_assertion(! traits.is_on_y_identification_2_object()(p));
if (traits.is_on_y_identification_2_object()(c)) return false;
// Note that a great-circle arc that is incident to a pole is vertical.
auto cmp_x_boundary = traits.compare_x_on_boundary_2_object();
auto psy = traits.parameter_space_in_y_2_object();
auto psy_min = psy(c, CGAL::ARR_MIN_END);
if (psy_min == CGAL::ARR_BOTTOM_BOUNDARY)
return cmp_x_boundary(p, c, CGAL::ARR_MIN_END) == CGAL::EQUAL;
auto psy_max = psy(c, CGAL::ARR_MAX_END);
if (psy_max == CGAL::ARR_TOP_BOUNDARY)
return cmp_x_boundary(p, c, CGAL::ARR_MAX_END) == CGAL::EQUAL;
auto psx = traits.parameter_space_in_x_2_object();
auto psx_min = psx(c, CGAL::ARR_MIN_END);
auto psx_max = psx(c, CGAL::ARR_MAX_END);
if ((psx_min == CGAL::ARR_LEFT_BOUNDARY) &&
(psx_min == CGAL::ARR_RIGHT_BOUNDARY))
return true;
auto cmp_x = traits.compare_x_2_object();
if (psx_min == CGAL::ARR_LEFT_BOUNDARY) {
const auto& p_right = traits.construct_max_vertex_2_object()(c);
auto res = cmp_x(p, p_right);
return ((res == CGAL::SMALLER) || (res == CGAL::EQUAL));
}
if (psx_max == CGAL::ARR_RIGHT_BOUNDARY) {
const auto& p_left = traits.construct_min_vertex_2_object()(c);
auto res = cmp_x(p_left, p);
return ((res == CGAL::SMALLER) || (res == CGAL::EQUAL));
}
const auto& p_left = traits.construct_min_vertex_2_object()(c);
auto res = cmp_x(p_left, p);
if (res == CGAL::LARGER) return false;
const auto& p_right = traits.construct_max_vertex_2_object()(c);
res = cmp_x(p, p_right);
if (res == CGAL::LARGER) return false;
return true;
}
\endcode
\cgalAdvancedEnd
\section aos_sec-geom_traits The Geometry Traits
A geometry traits class encapsulates the definitions of the geometric
entities and the implementation of the geometric predicates and
constructions that handle these geometric entities, used by theArrangement_on_surface_2 class template and by the peripheral
modules. The identified minimal requirements imposed by the various
algorithms that apply to arrangements are organized in a hierarchy of
refined geometry-traits concepts. The requirements listed by each
concept include only the utterly essential types and operations needed
to implement specific algorithms. This modular structuring yields
controllable parts that can be produced, maintained, and utilized with
less effort. For each operation, all the preconditions that its
operands must satisfy are specified as well, as these may simplify the
implementation of models of these concepts even further. Each traits
class models one or more concepts. This section contains a detailed
description of the concepts in the refinement hierarchy and the
various traits classes that model these concepts.
All the algebra required for constructing and manipulating
arrangements is concentrated in the traits classes. The knowledge
required to devise a good traits class is very different from the
knowledge required for the development of the rest of the package or
for using the package. It has less to do with computational geometry
and it involves mainly algebra and numerical computation. This way,
traits classes for new types of curves can be developed with little
knowledge of algorithms and data structures in computational
geometry. In this section we discuss how to use existing traits
classes, but we also explain the concepts these traits classes
model---a starting point for every novice developer of such classes.
This section is divided into three parts. The first part describes the
refinement hierarchy of the arrangement geometry-traits concepts. The
second part reviews various models of these concepts. These traits
classes handle different curve families, such as line segments,
polylines, conic arcs, Bézier curves, algebraic curves, and
geodesic arcs on the sphere. The last part introduces decorators for
geometric traits classes. A decorator of a traits class attaches
auxiliary data to the geometric objects handled by the original traits
class, thereby extending it.
\subsection aos_ssec-geom_traits-concepts The Hierarchy of the Geometry Traits Concepts
A hierarchy of related concepts can be viewed as a directed acyclic
graph, where a node of the graph represents a concept and an arc
represents a refinement relation. An arc directed from concept A to concept B
indicates that concept B refines concept
A. A rather large directed acyclic graph
is required to capture the entire hierarchy of the geometry
traits-class concepts. In the following sections we review individual
clusters of the graph and describe the relations between them.
\cgalFigureRef{aos_fig-central_concept_cluster} depicts the central
cluster.
\cgalFigureBegin{aos_fig-central_concept_cluster,central_concept_cluster.png}
The hierarchy of the main geometry traits concepts.
\cgalFigureEnd
\subsubsection aos_sssec-geom_traits-concepts_basic The Basic Concept
A model of the basic concept AosBasicTraits_2 needs to
define the types \link AosBasicTraits_2::Point_2Point_2\endlink and \link
AosBasicTraits_2::X_monotone_curve_2X_monotone_curve_2\endlink, where objects of the former type are the
geometric mapping of arrangement vertices, and objects of the latter
type are the geometric mapping of edges. In addition, it has to
support the set of predicates listed below. This basic set of
predicates is sufficient for constructing arrangements of bounded
\f$x\f$-monotone curves that are pairwise disjoint in their interiors
and points that do not lie in the interiors of the curves. The basic
set of predicates is also sufficient for answering vertical
ray-shooting queries and point-location queries with a small
exception: Locating a point using the landmark strategy requires a
traits class that models the concept AosLandmarkTraits_2;
see Section \ref aos_sssec-tr_landmarks_concept.
- \link AosBasicTraits_2::Compare_x_2 **`Compare_x_2`**\endlink:
- Compares the \f$x\f$-coordinates of two points.
- \link AosBasicTraits_2::Compare_xy_2 **`Compare_xy_2`**\endlink:
- Compares two points lexicographically, first by their \f$x\f$-coordinates, and if they are equal, by their \f$y\f$-coordinates.
- \link AosBasicTraits_2::Equal_2 **`Equal_2`**\endlink:
- There are two overloaded Boolean operators that test equality. One returns `true` iff two given points represent the same geometric point in the two-dimensional surface, and the second returns `true` iff the graphs of two given \f$x\f$-monotone curves are the same.\cgalFootnote{The predicate that tests equality between graphs of two \f$x\f$-monotone curves is used only for testing as part of the test suite.}
- \link AosBasicTraits_2::Construct_min_vertex_2 **`Construct_min_vertex_2`**\endlink:
- Returns the lexicographically smallest (or left) endpoint of an \f$x\f$-monotone curve.
- \link AosBasicTraits_2::Construct_max_vertex_2 **`Construct_max_vertex_2`**\endlink:
- Returns the lexicographically largest (or right) endpoint of an \f$x\f$-monotone curve.
- \link AosBasicTraits_2::Is_vertical_2 **`Is_vertical_2`**\endlink:
- Determines whether an \f$x\f$-monotone curve is vertical.
- \link AosBasicTraits_2::Compare_y_at_x_2 **`Compare_y_at_x_2`**\endlink:
- Given an \f$x\f$-monotone curve \f$c\f$ and a point \f$p\f$ that lies in the \f$x\f$-range of \f$c\f$, determines whether \f$p\f$ is below, above or lies on \f$c\f$.
- \link AosBasicTraits_2::Compare_y_at_x_right_2 **`Compare_y_at_x_right_2`**\endlink:
- Given two \f$x\f$-monotone curves \f$c_1\f$ and \f$c_2\f$ that share a common minimal (left) endpoint \f$p\f$, determines whether \f$c_1\f$ is above or below \f$c_2\f$ immediately to the right of \f$p\f$, or whether the two curves overlap there.
- \link AosBasicTraits_2::Do_intersect_2 **`Do_intersect_2`**\endlink:
- Determines whether two given \f$x\f$-monotone curves intersect. An input flag determines whether the curve are closed or open.
Every model of the concept AosBasicTraits_2 needs to define
a nested type named \link AosBasicTraits_2::Has_left_categoryHas_left_category\endlink. It determines whether the traits class
supports the following predicate:
- \link AosBasicTraits_2::Compare_y_at_x_left_2 **`Compare_y_at_x_left_2`**\endlink:
- Given two \f$x\f$-monotone curves \f$c_1\f$ and \f$c_2\f$ that share a common maximal (right) endpoint \f$p\f$, determines whether \f$c_1\f$ is above or under \f$c_2\f$ immediately to the left of \f$p\f$, or whether the two curves overlap there.
If the AosBasicTraits_2::Has_left_category type nested in a
model of the basic concept is defined as Tag_true\cgalFootnote{In
principle, the category type may only be convertible to the tag type,
but in practice the category is typically explicitly defined as the
tag.} the model must support the predicate. If the type is defined asTag_false, we resort to an internal version, which is based just on
the reduced set of provided operations. The internal version might be
less efficient, but it exempts the traits developer from the providing
an (efficient) implementation of this predicate---a task that turns
out to be nontrivial in some cases.
The basic set of predicates is sufficient for constructing
arrangements of \f$x\f$-monotone curves that do not reach or approach
the boundary of the parameter space. The nature of the input curves,
whether they are expected to reach or approach the left, right,
bottom, or top side of the boundary of the parameter space, must be
conveyed by the traits class. This is done through the definition of
four additional nested types, namely
- \link AosBasicTraits_2::Left_side_category `Left_side_category`\endlink,
- \link AosBasicTraits_2::Right_side_category `Right_side_category`\endlink,
- \link AosBasicTraits_2::Bottom_side_category `Bottom_side_category`\endlink, and
- \link AosBasicTraits_2::Top_side_category `Top_side_category`\endlink.
\subsubsection aos_sssec-geom_traits-concepts_intersecting Intersections
Constructing an arrangement induced by \f$x\f$-monotone curves that
may intersect in their interior requires operations that are not part
of the AosBasicTraits_2 concept. The additional operations
are listed by the concept AosXMonotoneTraits_2, which
refines the basic arrangement geometry-traits concept described above.
While models of the AosXMonotoneTraits_2 concept still
handle only \f$x\f$-monotone curves, the curves are not restricted to
be disjoint in their interiors. Such a model must be capable of
computing points of intersection between \f$x\f$-monotone curves,
splitting curves at these intersection points to obtain pairs of
interior-disjoint subcurves, and optionally merging pairs of
subcurves. A point of intersection between two curves is also
represented by the \link AosBasicTraits_2::Point_2Point_2\endlink type. A model of the refined concept must define an
additional type called Multiplicity. An object of this type
indicates the multiplicity of the intersection point of two curves,
also referred to as the intersection
number.\cgalFootnote{See, e.g.,
https://en.wikipedia.org/wiki/Intersection_number for more information
about intersection numbers.} Loosely speaking, if two curves intersect
at a point \f$p\f$ but have different tangents (first derivatives) at
\f$p\f$, The point \f$p\f$ is of multiplicity 1. If the curve tangents
are equal but their curvatures (second derivatives) are not, \f$p\f$
is of multiplicity 2, and so on. The multiplicity of points of
intersection between line segments is always 1, and the multiplicity
of a point of intersection between two polylines is 1 if the
intersection point is interior to the corresponding two line segments
of the polylines, and undefined (coded as 0) otherwise. A model of the
refined concept thus has to support the following additional
operations:
- \link AosXMonotoneTraits_2::Split_2 **`Split_2`**\endlink:
- Splits an \f$x\f$-monotone curve \f$c\f$ into two subcurves at a given point \f$p\f$ that lies in the interior of \f$c\f$.
- \link AosXMonotoneTraits_2::Are_mergeable_2 **`Are_mergeable_2`**\endlink:
- Determines whether two \f$x\f$-monotone curves \f$c_1\f$ and \f$c_2\f$ that share a common endpoint can be merged into a single continuous \f$x\f$-monotone curve representable by the traits class.\cgalFootnote{On the face of it this seems a difficult predicate to implement. In practice we use very simple tests to decide whether two curves are mergeable: We check whether their underlying curves are identical and whether they do not bend to form a non-\f$x\f$-monotone curve.}
- \link AosXMonotoneTraits_2::Merge_2 **`Merge_2`**\endlink:
- Merges two mergeable \f$x\f$-monotone curves into a single curve.
- \link AosXMonotoneTraits_2::Intersect_2 **`Intersect_2`**\endlink:
- Computes all intersection points and overlapping sections of two given \f$x\f$-monotone curves. If possible, computes also the multiplicity of each intersection point. Providing the multiplicity of an intersection point is not required, but it can expedite the arrangement construction. Typically, the multiplicity is a byproduct of the intersection computation. However, if it is not available, undefined multiplicity (coded as 0) is assumed. Having the multiplicity of intersection points while constructing arrangements enables the exploitation of the geometric knowledge intersection points may have.
Using a model of the AosXMonotoneTraits_2 it is
possible to construct arrangements of sets of \f$x\f$-monotone curves
(and points) that may intersect one another. The two operations
listed above, regarding the merging of curves, are optional, and should
be provided only if the type Has_merge_category nested in a model of
the AosXMonotoneTraits_2 concept is defined as Tag_true.
Otherwise, it is not possible to merge \f$x\f$-monotone curve and
redundant vertices may be left in the arrangement due to the removal of
edges.
\subsubsection aos_sssec-geom_traits-concepts_arbitrary Supporting Arbitrary Curves
The concept AosTraits_2 refines theAosXMonotoneTraits_2 concept. A model of the refined concept
must define an additional type that is used to represent general, not
necessarily \f$x\f$-monotone and not necessarily continuous, curves,
named \link AosTraits_2::Curve_2 Curve_2\endlink. It also
has to support the subdivision of a curve of that type into a set of
continuous \f$x\f$-monotone curves and isolated points. For example,
the curve \f$c:\ (x^2 + y^2)(x^2 + y^2 - 1) = 0\f$ comprises the unit
circle (the locus of all points for which \f$x^2 + y^2 = 1\f$) and the
origin (the singular point \f$(0,0)\f$). \f$c\f$ should therefore be
subdivided into two circular arcs, the upper part and the lower part
of the unit circle, and a single isolated point. In particular a
model of the refined concept has to support the following additional
operation:
- \link AosTraits_2::Make_x_monotone_2 **`Make_x_monotone_2`**\endlink:
- Divides a given general curve of type \link AosTraits_2::Curve_2 `Curve_2`\endlink into continuous weakly \f$x\f$-monotone curves and isolated points.
Note that a model of the refined concept AosTraits_2 is
required only when using the free insert() function templates (see
Section \ref arr_secgl_funcs) that accept an object of type \link
AosTraits_2::Curve_2 Curve_2\endlink or a range of objects
of that type. In all other cases it is sufficient to use a model of
the AosXMonotoneTraits_2 concept.
\subsubsection aos_sssec-tr_landmarks_concept The Landmark Concept
\cgalFigureBegin{aos_fig-landmark_concept_cluster,landmark_concept_cluster.png}
The traits-concept hierarchy for arrangements associated with the landmark
point-location strategy.
\cgalFigureEnd
The type of an arrangement associated with the landmark point-location
strategy (see Section \ref arr_ssecpl) must be an instance of theArrangement_on_surface_2<GeomTraits, TopolTraits> class template,
where the GeomTraits parameter is substituted by a model of the
concept AosLandmarkTraits_2. (Naturally, it can also model
either the AosXMonotoneTraits_2 concept or theAosTraits_2 concept.) The AosLandmarkTraits_2
concept refines the two concepts AosApproximatePointTraits_2 andAosConstructXMonotoneCurveTraits_2. Each of these two
concepts, in turn, refines the concept AosBasicTraits_2.
A model of the AosApproximatePointTraits_2 concept must define a
fixed precision number type (typically the double-precision
floating-point double) and support the operation below (in addition
to fulfilling the requirements listed by the AosBasicTraits_2
concept).
- \link AosApproximatePointTraits_2::Approximate_2 **`Approximate_2`**\endlink:
- Given a point `p`, approximate the \f$x\f$ and \f$y\f$-coordinates of `p` using a not necessarily multi-precision number type. We use this operation for approximate computations---there are certain operations performed during the search for the location of the point that need not be exact, and that can be performed faster when carried out, for example, using a fixed-precision number type.
A model of the AosConstructXMonotoneCurveTraits_2 concept
must support the operation below (in addition to fulfilling the
requirements listed by the AosBasicTraits_2 concept).
- \link AosConstructXMonotoneCurveTraits_2::Construct_x_monotone_curve_2 **`Construct_x_monotone_curve_2`**\endlink:
- Given two points \f$p_1\f$ and \f$p_2\f$, construct an \f$x\f$-monotone curve connecting \f$p_1\f$ and \f$p_2\f$.
Most traits classes model the AosTraits_2 concept, and some
also model the AosLandmarkTraits_2 concept.
The concept AosConstructCurveTraits_2 refines the conceptAosBasicTraits_2. A model of the AosConstructCurveTraits_2 concept
must support the operation below (in addition to fulfilling the
requirements listed by the AosBasicTraits_2 concept).
\subsubsection aos_sssec-tr_additional_concepts The Construct Curve Concept
- \link AosConstructCurveTraits_2::Construct_curve_2 **`Construct_curve_2`**\endlink:
- Given two points \f$p_1\f$ and \f$p_2\f$, construct a curve connecting \f$p_1\f$ and \f$p_2\f$.
The Arr_polyline_traits_2<SubcurveTraits_2> class template handles
polylines; see Section ~\ref arr_sssectr_polylines.
The type that substitutes the template parameter SubcurveTraits_2
when Arr_polyline_traits_2<SubcurveTraits_2> is instantiated must be
a geometry-traits class that models the concept AosConstructCurveTraits_2
to enable the construction of polylines from a sequence of two or more points.
\subsubsection aos_sssec-tr_approximatetion_concept Supporting Approximations of Curves
Operations on arrangements are guaranteed to be robust only if the
operations of the geometry traits used to carry out the high-level
operations are robust. Geometry traits constructors are guaranteed to
be robust only if the kernel in use supports exact constructions, such
as the Epec kernel. Even if you only intend to perform point-location
queries on an arrangement induced by a collection of curves, you need
an exact-construction kernel, if the curves are not pairwise disjoint
in their interiors. However, when using an exact-construction kernel
you still may desire to perform (independent) operations on
approximations of the points or the interior-disjoint \f$x\f$-monotone
curves of the arrangements in order to gain speed, for example drawing
the arrangement on a raster window. Naturally, reconstructing an
arrangement from the approximations of its points and
interior-disjoint \f$x\f$-monotone curves, may result with an arrangement
of a different topology, or even worse, intersections in the interior
of the curves.
The concept AosApproximateTraits_2 refines the conceptAosApproximatePointTraits_2. In addition to the typeApproximate_number_type required by the latter, a model of theAosApproximateTraits_2 concept must define the typeApproximate_point_2, which is typically defined asCGAL::Cartesian<Approximate_number_type>::%Point_2. Also, it must
support the operations below (in addition to the operation required by
the concept AosApproximatePointTraits_2).
- \link AosApproximateTraits_2::Approximate_2 **`Approximate_2`**\endlink:
- This operation is overloaded with three versions. The first one
returns an approximation of the \f$x\f$- or \f$y\f$-coordinates of a given
point as required by the concept `AosApproximatePointTraits_2`; see
Section \ref aos_sssec-tr_landmarks_concept. The description of the remaining
two follow.
(i) Given a point \f$p\f$, obtain an approximation of \f$p\f$, which is an
object of typeApproximate_point_2. This is trivially
implemented using the first version.(ii) Given an \f$x\f$-monotone curve \f$c\f$, a small number \f$\epsilon\f$ of
typeApproximate_number_typethat serves as an error bound, an
output iterator associated with a container
of approximate points, and a flag that indicates whether to
approximate \f$c\f$ starting from its smallest lexicographic endpoint,
compute a polyline that approximates \f$c\f$ in the indicated
direction, such that the largest distance between \f$c\f$ and the
polyline is not larger than \f$\epsilon\f$. The polyline is
represented as a sequence of approximate points.
\subsubsection aos_ssec-traits-curved Supporting Unbounded Curves or Curved Surfaces
We descend to the bottom level of the hierarchy. The refinements
described in this section provide the requirements imposed on traits
classes used with arrangements induced by curves with boundary
conditions. In particular, these requirements list additional
operations that handle curves that either reach specific sides of the
parameter-space boundary (the pre-images of their endpoints lie on
boundary sides) or approach them (the pre-images of their endpoints
approach boundary sides). These boundary conditions typically occur
with unbounded curves or with arrangements embedded in curved
surfaces. The category types \link
AosBasicTraits_2::Left_side_categoryLeft_side_category\endlink, \link
AosBasicTraits_2::Right_side_categoryRight_side_category\endlink, \link
AosBasicTraits_2::Bottom_side_categoryBottom_side_category\endlink, and \link
AosBasicTraits_2::Top_side_categoryTop_side_category\endlink, nested in every geometry traits class
indicate whether the corresponding boundary side (i.e., left, right,
bottom, and top) is open, closed, contracted, or identified, and
whether this information is relevant at all. When all curves inserted
into the arrangement are expected to neither approach nor reach that
particular boundary side, the information is irrelevant. (Note that we
explicitly distinguish closed-only, contracted, and identified sides,
although the latter two are closed in the parameter space as well.)
Each one of the categories above must be convertible to one of the tag
types listed below according to the boundary condition that should be
handled.
For each of the four sides of the parameter space there are four
available concepts, models of which can be used to handle open,
closed, contracted, and identified sides, respectively. (When curves
are expected to neither reach nor approach a particular side, special
handling is not required; thus, there is no need for a corresponding
concept.) For example, it is required that the category type \link
AosOpenBottomTraits_2::Bottom_side_categoryBottom_side_category\endlink, nested in models of the conceptAosOpenBottomTraits_2, is convertible to the tag typeArr_open_side_tag. This makes for a total of 16 abstract concepts
(combine one of left, right, bottom, and top with one of open, closed,
contracted, identified). Any one of these individual abstract concept
is useless on its own; only concepts that refine a combination of 0,
1, 2, 3, or 4 abstract concepts (which capture the conditions that
occur in the four sides simultaneously) are purposeful. Theoretically,
there are \f$\sum_{i=0}^4\binom{i}{4} \cdot 4^i = 5^4 = 625\f$ such
concepts. However, only a subset of them is meaningful. If a side is
identified, the opposite side must also be identified. In addition, a
contracted side must be adjacent to two identified sides. While this
narrows down the number of tangible concepts, the total number is
still high; in practice we have used only two so far (not counting the
"plain" concept, models of which do not handle boundary conditions at
all), namely, AosOpenBoundaryTraits_2 andAosSphericalBoundaryTraits_2; see below for more
details. Many of the traits class-templates provided by the \ref
PkgArrangementOnSurface2 package do not handle boundary conditions at
all. Some of them, such as the Arr_segment_traits_2 traits (see
Section \ref arr_ssectr_segs) only handle bounded curves. Thus, the
four category types nested in these models are defined to beArr_oblivious_side_tag.
\cgalFigureBegin{aos_fig-open_concept_hierarchy,open_concept_hierarchy.png}
Bottom portion of the refinement hierarchy of the geometry-traits
concepts for curves embedded in an open surface, for instance, the
entire plane.
\cgalFigureEnd
Several predicates are required to handle \f$x\f$-monotone curves that
approach the boundary of the parameter space. These predicates are
sufficient to handle not only curves embedded in an unbounded
parameter space, but also curves embedded in a bounded parameter space
with open boundaries. The arrangement type instantiated with a traits
class that models the combined conceptAosOpenBoundaryTraits_2 shown in
\cgalFigureRef{aos_fig-open_concept_hierarchy} can handle curves that
are open in any direction. Recall that an arrangement that supports
unbounded \f$x\f$-monotone curves maintains an implicit bounding
rectangle in the \dcel structure; see Section \ref
aos_sec-unbounded. If some curves inserted into an arrangement object
are expected to be unbounded; namely, there exists \f$d \in {0,1}\f$
such that \f$\lim_{t \rightarrow d}x(t) = \pm\infty\f$ or \f$\lim_{t
\rightarrow d}y(t) = \pm\infty\f$ holds for at least one input curve
\f$c(t) = (x(t),y(t))\f$, the arrangement template must be
instantiated with a model of the AosOpenBoundaryTraits_2
concept.\cgalFootnote{A curve that reaches the boundary of the
parameter space in this case is open and unbounded.}
\cgalFigureBegin{aos_fig-spherical_concept_hierarchy,spherical_concept_hierarchy.png}
The entire spherical boundary cluster of the refinement hierarchy of the
geometry-traits concepts for curves embedded in a sphere-like parameterized
surface.
\cgalFigureEnd
A suitable geometry-traits component for arrangements embedded in
surfaces homeomorphic to a sphere is a model of the combined conceptAosSphericalBoundaryTraits_2 shown in
\cgalFigureRef{aos_fig-spherical_concept_hierarchy}. Here the two
vertical sides of the parameter space are identified and the two
horizontal sides are contracted.
\cgalFigureBegin{aos_fig-left_side_cluster,left_side_cluster.png}
Top portion of the refinement hierarchy of the geometry-traits
concepts for curves that either reach the left side of the boundary
of the parameter space or approach it. A similar hierarchy also
exists for the right, bottom, and top sides.
\cgalFigureEnd
The shared requirements for the four options of a side are collected
in abstract layers called AosLeftSideTraits_2,AosRightSideTraits_2, AosBottomSideTraits_2, andAosTopSideTraits_2; see, e.g.,
\cgalFigureRef{aos_fig-left_side_cluster}.
\cgalFigureBegin{aos_fig-side_clusters,side_clusters.png}
Top portion of the refinement hierarchy of the geometry-traits
concepts for curves that either reach the vertical sides of the
boundary of the parameter space or approach it, and similarly for
curves that either reach or approach horizontal sides.
\cgalFigureEnd
The shared requirements for the options of opposite sides are
collected in two additional abstract layers calledAosVerticalSideTraits_2 andAosHorizontalSideTraits_2; see
\cgalFigureRef{aos_fig-side_clusters}.
\cgalFigureBegin{aos_fig-identified_clusters,identified_clusters.png}
Top portion of the refinement hierarchy of the geometry-traits
concepts for curves that reach the identified vertical sides of the
parameter space and for curves that reach the identified horizontal
sides of the parameter space.
\cgalFigureEnd
Individual concepts for curves that reach the identified left side of
the parameter space and for curves that reach the identified right
side of the parameter space do not exist, because if one side is
identified the opposite side must be identified as well. Similarly,
individual concepts for curves that reach the identified bottom side
of the parameter space and for curves that reach the identified top
side of the parameter space do not exist either. Instead, the shared
requirements for opposite identified sides are collected in two
additional abstract concepts calledAosIdentifiedVerticalTraits_2 andAosIdentifiedHorizontalTraits_2; see
\cgalFigureRef{aos_fig-identified_clusters}. The former lists
requirements for operations that handle curves that reach identified
vertical sides of the parameter space, and the latter lists
requirements for operations that handle curves that reach identified
horizontal sides of the parameter space.
In the following we list the specific requirements of all the
aforementioned concepts.
The abstract concept AosVerticalSideTraits_2 requires the
following predicate:
- \link AosVerticalSideTraits_2::Parameter_space_in_x_2 **`Parameter_space_in_x_2`**\endlink:
- This three-valued predicate is overloaded with two versions as
follows:
(i) Given a curve \f$c = (x(t), y(t))\f$ and an enumerator that
specifies either the minimum end or the maximum end of the curve, and
thus maps to a parameter value \f$d \in {0,1}\f$, determine the
location of the \f$d\f$-end of \f$c\f$ along the
\f$x\f$-dimension. Formally, if \f$c\f$ is open at its \f$d\f$-end,
determine whether \f$\lim_{t \rightarrow d} x(t)\f$ evaluates to
\f$x_{\rm min}\f$, \f$x_{\rm max}\f$, or a value in between. If
\f$c\f$ is not open at its \f$d\f$-end, determine whether \f$x(d)\f$
is equal to \f$x_{\rm min}\f$, \f$x_{\rm max}\f$, or a value in
between. ReturnCGAL::ARR_LEFT_BOUNDARY,CGAL::ARR_RIGHT_BOUNDARY,
orCGAL::ARR_INTERIOR, accordingly. If \f$c\f$ is vertical and lies
on a vertical boundary; that is, either \f$\forall p=(x_p, y_p) \in c\f$
we have \f$x_p = x_{\rm min}\f$ or \f$\forall p=(x_p, y_p) \in c\f$ we
have \f$x_p = x_{\rm max}\f$, then the boundary side containing \f$c\f$
cannot be open.(ii) Given a point \f$p=(x_p, y_p)\f$, determine the location of the
point with respect to the \f$x\f$-axis. Formally, determine whether
\f$x_p\f$ is equal to \f$x_{\rm min}\f$, \f$x_{\rm max}\f$, or a value
in between. ReturnCGAL::ARR_LEFT_BOUNDARY,CGAL::ARR_RIGHT_BOUNDARY, orCGAL::ARR_INTERIOR, accordingly. If
\f$p\f$ is on a vertical boundary; that is, \f$x_p \in {x_{\rm
min},x_{\rm max}}\f$, then the boundary side containing \f$p\f$
cannot be open and must not be identified. In other words, this
overloaded version is required only for the conceptsAosClosedLeftTraits_2,AosClosedRightTraits_2,AosContractedLeftTraits_2andAosContractedRightTraits_2.
The abstract concept AosHorizontalSideTraits_2 requires the
following predicate:
- \link AosHorizontalSideTraits_2::Parameter_space_in_y_2 **`Parameter_space_in_y_2`**\endlink:
- This three-valued predicate is overloaded with two versions as
follows:
(i) Given a curve \f$c = (x(t), y(t))\f$ and an enumerator that
specifies either the minimum end or the maximum end of the curve, and
thus maps to a parameter value \f$d \in {0,1}\f$, determine the
location of the \f$d\f$-end of \f$c\f$ along the
\f$y\f$-dimension. Formally, if \f$c\f$ is open at its \f$d\f$-end,
determine whether \f$\lim_{t \rightarrow d} y(t)\f$ evaluates to
\f$y_{\rm min}\f$, \f$y_{\rm max}\f$, or a value in between. If
\f$c\f$ is not open at its \f$d\f$-end, determine whether \f$y(d)\f$
is equal to \f$y_{\rm min}\f$, \f$y_{\rm max}\f$, or a value in
between. ReturnCGAL::ARR_BOTTOM_BOUNDARY,CGAL::ARR_TOP_BOUNDARY,
orCGAL::ARR_INTERIOR, accordingly. If \f$c\f$ is horizontal and
lies on a horizontal boundary; that is, either \f$\forall p=(x_p, y_p)
\in c, y_p = y_{\rm min}\f$ or \f$\forall p=(x_p, y_p) \in c, y_p =
y_{\rm max}\f$, then the boundary side containing \f$c\f$ cannot be
open and must not be identified.(ii) Given a point \f$p=(x_p, y_p)\f$, determine the location of the
point along the \f$y\f$-dimension. Formally, determine whether
\f$y_p\f$ is equal to \f$y_{\rm min}\f$, \f$y_{\rm max}\f$, or a value
in between. ReturnCGAL::ARR_BOTTOM_BOUNDARY,CGAL::ARR_TOP_BOUNDARY, orCGAL::ARR_INTERIOR, accordingly. If
\f$p\f$ is on a horizontal boundary; that is, \f$y_p \in {y_{\rm
min},y_{\rm max}}\f$, then the boundary side containing \f$p\f$
cannot be open and must not be identified. In other words, this
overloaded version is required only for the conceptsAosClosedBottomTraits_2,AosClosedTopTraits_2,AosContractedBottomTraits_2andAosContractedTopTraits_2.
The two symmetric predicates above determine the location of a
curve-end in the parameter space. However, in general,
\f$x\f$-coordinates and \f$y\f$-coordinates are handled
differently. This asymmetry is brought on by the various algorithms
applied to arrangements, the input and output arguments of which are
\f$x\f$-monotone curves. Indeed, all curves maintained by any
arrangement are continuous weakly \f$x\f$-monotone curves. A non
\f$x\f$-monotone curve is divided into \f$x\f$-monotone sub curves
(and perhaps points) before it is inserted into an arrangement. This
asymmetry is also reflected in the predicates listed below. They help
determining the order of curve-ends lying on the boundary of the
parameter space with respect to regular points and among each other.
The concepts AosClosedLeftTraits_2,AosClosedRightTraits_2, andAosIdentifiedVerticalTraits_2 require the following
additional predicate:
- \link AosIdentifiedVerticalTraits_2::Compare_y_on_boundary_2 **`Compare_y_on_boundary_2`**\endlink:
- Given two points \f$p_1=(x_{p_1},y_{p_1})\f$ and \f$p_2=(x_{p_2},y_{p_2})\f$, such that at least one of them lies on a vertical boundary side, compare the \f$y\f$-coordinates of the points. That is, compare \f$y_{p_1}\f$ and \f$y_{p_2}\f$. If \f$p_1\f$ (resp. \f$p_2\f$) is on the boundary; that is, \f$x_{p_1} \in \{x_{\rm min},x_{\rm max}\}\f$ (resp. \f$x_{p_2} \in \{x_{\rm min},x_{\rm max}\}\f$), then the vertical boundary side containing \f$p_1\f$ (resp. \f$p_2\f$) must be either closed or identified. In other words, this overloaded version is required only for the concepts `AosClosedLeftTraits_2`, `AosClosedRightTraits_2`, and `AosIdentifiedVerticalTraits_2`.
The concept AosVerticalSideTraits_2 requires the following additional predicate:
- \link AosVerticalSideTraits_2::Compare_y_near_boundary_2 **`Compare_y_near_boundary_2`**\endlink:
| \image html compare_y_near_boundary.png
\image latex compare_y_near_boundary.png | Given two \f$x\f$-monotone curves \f$c_1\f$ and \f$c_2\f$, and an
enumerator \f$i\f$ that specifies either the minimum ends or the
maximum ends of the two curves, and thus maps to a parameter value
\f$d \in {0,1}\f$, compare the \f$y\f$-coordinates of the curves
near their respective ends. More precisely, compare the
\f$y\f$-coordinates of the vertical projections of a point \f$p\f$
onto \f$c_1\f$ and onto \f$c_2\f$. If the enumerator \f$i\f$ specifies
the minimum ends, the curves must approach the left boundary-side. In
this case \f$p\f$ is located sufficiently close to the left
boundary-side, such that the result is invariant under a translation
of \f$p\f$ closer to the left boundary-side. If \f$i\f$ specifies the
maximum ends, the curves must approach the right boundary-side. In
that case \f$p\f$ is located sufficiently close to the rightboundary-side in a similar manner. |
| --- | --- |
The concept AosHorizontalSideTraits_2 requires two additional
predicates:
- \link AosHorizontalSideTraits_2::Compare_x_on_boundary_2 **`Compare_x_on_boundary_2`**\endlink:
- This predicate is overloaded with three versions.
We distinguish between open and non-open sides as explained below.
(i) Given a regular point \f$p=(x_p,y_p)\f$, an \f$x\f$-monotone curve
\f$c = (x(t),y(t))\f$, and an enumerator that specifies either the
minimum end or the maximum end of the curve, and thus maps to a
parameter value \f$d \in {0,1}\f$, compare the \f$x\f$-coordinate of
\f$p\f$ and the \f$x\f$-coordinate of \f$c\f$ at its respective limit
or end depending on whether it is open or non-open. More precisely, if
\f$c\f$ is open at its \f$d\f$-end, compare the values \f$x_p\f$ and
\f$\lim_{t \rightarrow d} x(t)\f$. A precondition assures that \f$c\f$
has a vertical asymptote at its \f$d\f$-end; that is \f$\lim_{t
\rightarrow d} x(t)\f$ is a real value. If \f$c\f$ is not open,
compare the values \f$x_p\f$ and \f$x(d)\f$.| \image html compare_x_on_boundary.png
\image latex compare_x_on_boundary.png | (ii) Given two curves \f$c_1 = (x_1(t),y_1(t))\f$ and \f$c_2 =
(x_2(t),x_2(t))\f$ and enumerators that specify either the minimum end
or the maximum end of each curve, and thus map to parameter values
\f$d_1, d_2 \in {0,1}\f$, compare the \f$x\f$-coordinates of the
curves at their respective limits or ends. More precisely, if
\f$c_1\f$ (resp. \f$c_2\f$) is open at its \f$d_1\f$-end
(resp. \f$d_2\f$-end), use the value \f$\lim_{t \rightarrow d_1}
x_1(t)\f$ (resp. \f$\lim_{t \rightarrow d_2} x_2(t)\f$) for the
comparison. A precondition assures that \f$c_1\f$ (resp. \f$c_2\f$)
has a vertical asymptote at its \f$d_1\f$-end (resp. \f$d_2\f$-end);
that is \f$\lim_{t \rightarrow d_1} x_1(t)\f$ is (resp. \f$\lim_{t
\rightarrow d_2} x_2(t)\f$) a real value. If \f$c_1\f$
(resp. \f$c_2\f$) is not open at its \f$d_1\f$-end
(resp. \f$d_2\f$-end), use the value \f$x_1(d_1)\f$(resp. \f$x_2(d_2)\f$) for the comparison. |
| --- | --- |(iii) Given two points \f$p_1=(x_{p_1},y_{p_1})\f$ and
\f$p_2=(x_{p_2},y_{p_2})\f$, such that at least one of them lies on a
horizontal boundary side, compare the \f$x\f$-coordinates of the
points. That is, compare \f$x_{p_1}\f$ and \f$x_{p_2}\f$. If \f$p_1\f$
(resp. \f$p_2\f$) is on the boundary; that is, \f$y_{p_1} \in {y_{\rm
min},y_{\rm max}}\f$ (resp. \f$y_{p_2} \in {y_{\rm min},y_{\rm
max}}\f$), then the boundary side containing \f$p_1\f$
(resp. \f$p_2\f$) must be either closed or identified. In other words,
this overloaded version is required only for the conceptsAosClosedBottomTraits_2,AosClosedTopTraits_2,
andAosIdentifiedHorizontalTraits_2. - \link AosHorizontalSideTraits_2::Compare_x_near_boundary_2 **`Compare_x_near_boundary_2`**\endlink:
| \image html compare_x_near_boundary.png
\image latex compare_x_near_boundary.png | Given two \f$x\f$-monotone curves \f$c_1\f$ and \f$c_2\f$ and an
enumerator \f$i\f$ that specifies either the minimum ends or the
maximum ends of the two curves, and thus map to parameter values
\f$d_1, d_2 \in {0,1}\f$, respectively, compare the
\f$x\f$-coordinate of the curves near their respective limits or
ends. A precondition assures that the \f$x\f$-coordinates of the
limits or ends of the curves at their respective ends are equal. That
is, the predicateCompare_x_on_boundary_2applied to \f$c_1\f$,
\f$c_2\f$, and \f$i\f$ evaluates toCGAL::EQUAL. Formally, compare
the \f$x\f$-coordinates of the horizontal projection of a point
\f$p\f$ onto \f$c_1\f$ and onto \f$c_2\f$. A precondition assures that
\f$c_1\f$ and \f$c_2\f$ have vertical asymptotes at their respective
ends. Furthermore, both curves approach the same boundary-side, either
the bottom or the top, at their respective ends. If both curves
approach the bottom boundary-side, \f$p\f$ is located far to the
bottom, such that the result is invariant under a translation of
\f$p\f$ farther to the bottom. If both curves approach the topboundary-side, \f$p\f$ is located far to the top in a similar manner. |
| --- | --- |
The concept AosIdentifiedVerticalTraits_2 requires the following
additional predicate:
- \link AosIdentifiedVerticalTraits_2::Is_on_y_identification_2 **`Is_on_y_identification_2`**\endlink:
- This predicate is overloaded with two versions.
(i) Given a point \f$p=(x_p, y_p)\f$, determine whether \f$p\f$ lies
in the image of the vertical (and identified) sides of the
boundary. More precisely, determine whether \f$x_p \in {x_{\rm
min},x_{\rm max}}\f$ for all pre-images of \f$p\f$.(ii) Given a curve \f$c\f$, determine whether the entire curve \f$c\f$
lies in the image of the vertical (and identified) sides of the
boundary.
Similarly, the concept AosIdentifiedHorizontalTraits_2
requires the following additional predicate:
- \link AosIdentifiedHorizontalTraits_2::Is_on_x_identification_2 **`Is_on_x_identification_2`**\endlink:
- This predicate is overloaded with two versions.
(i) Given a point \f$p=(x_p, y_p)\f$, determine whether \f$p\f$ lies
in the image of the horizontal (and identified) sides of the
boundary. More precisely, determine whether \f$y_p \in {y_{\rm
min},y_{\rm max}}\f$ for all pre-images of \f$p\f$.(ii) Given a curve \f$c\f$, determine whether the entire curve \f$c\f$
lies in the image of the horizontal (and identified) sides of the
boundary.
\subsection aos_ssec-geom_traits-models Models of the Geometry Traits Concepts
In this section we review the traits classes that are models of
concepts introduced in the previous sections. They handle line
segments, circular arcs, polylines, conic arcs, rational functions,
arcs of Bézier, and arcs of algebraic curves. The last
subsection describes decorators for geometric traits classes
distributed with \cgal, which extend geometric traits-classes by
attaching auxiliary data with the geometric objects.
\subsubsection arr_ssectr_segs Traits Classes for Line Segments and Linear Objects
There are two distinct traits classes that handle line segments. One
caches information in the curve records (see Section \ref
arr_sssectr_caching_segs), while the other retains the minimal amount
of data (see Section \ref arr_sssectr_non_caching_segs). Operations
on arrangements instantiated with the former traits class consume more
space, but they are more efficient for dense arrangements (namely,
arrangements induced by line segments with a large number of
intersections). Another model handles not only (bounded) line
segments, but also rays and lines; see Section \ref
arr_sssectr_linear.
\paragraph arr_sssectr_caching_segs The Caching Segment-Traits Class
An instance of the Arr_segment_traits_2<Kernel> class template used
in most example programs so far is instantiated by substituting theKernel template parameter with a geometric kernel that must conform
to the Kernel concept; see Package \ref PkgKernel23Concepts. This
traits class defines its point type to be the Kernel::Point_2
type. However, neither the \link Arr_segment_traits_2::Curve_2Curve_2\endlink nor the \link
Arr_segment_traits_2::X_monotone_curve_2 X_monotone_curve_2\endlink
nested types of the traits are defined as the Kernel::Segment_2
type. A kernel segment is represented by its two endpoints, and these
may have a large bit size representation when the segment is the
result of several split operations in comparison with the
representation of the original-segment endpoints. The large bit size
representation may significantly slow down the various traits-class
operations involving such segments. (A straightforward solution would
be to repeatedly normalize the results of all computations. However,
our experience shows that indiscriminate normalization considerably
slows down the arrangement construction.)
In contrast, the Arr_segment_traits_2 class template represents a
segment using its supporting line in addition to the two endpoints.
Most computations are performed on the supporting line, which never
changes as the segment is split. The Arr_segment_traits_2 class
template also caches some additional information with each segment to
speed up various predicates, i.e., two Boolean flags indicating (i)
whether the segment is vertical and (ii) whether the segment
target-point is lexicographically larger than its source. The
calculation of the supporting line and two Boolean flags is delayed to
the point in time when needed to achieve further improvement in
efficiency. An \link Arr_segment_traits_2::X_monotone_curve_2X_monotone_curve_2\endlink object is still constructible from two
endpoints or from a kernel segment and converted to anX_monotone_curve_2 object. Moreover, an \link
Arr_segment_traits_2::X_monotone_curve_2 X_monotone_curve_2\endlink
instance can also be cast or to a Kernel::Segment_2 object. The two
types are thus convertible to one another.
Computing the intersection between two segments is preceded by an
application of an efficient predicate that tests whether an
intersection exists at all. This optimization has negligible overhead;
Thus, using an instance of the Arr_segment_traits_2<Kernel> class
template is still very efficient when constructing arrangements
induced by line segments with a large number of intersections.
Efficiency is affected by the substituted geometric kernel. UsingExact_predicates_exact_constructions_kernel as the kernel type is in
general not a bad choice; the coordinates of the segment endpoints are
represented as multi-precision rational numbers, which ensures the
correctness of all computations regardless of the input. Computations
on multi-precision number types (such as Gmpq) take longer than
computations on machine-precision floating-point. A kernel object of
the aforementioned type uses numerical filtering to expedite
computation; see Kernel_2 and Kernel_3. If the input set of line
segments do not have degeneracies; namely, no two segments in the set
share a common endpoint, and no three segments intersect at a common
point, or at least, degeneracies exist but their number is relatively
small, then filtered computation incurs only negligible overhead
compared to floating-point arithmetic, which is error-prone. Indeed,
in almost all examples and applications given in this manual, the
predefined filtered kernelExact_predicates_exact_constructions_kernel is used to instantiate
the line-segment traits class.
\cgalFigureBegin{aos_figpredef_kernels,fan_grids.png,Europe.png}
(a) An arrangement of \f$104\f$ line segments from the input filefan_grids.dat. (b) An arrangement of more than \f$3000\f$ interior
disjoint line segments, defined in the input file Europe.dat.
\cgalFigureEnd
In the following example we use the predefinedExact_predicates_exact_constructions_kernel for instantiating our
segment-traits class. This kernel uses interval arithmetic to filter
the exact computations. The program reads a set of line segments with
integer coordinates from a file and computes their arrangement. By
default it opens the fan_grids.dat input-file, located in the
examples folder, which contains \f$104\f$ line segments that form
four "fan-like" grids and induce a dense arrangement, as illustrated
in \cgalFigureRef{aos_figpredef_kernels} (a):
\cgalExample{Arrangement_on_surface_2/predefined_kernel.cpp}
\paragraph arr_sssectr_non_caching_segs The Non-Caching Segment-Traits Class
The arrangement package offers an alternative segment-traits class
template that handles line segments, namely theArr_non_caching_segment_basic_traits_2<Kernel> class template. This
class template and the Arr_segment_traits_2<Kernel> class template
are both parameterized by a geometric kernel and model the conceptsAosTraits_2 and AosLandmarkTraits_2.
\cgalFootnote{They also model the refined concept
\cgalFootnoteCode{AosDirectionalXMonotoneTraits_2}, which enables Boolean set
operations; see Package \ref PkgBooleanSetOperations2.} The class
template Arr_non_caching_segment_traits_2<Kernel> derives from the
instance Arr_non_caching_segment_basic_traits_2<Kernel>, which
models the AosLandmarkTraits_2 traits concept but not the
refined AosXMonotoneTraits_2 concept. Like theArr_segment_traits_2 class template it derives from the Kernel
type. Unlike the Arr_segment_traits_2 class template it defines its
point and segment types as Kernel::Point_2 and Kernel::Segment_2,
respectively, and most of its defined operations are delegations of
the corresponding operations of the Kernel type. For example, the
functor Compare_xy_2 is defined as Kernel::Compare_xy_2. The
remaining operations are defined in terms of just a few other kernel
operations. For example, the Compare_y_at_x_right_2 predicate is
defined in terms of the Kernel::Compare_slope_2 predicate (ignoring
preconditions for the sake of clarity); see Section \ref
aos_sssec-geom_traits-concepts_basic for the description of this
predicate. The class templateArr_non_caching_segment_basic_traits_2<Kernel> is slightly less
efficient than the Arr_segment_traits_2 class template for
constructing arrangements of pairwise interior-disjoint line-segments
in many cases, as it does not exploit caching at all. Nevertheless,
you may choose to use this traits class, as it consumes less
memory. For arrangements of line segments that do intersect you may
use the class templateArr_non_caching_segment_traits_2<Kernel>. However, the performance
difference in favor of the Arr_segment_traits_2 class template is
much larger, especially when the number of intersections is large.
In the following example we read an input file containing a set of
line segments that are pairwise disjoint in their interior. As the
segments do not intersect, no new points are constructed and we can
instantiate the Arr_non_caching_segment_traits_basic_2<Kernel>
class-template with the predefinedExact_predicates_inexact_constructions_kernel. Note that we use theinsert_non_intersecting_curves() function to construct the
arrangement. By default, the example opens the Europe.dat
input-file, located in the examples folder, which contains more than
\f$3000\f$ line segments with floating-point coordinates that form
the map of Europe, as depicted in
\cgalFigureRef{aos_figpredef_kernels} (b):
\cgalExample{Arrangement_on_surface_2/predefined_kernel_non_intersecting.cpp}
\paragraph arr_sssectr_linear The Linear-Traits Class
The Arr_linear_traits_2<Kernel> class used in Section \ref
aos_sec-unbounded for demonstrating the construction of arrangements
of unbounded curves is capable of handling bounded and unbounded
linear objects, namely, lines, rays, and line segments. It models the
concepts AosTraits_2, AosLandmarkTraits_2, and{AosOpenBoundaryTraits_2. It is parameterized by a geometric
kernel and its nested \link Arr_linear_traits_2::Point_2Point_2\endlink type is defined to be the kernel-point type. The
\link Arr_linear_traits_2::Curve_2 Curve_2\endlink (and \link
Arr_linear_traits_2::X_monotone_curve_2 X_monotone_curve_2\endlink)
nested types are constructible from a Kernel::Line_2, aKernel::Ray_2, or from a Kernel::Segment_2 object. Given a
linear-curve object \f$c\f$, you can use the calls c.is_line(),c.is_ray(), and c.is_segment() to find out whether it has a source
point or a target point. Based on the curve type, you can access its
endpoints using the methods c.source() (for rays and for line
segments) and c.target() (for segments only). It is also possible to
cast a curve into a Kernel::Line_2, a Kernel::Ray_2, or aKernel::Segment_2 type, using the methods c.line(), c.ray(), andc.segment(), respectively. Just like the default segment-traits
class, the linear-curve traits class uses caching techniques to speed
up its predicate evaluations and object constructions.
\subsubsection arr_ssectr_polylines The Polyline and Polycurve Traits Classes
Polylines are continuous piecewise linear curves. Polylines are of
particular interest, as they can be used to approximate more complex
curves in the plane. At the same time they are easier to handle in
comparison to higher-degree algebraic curves, as rational arithmetic
is sufficient to carry out computations on polylines, and to construct
arrangements of polylines in an exact and robust manner. Here, we
extend the notion of polylines and use the term to refer to chains of
subcurves that are not necessarily linear. However, each subcurve must
be uniquely defined by (and, thus, can be uniquely constructed from)
two points within the handled family of curves; see Section \ref
arr_sssectr_polylines. We also provide a similar traits class that
handles continuous piecewise curves that are not necessarily linear
and are not subject to the aforementioned constraint; see Section \ref
arr_sssectr_polycurves.
\paragraph arr_sssectr_polylines The Polyline Traits Class
The Arr_polyline_traits_2<SubcurveTraits_2> class template handles
polylines. It models the following four concepts:
AosTraits_2,AosDirectionalXMonotoneTraits_2AosConstructXMonotoneCurveTraits_2, andAosConstructCurveTraits_2.
The type that substitutes the template parameter SubcurveTraits_2
when Arr_polyline_traits_2<SubcurveTraits_2> is instantiated must be
a geometry-traits class that models the same four concepts. We refer
to the type that substitutes the template parameter SubcurveTraits_2
as the subcurve traits hereafter. If, in addition, the
subcurve traits also models the conceptAosApproximatePointTraits_2 then the instantiatedArr_polyline_traits_2<SubcurveTraits> type models the conceptAosApproximatePointTraits_2 as well. (By definition, modeling the
concepts AosApproximatePointTraits_2 andAosConstructXMonotoneCurveTraits_2 implies modeling the
concept AosLandmarkTraits_2.) Similarly, if the subcurve
traits also models the concept AosOpenBoundaryTraits_2 then
the instantiated Arr_polyline_traits_2<SubcurveTraits> type models
the concept AosOpenBoundaryTraits_2 as well. Modeling theAosConstructXMonotoneCurveTraits_2 concept implies that the
subcurve traits must support the construction of a unique
(\f$x\f$-monotone) segment given two input points.
An instance of the polyline traits class-template inherits its nested
point type, i.e., \link Arr_polyline_traits_2::Point_2Point_2\endlink, from the subcurve traits, and defines the nested
types \link Arr_polyline_traits_2::Curve_2 Curve_2\endlink and \link
Arr_polyline_traits_2::X_monotone_curve_2X_monotone_curve_2\endlink, which are used to represent polylines
and \f$x\f$-monotone polylines, respectively. A polyline of the
nested type \link Arr_polyline_traits_2::Curve_2 Curve_2\endlink is
stored as a vector of SubcurveTraits_2::Curve_2 objects, and an
\f$x\f$-monotone polyline of the nested type \link
Arr_polyline_traits_2::X_monotone_curve_2 X_monotone_curve_2\endlink
is stored as a vector of SubcurveTraits_2::X_monotone_curve_2
objects. The nested X_monotone_curve_2 type inherits from the
nested type \link Arr_polyline_traits_2::Curve_2 Curve_2\endlink.
By default, Arr_segment_traits_2 is used as the subcurve traits (in
case where the SubcurveTraits_2 parameter is omitted). In this case
the nested types SubcurveTraits_2::Curve_2 andSubcurveTraits_2::X_monotone_curve_2 are identical types
representing line segments.
A polyline can be constructed given one of the following inputs:
A range of points, where two succeeding points in the
range represent the endpoints of a segment of the polyline.A range of segments.
A pair of points \a or a single segment. In this case a polyline
that consists of a single segment is constructed.
Note that degenerate polylines are not supported. That is, it is
impossible to construct a polyline that contains a segment of length
zero, or an isolated point. Finally, a polyline is
continuous and well-oriented.
Moreover, the target of the \f$i\f$th segment is the source of
the \f$i+1\f$st segment. If the macro CGAL_ALWAYS_LEFT_TO_RIGHT is
set to 1, then \f$x\f$-monotone polylines are always directed from
left-to-right. (This option is retained for backward compatibility.)
Also, note that a single polyline can be split into several
\f$x\f$-monotone polylines, and that the number of intersection points
(or overlapping sections) between two polylines can be large.
For example, the general polyline
\image html generic-polyline.png
\image latex generic-polyline.png
can be represented by one of the following two orientations.
\image html well_oriented_polyline.png
\image latex well_oriented_polyline.png
Distinct \f$x\f$-monotone polylines are rendered in different colors.
\cgalAdvancedBegin
Technically speaking, it is possible to construct a general polyline
that is neither well-oriented nor continuous. However, it is
impossible to use such polylines for the purpose of computing an
arrangement.
\cgalAdvancedEnd
The polyline traits class also supports the traversal over the range
of defining segments of a given polyline. The \a first and \a
past-the-end iterators can be obtained through the access methods
\link
CGAL::Arr_polyline_traits_2::X_monotone_curve_2::begin_segments()begin_segments()\endlink and \link
CGAL::Arr_polyline_traits_2::X_monotone_curve_2::end_segments()end_segments()\endlink, respectively, of a polyline \f$c\f$. The
vertices of an \f$x\f$-monotone curve are always stored in a strongly
monotonic lexicographical order. In other words, \f$x\f$-monotone
polylines can be directed \a either left-to-right \a or right-to-left.
The polyline-traits class does not perform any geometric operations
directly. Instead, it solely relies on the functionality of the
segment traits. For example, when we need to determine the position of
a point with respect to an \f$x\f$-monotone polyline, we use binary
search to locate the relevant segment that contains the point in its
\f$x\f$-range. Then, we compute the position of the point with respect
to this segment. Thus, operations on \f$x\f$-monotone polylines of
size \f$m\f$ typically take \cgalBigO{\log m} time.
You are free to choose the underlying segment traits class. Your
decision could be based, for example, on the number of expected
intersection points; see Section \ref arr_ssectr_segs. Moreover, it is
possible to substitute the SubcurveTraits_2 template parameter with
a traits class that handles segments with some additional data
attached to each individual segment; see Section \ref
arr_sssec_tr_data_decorators. This makes it possible to associate
different data objects with the different segments that compose a
polyline.
\cgalFigureBegin{aos_fig-polylines,polylines.png}
An arrangement of three polylines, as constructed in \ref
Arrangement_on_surface_2/polylines.cpp. Red disks mark vertices
associated with polyline endpoints, while rings mark vertices that
correspond to intersection points. The target endpoint of \f$\pi_3\f$
is also an intersection point; it is rendered as a ring. Observe that
the polyline \f$\pi_2\f$ is split into three \f$x\f$-monotone
polylines, and that the two curves \f$\pi_1\f$ and \f$\pi_3\f$ have
two overlapping sections---an impossible scenario in arrangements of
line segments. \cgalFigureEnd
The following example program constructs an arrangement of three
polylines, \f$\pi_1\f$, \f$\pi_2\f$, and \f$\pi_3\f$, as depicted in
\cgalFigureRef{aos_fig-polylines}. In this example, each polyline is
constructed from points stored in a different container, i.e., array,
list, and vector. Points defining the polylines are not necessarily
associated with arrangement vertices. The arrangement vertices are
either the extreme points of each \f$x\f$-monotone polyline (drawn as
red discs) or the intersection points between two polylines (drawn as
rings).
\cgalExample{Arrangement_on_surface_2/polylines.cpp}
The common types used by the example programs involving arrangements
of polylines are listed below, and defined in the header filearr_polylines.h. As we do in the case of segments and linear
objects, we use the predefined kernel to instantiate our traits class.
However, in this case we have yet another layer of template
instantiation, namely the instantiation of the polyline-traits
class-template with an instance of a subcurve traits
class-template. Naturally, we use the Arr_segment_traits_2 class
template, instantiated with the predefined filtered kernel.
\code{.cpp}
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Arr_segment_traits_2.h>
#include <CGAL/Arr_polyline_traits_2.h>
#include <CGAL/Arrangement_2.h>
using Kernel = CGAL::Exact_predicates_exact_constructions_kernel;
using Number_type = Kernel::FT;
using Segment_traits = CGAL::Arr_segment_traits_2
using Traits = CGAL::Arr_polyline_traits_2
using Point = Traits::Point_2;
using Polyline = Traits::Curve_2;
using Arrangement = CGAL::Arrangement_2;
\endcode
\paragraph arr_sssectr_polycurves The Polycurve Traits Class
The traits class Arr_polycurve_traits_2<SubcurveTraits_2> handles
piecewise curves that are not necessarily linear, such as conic arcs,
circular arcs, Bézier curves, or line segments. We call such a
compound curve a polycurve. Similar to a polyline, a
polycurve is a chain of subcurves, where each two neighboring
subcurves in the chain share a common endpoint; that is, the polycurve
is continuous. As a matter of fact, most characteristics of theArr_polyline_traits_2<SubcurveTraits_2> traits class template apply
also to the Arr_polycurve_traits_2<SubcurveTraits_2> traits class
template. The only difference between the two, is that the latter is
not a model of the conceptsAosConstructXMonotoneCurveTraits_2 norAosConstructCurveTraits_2, and as such, it is not
able to construct a subcurve from only two points. As a consequence,
it does not support the operations that (i) construct a polycurve from
a sequence of points, and (ii) push a point at the back or at the
front of a non-empty polycurve.
\subsubsection arr_ssectr_algebraic Traits Classes for Algebraic Curves
A curve in our context is typically (but not necessarily) defined as
the zero set of a bivariate nonzero polynomial with rational (or,
equivalently, integral) coefficients. We call such polynomials and the
curves they define \a algebraic. When dealing with linear curves
(e.g., line segments and polylines), having rational coefficients
guarantees that all intersection points also have rational
coordinates, such that the arrangement of such curves can be
constructed and maintained using only rational arithmetic. The \ref
PkgArrangementOnSurface2 package also offers geometry traits-classes
that handle algebraic curves defined by algebraic polynomials of
degree higher than~\f$1\f$. Unfortunately, the coordinates of the
intersection points constructed by these traits models are in general
algebraic numbers\cgalFootnote{A number is called \a algebraic, if it
is the root of a univariate algebraic polynomial with rational
coefficients.} of degree higher than \f$1\f$. It is therefore clear
that we have to use number types different from plain rational to
represent point coordinates and be able to apply arithmetic operations
on them.
Several types of algebraic curves are handled by more than one traits
model. Section \ref arr_sssectr_linear introduces a few different
traits models that handle line segments. This duplication becomes more
evident with the introduction of traits classes that handle algebraic
curves. The different traits models have different properties. In some
cases they were developed by different authors at different times
exploiting different tools that were available at the time they were
developed. As a general rule, you should always use the minimal traits
model that still satisfies your needs, as the most dedicated model is
most likely to be the most efficient.
\paragraph arr_sssectr_circ_seg Circular Arcs and Line Segments
Arrangement of circular arcs and of line segments are very useful and
frequently arise in applications, where curves of interleaved line
segments and circular arcs are used to model the boundaries of complex
shapes. Such curves can fit the original boundary more tightly and
more compactly than, for example, a simple polyline. For example, when
dilating a polygon by some radius we obtain a shape whose boundary
comprises of line segments, which correspond to dilated polygon edges,
and circular arcs, which result from dilated polygon vertices. Using
the arrangement of the boundary curves it is possible, for example, to
compute the union of a set of dilated polygons. Besides the
importance of arrangements of circular arcs and line segments it turns
out that it is possible to implement efficient traits models that
handle curves restricted to circular arcs and line segments. Rational
numbers cannot represent the coordinates of intersection points that
may arise in such arrangements in an exact manner. Thus, algebraic
numbers must be used. However, the performance impact that (general)
algebraic numbers incur can be reduced by the use of an efficient type
of exact algebraic numbers called square root extension that
uses rational arithmetic in an almost straightforward fashion.
Square root numbers have the form \f$\alpha + \beta\sqrt{\gamma}\f$,
where \f$\alpha\f$, \f$\beta\f$, and \f$\gamma\f$ are rational
numbers. (Square root numbers are also called one-root numbers.) The
rational number \f$\gamma\f$ is referred to as the extension. Each
subset that has a particular extension is closed under arithmetic
operations and order relations; hence, it is a valid algebraic
structure.\cgalFootnote{The term algebraic structure refers to the set
closed under one or more operations satisfying some axioms; see, e.g.,
https://en.wikipedia.org/wiki/Algebraic_structure. For example, all
numbers that can be expressed as \f$\alpha + \beta\sqrt{K}\f$, where
\f$\alpha\f$ and \f$\beta\f$ are rational and \f$K\f$ is a rational
constant, compose an algebraic structure.} The class templateCGAL::Sqrt_extension<NT,Root> implements the square root extension
type. Instances of this template represent square root numbers. It is
equipped with the implementation of a set of arithmetic operations and
order relations that exploit identical extensions of operands. It also
provides the ability to compare two numbers with different extensions
\f$\gamma_1 \neq \gamma_2\f$ efficiently. Each operation is
implemented using only few rational arithmetic operations. Here, NT
is the type of \f$\alpha\f$ and \f$\beta\f$, and Root is the type of
\f$\gamma\f$. The running times of the arithmetic operations and
order relations provided by the square root extension when the
identical-extension condition is met are comparable to the running
times of the corresponding arithmetic operations of rational-number
types. The running times of order relations when the condition is not
met are only slightly larger. In practice, using number types that
represent (arbitrary) algebraic numbers increases the running time of
the application significantly.
We call circles whose center coordinates and squared radii are
rational numbers rational circles. The equation of such a
circle, that is, \f$(x - x_0)^2 + (y - y_0)^2 = r^2\f$, where
\f$(x_0,y_0)\f$ and \f$r\f$ denote the circle center and its radius,
respectively, has rational coefficients. The coordinates of the points
of intersection between two such circles are therefore solutions of
quadratic equations with rational coefficients, in other words,
algebraic numbers of degree~\f$2\f$ or simply square root numbers. The
same applies to intersection points between such a rational circle and
a line, or a line segment, with rational coefficients (a line whose
equation is \f$ax + by + c = 0\f$, where \f$a\f$, \f$b\f$, and \f$c\f$
are rational).
The \ref PkgArrangementOnSurface2 package offers a traits
class-template called Arr_circle_segment_traits_2<Kernel> that
exclusively handles line segments, circular arcs, and whole circles
and models the concepts AosTraits_2 andAosDirectionalXMonotoneTraits_2; see Package \ref
PkgBooleanSetOperations2Ref. Note that it is not a model of theAosLandmarkTraits_2 concept. It exploits efficient
computations with square root numbers, which makes it attractive for
arrangements induced by line segments, circular arcs, and whole
circles. When the traits class-template is instantiated, the Kernel
template parameter must be substituted by a geometric kernel that
models the Kernel concept. Always plug in a kernel that uses a
rational number type, such asExact_predicates_exact_constructions_kernel. Observe that the nested
type \link CGAL::Arr_circle_segment_traits_2::Point_2Point_2\endlink defined by the traits class, whose coordinates are
typically algebraic numbers of degree 2, is \a not the same as theKernel::Point_2 type. The coordinates of a point are represented
using the number type CoordNT, nested in the traits class-template.
\cgalFigureBegin{aos_fig-circles,circles.png}
An arrangement of three circles constructed in \ref
Arrangement_on_surface_2/circles.cpp. Each circle is split into two
\f$x\f$-monotone circular arcs, whose endpoints are drawn as red
disks. Rings mark vertices that correspond to intersection points. The
vertex \f$v_{\rm max}\f$ is a common intersection point of all three
circles.
\cgalFigureEnd
In the following example an arrangement of three full circles is
constructed, as shown in \cgalFigureRef{aos_fig-circles}. Each Index is
split into two \f$x\f$-monotone circular arcs, the endpoints of which
are drawn as red discs; rings mark vertices that correspond to
intersection points. Once the arrangement is constructed, we locate
the vertex of maximal degree in the arrangement. The geometric mapping
of this vertex, denoted by \f$v_{\rm max}\f$, is the point
\f$(4,3)\f$, as all three circles intersect at this point and the
associated vertex has six incident edges.
\cgalExample{Arrangement_on_surface_2/circles.cpp}
The types common to the example programs that use theArr_circle_segment_traits_2 class template are listed below and
defined in the header file arr_circular.h. Even though algebraic
numbers are required to represent coordinates of points where the
inducing curves are circles or circular arcs, such as the curves
handled by the Arr_circle_segment_traits_2 class template, an
(exact) rational kernel suffices, and a filtered one improves the
performance further.
\code{.cpp}
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Arr_circle_segment_traits_2.h>
#include <CGAL/Arrangement_2.h>
using Kernel = CGAL::Exact_predicates_exact_constructions_kernel;
using Number_type = Kernel::FT;
using Traits = CGAL::Arr_circle_segment_traits_2
using CoordNT = Traits::CoordNT;
using Point = Traits::Point_2;
using Curve = Traits::Curve_2;
using Rational_point = Traits::Rational_point_2;
using Segment = Traits::Rational_segment_2;
using Circle = Traits::Rational_circle_2;
using Arrangement = CGAL::Arrangement_2;
\endcode
The \link Arr_circle_segment_traits_2::Curve_2 Curve_2\endlink type
nested in Arr_circle_segment_traits_2 can be used to represent
circles, circular arcs, or line segments. We now describe and
demonstrate a variety of ways in which the curves of this type can be
constructed. A Curve object can be constructed from aKernel::Circle_2 object or from a Kernel::Segment_2 object. A
circular arc is typically defined by a supporting circle and two
endpoints, where the endpoints are of type \link
Arr_circle_segment_traits_2::Point_2 Point_2\endlink, with rational
or irrational coordinates. The orientation of the arc is determined by
the orientation of the supporting circle. Similarly, we also support
the construction of line segments given their supporting line (of typeKernel::Line_2) and two endpoints, which may have irrational
coordinates (unlike the Kernel::Segment_2 type).
Note that the Kernel::Circle_2 type is used to represent a circle
whose squared radius is rational, where the radius itself may
be irrational. However, if the radius is known to be rational, its
use is recommended for efficiency. It is therefore also possible to
construct a circle, or a circular arc specifying the circle center (aKernel::Point_2), its rational radius (of type Kernel::FT), and
its orientation. Finally, we also support the construction of a
circular arc that is defined by two endpoints and an arbitrary
interior point that lies on the arc in between its endpoints. In this
case, all three points are required to have rational coordinates;
namely, they are all given as Kernel::Point_2 objects.
\cgalFigureBegin{aos_fig-circular_arcs,circular_arcs.png}
An arrangement of two full circles, two line segments, and three
circular arcs as constructed in \ref
Arrangement_on_surface_2/circular_arcs.cpp. Endpoints are drawn as
red disks and intersection points are drawn as rings.
\cgalFigureEnd
The following example demonstrates the usage of the various
construction methods for circular arcs and line segments. The
resulting arrangement is depicted in
\cgalFigureRef{aos_fig-circular_arcs}. Note the usage of the
constructor of CoordNT(alpha, beta, gamma), which creates a
degree-\f$2\f$ algebraic number whose value is \f$\alpha +
\beta\sqrt{\gamma}\f$.
\cgalExample{Arrangement_on_surface_2/circular_arcs.cpp}
It is also possible to construct \f$x\f$-monotone curve objects that
represent \f$x\f$-monotone circular arcs or line segments using
similar constructors. (Full circles are not \f$x\f$-monotone.)
The traits class-templateArr_circular_line_arc_traits_2<CircularKernel> offered by the
arrangement package also handles circular arcs and line segments. It
is an alternative to the Arr_circle_segment_traits_2<Kernel>
class-template. These two class templates, while serve similar
purposes, are based on different concepts, and posses different
characteristics. You are encouraged to experiment with both, compare
their performance, and use the most suitable for your case.
\paragraph arr_sssectr_conic A Traits Class for Conic Arcs
A conic curve is an algebraic curve of degree 2. Namely, it
is the locus of all points \f$(x,y)\f$ satisfying the equation \f$c:
r x^2 + s y^2 + t xy + u x + v y + w = 0\f$, where the six
coefficients \f$\langle r, s, t, u, v, w \rangle\f$ completely
characterize the curve. The sign of the expression \f$\Delta_{c} = 4 r
s - t^2\f$ determines the type of curve:
The Arr_conic_traits_2<RatKernel, AlgKernel, NtTraits> class
template is capable of handling only bounded arcs of conic curves,
referred to as conic arcs. A conic arc \f$a\f$ may be either
(i) a full ellipse, or (ii) defined by the tuple \f$\langle \kappa,
p_s, p_t, o \rangle\f$, where \f$\kappa\f$ is a conic curve and
\f$p_s\f$ and \f$p_t\f$ are two points on \f$c\f$ (namely
\f$\kappa(p_s) = \kappa(p_t) = 0\f$) that define the source
and target of the arc, respectively. The arc is formed by
traversing \f$\kappa\f$ from the source to the target going in the
orientation specified by \f$o\f$, which is typically clockwise or
counterclockwise orientation, but may also be collinear in case of
degenerate conic conic-curves, namely, lines or pairs of lines.
We always assume that the conic coefficients \f$\langle r, s, t, u, v,
w \rangle\f$ are rational. The coordinates of points of intersection
between two conic curves with rational coefficients are in general
algebraic numbers of degree 4. Namely, they are roots of algebraic
polynomials of degree 4. In addition, conic arcs may not necessarily
be \f$x\f$-monotone, and must be split at points where the tangent to
the arc is vertical. In the general case such points typically have
coordinates that are algebraic numbers of degree 2. Note that as
arrangement vertices induced by intersection points and points with
vertical tangents are likely to have algebraic coordinates, we also
allow the original endpoints of the input arcs \f$p_s\f$ and \f$p_t\f$
to have algebraic coordinates.
The Arr_conic_traits_2<RatKernel, AlgKernel, NtTraits> class
template is designed for efficient handling of arrangements of
bounded conic arcs. The template has three parameters, defined as
follows:
The instantiation of the conic traits class-template is slightly more
complicated than the instantiation of the traits classes you have
encountered so far. This instantiation is exemplified in the header
file arr_conics.h. Note how we first define the rational and
algebraic kernels using the number types given by the \core number
type traits-class, then use them to define the conic traits
class-template. Also note the types defined by the rational kernels,
which we need for conveniently constructing conic arcs.
The Arr_conic_traits_2 models the AosTraits_2 andAosLandmarkTraits_2 concepts. Its \link
Arr_conic_traits_2::Point_2 Point_2\endlink type is derived fromAlgKernel::Point_2, while the \link Arr_conic_traits_2::Curve_2Curve_2\endlink type represents a bounded, not necessarily
\f$x\f$-monotone, conic arc. The \link
Arr_conic_traits_2::X_monotone_curve_2 X_monotone_curve_2\endlink
type is derived from \link Arr_conic_traits_2::Curve_2Curve_2\endlink, but its constructors are used only by the traits
class. Users should therefore construct only \link
Arr_conic_traits_2::Curve_2 Curve_2\endlink objects and insert them
into the arrangement using the insert() function.
Conic arcs are constructible from full ellipses or by specifying a
supporting curve, two endpoints, and an orientation. However, several
constructors of \link Arr_conic_traits_2::Curve_2 Curve_2\endlink
are available to allow for some special cases, such as circular arcs
or line segments. The \link Arr_conic_traits_2::Curve_2Curve_2\endlink and the derived \link
Arr_conic_traits_2::X_monotone_curve_2 X_monotone_curve_2\endlink
classes also support basic access functions such as source(),target(), and %orientation().
\cgalFigureBegin{aos_fig-conics,conics.png}
An arrangement of mixed conic arcs, as constructed in conics.cpp
\cgalFigureEnd
The following example demonstrates the usage of the various
constructors for conic arcs. The resulting arrangement is depicted in
\cgalFigureRef{aos_fig-conics}. Especially noteworthy are the
constructor of a circular arc that accepts three points, the
constructor of a conic arc that accepts five points, and the
constructor that allows specifying approximate endpoints, where the
exact endpoints are given explicitly as intersections of the
supporting conic with two other conic curves. The approximate
endpoints are used to select the specific exact endpoints out of all
intersection points of the pair of curves (the supporting conic curve
and the auxiliary conic curve). Also note that as the preconditions
required by some of these constructors are rather complicated (see the
Reference Manual for the details), a precondition violation does not
cause the program to terminate---instead, an invalid arc is
created. The call a.is_valid() verifies the validity of the arc
\f$a\f$. Naturally, inserting invalid arcs into an arrangement is not
allowed, so the validity of an arc should be checked once it is
constructed.
\cgalExample{Arrangement_on_surface_2/conics.cpp}
The types common to the example programs that use theArr_conic_traits_2 class template are listed below and
defined in the header file arr_conics.h.
\code{.cpp}
#include <CGAL/Cartesian.h>
#include <CGAL/CORE_algebraic_number_traits.h>
#include <CGAL/Arr_conic_traits_2.h>
#include <CGAL/Arrangement_2.h>
using Nt_traits = CGAL::CORE_algebraic_number_traits;
using Rational = Nt_traits::Rational;
using Rat_kernel = CGAL::Cartesian
using Rat_point = Rat_kernel::Point_2;
using Rat_segment = Rat_kernel::Segment_2;
using Rat_circle = Rat_kernel::Circle_2;
using Algebraic = Nt_traits::Algebraic;
using Alg_kernel = CGAL::Cartesian
using Traits = CGAL::Arr_conic_traits_2<Rat_kernel, Alg_kernel, Nt_traits>;
using Point = Traits::Point_2;
using Conic_arc = Traits::Curve_2;
using X_monotone_conic_arc = Traits::X_monotone_curve_2;
using Arrangement = CGAL::Arrangement_2;
\endcode
\cgalFigureBegin{aos_fig-conic_multiplicities,conic_multiplicities.png}
An arrangement of a circular arc and a hyperbolic arc, as constructed
in \ref Arrangement_on_surface_2/conic_multiplicities.cpp.
\cgalFigureEnd
The last example in this section demonstrates how the conic-traits
class can handle intersection points with multiplicity. The resulting
arrangement is depicted in
\cgalFigureRef{aos_fig-conic_multiplicities}. The supporting curves of
the two arcs, a circle centered at \f$(0,\frac{1}{2})\f$ with radius
\f$\frac{1}{2}\f$, and the hyperbola \f$y =
\frac{x^2}{1-x}\f$,\cgalFootnote{This curve can also be written as
\f$c: x^2 + xy - y = 0\f$. It is a hyperbola since \f$\Delta_{c} =
-1\f$.} intersect at the origin such that the intersection point has
multiplicity \f$3\f$ (note that they both have the same horizontal
tangent at \f$(0,0)\f$ and the same curvature \f$1\f$). In addition,
they have another intersection point at
\f$(\frac{1}{2},\frac{1}{2})\f$ of multiplicity \f$1\f$.
\cgalExample{Arrangement_on_surface_2/conic_multiplicities.cpp}
\paragraph arr_sssectr_ratfunc A Traits Class for Arcs of Rational Functions
A rational function is given by the equation \f$y =
\frac{P(x)}{Q(x)}\f$, where \f$P\f$ and \f$Q\f$ are polynomials of
arbitrary degrees. In particular, if \f$Q(x) = 1\f$, then the
function is simply a polynomial function. A bounded rational
arc is defined by the graph of a rational function over some
interval \f$[x_{\rm min}, x_{\rm max}]\f$, where \f$Q\f$ does not have
any real roots in this interval (thus, the arc does not contain any
singularities). However, we may consider functions defined over
unbounded intervals, namely, over \f$(-\infty, x_{\max}]\f$,
\f$[x_{\min}, \infty)\f$, or \f$(-\infty, \infty)\f$. Rational
functions, and polynomial functions in particular, are not only
interesting in their own right, they are also useful for approximating
more complicated curves and for interpolation; see, e.g.,
\cgalCite{cgal:ptvf-nrcpp-02} Chapter 3.
Using the Arr_rational_function_traits_2 class template it is
possible to construct and maintain arrangements induced by rational
arcs. Every instance of the Arr_rational_function_traits_2 class
template models the concepts AosTraits_2 andAosOpenBoundaryTraits_2, but it does not model theAosLandmarkTraits_2 concept. It also models the refined
concept AosDirectionalXMonotoneTraits_2, which enables
Boolean set operations; see Package \ref PkgBooleanSetOperations2.
Note that it is not a model of AosLandmarkTraits_2 concept,
so it is impossible to use the landmark point-location strategy with
this traits class.
\cgalFigureBegin{aos_fig-rat_func_singular,rational_function_singular.png}
An arrangement of an arc of a rational functions that has
singularities at \f$x = 1\f$ and at \f$x = 2\f$.
\cgalFigureEnd
A rational arc is always \f$x\f$-monotone in the mathematical sense.
However, it is not necessarily continuous, as it may have
singularities. An arc that has singularities must be split into
continuous portions before being inserted into the arrangement.
Consider, for example, the rational arc given by the equation \f$y =
\frac{1}{(x-1)(2-x)}\f$ defined over the interval \f$[0,3]\f$, as
depicted in \cgalFigureRef{aos_fig-rat_func_singular}. This arc has
two singularities, at \f$x = 1\f$ and at \f$x = 2\f$. It is split into
three continuous portions defined over the intervals \f$[0,1)\f$,
\f$(1,2)\f$, and \f$(2,3]\f$ by the traits operation \link
AosTraits_2::Make_x_monotone_2Make_x_monotone_2\endlink. Arbitrary rational functions are
represented by the nested type \link
Arr_rational_function_traits_2::Curve_2 Curve_2\endlink and
continuous portions of rational functions are represented by the
nested type \link Arr_rational_function_traits_2::X_monotone_curve_2X_monotone_curve_2\endlink. Constructors for both types are
provided by the traits in form of functors.
When the Arr_rational_function_traits_2<AlgebraicKernel_d_1> class
template is instantiated, the template parameter must be substituted
by a model of the AlgebraicKernel_d_1 concept. Models of this
concept, such as the Algebraic_kernel_d_1<Coefficient> class
template provided by the package \ref PkgAlgebraicFoundations are
meant to support algebraic functionalities on univariate polynomials
of arbitrary degree. See the documentation of the conceptAlgebraicKernel_d_1 for more information. A rational function is
then represented as the quotient of two polynomials, \f$P\f$ and
\f$Q\f$, of type Polynomial_1 nested in every model of the conceptAlgebraicKernel_d_1 and in particular in the algebraic-kernel type
that substitutes the template parameter AlgebraicKernel_d_1 when the
traits class-template is instantiated. Such a rational function is
constructible from a single polynomial \f$P\f$ (with \f$Q(x) = 1\f$),
or from two polynomials \f$P\f$ and \f$Q\f$. The type of the
polynomial coefficients, namely Coefficient, and the type of the
interval bounds, namely Bound, are also nested in the
algebraic-kernel type. If an instance of theAlgebraic_kernel_d_1<Coefficient> class template is used, for
example, as the algebraic-kernel type, the type that substitutes its
template parameter is defined as the Coefficient type. This type
cannot be algebraic. Moreover, it is recommended that this type is not
made rational either, since using rational (as opposed to integral)
coefficients does not extend the range of the rational arcs and is
typically less efficient.\cgalFootnote{The \cgalFootnoteCode{Algebraic_kernel_d_1}
class template uses the types provided by the \ref PkgPolynomial
package to define its nested \cgalFootnoteCode{Polynomial_1} type and conveniently
expose it to the user.} The Bound type, however, can be algebraic. A
point of type \link Arr_rational_function_traits_2::Point_2Point_2\endlink nested in the Arr_rational_function_traits_2 class
template is represented by a rational function and its
\f$x\f$-coordinate, which is derived from the type Algebraic_real_1
nested in the algebraic-kernel type. An explicit representation by the
nested type Algebraic_real_1 of the \f$y\f$-coordinate is only
computed upon request, as it can be a rather costly operation.
The aforementioned types, Polynomial_1, Coefficient,Bound, and Algebraic_real_1, are conveniently nested in
the Arr_rational_function_traits_2 class template among
the others and obtained from there in the type definitions used
in the examples given in this section and listed below. These types
are defined in the header file arr_rat_functions.h.
\code{.cpp}
#include <CGAL/basic.h>
#include <CGAL/CORE_BigInt.h>
#include <CGAL/Algebraic_kernel_d_1.h>
#include <CGAL/Arr_rational_function_traits_2.h>
#include <CGAL/Arrangement_2.h>
using Number_type = CORE::BigInt;
using AK1 = CGAL::Algebraic_kernel_d_1
using Traits = CGAL::Arr_rational_function_traits_2
using Polynomial = Traits::Polynomial_1;
using Alg_real = Traits::Algebraic_real_1;
using Bound = Traits::Bound;
using Arrangement = CGAL::Arrangement_2;
\endcode
The constructed rational functions are cached by the traits class. The
cache is local to each traits class object. It is therefore necessary
to construct curves using only the constructor objects provided by
member functions of the traits class. Moreover, a curve must only be
used by the traits class object that was used to construct it. The
cache is automatically cleaned up from time to time. The amortized
clean up costs are constant. In addition, there is also a separate
member function that cleans up the cache upon request.
\cgalFigureBegin{aos_fig-rational_functions,rational_functions.png}
An arrangement of four arcs of rational functions, as constructed in
\ref Arrangement_on_surface_2/rational_functions.cpp.
\cgalFigureEnd
The following example demonstrates the construction of an arrangement
induced by rational arcs depicted in
\cgalFigureRef{aos_fig-rational_functions}. It uses constructors both
for polynomial arcs and for rational arcs.
\cgalExample{Arrangement_on_surface_2/rational_functions.cpp}
\cgalFigureBegin{aos_fig-unbounded_rational_functions,unbounded_rational_functions.png}
An arrangement of six arcs of rational functions, as constructed in
\ref Arrangement_on_surface_2/unbounded_rational_functions.cpp.
\cgalFigureEnd
The following example demonstrates the construction of an arrangement
of six rational arcs, four unbounded arcs and two bounded ones, as
depicted in \cgalFigureRef{aos_fig-unbounded_rational_functions}. Note
the usage of the constructors of an entire rational function and of an
infinite "ray" of such a function. Also observe that the hyperbolas
\f$y = \pm\frac{1}{x}\f$ and \f$y = \pm\frac{1}{2x}\f$ never
intersect, although they have common vertical and horizontal
asymptotes, so very "thin" unbounded faces are created between them:
\cgalExample{Arrangement_on_surface_2/unbounded_rational_functions.cpp}
The curve constructors have an additional advantage. They conveniently
enable the provision of two polynomials that define a rational arc
using rational coefficients. For example, let \f$P\f$ and \f$Q\f$
denote two polynomials with integral coefficients that define a
rational arc of interest, and let \f$P'\f$ and \f$Q'\f$ denote two
polynomials with rational coefficients that define the same rational
arc; that is, the quotients \f$P/Q\f$ and \f$P'/Q'\f$ are
identical. You can construct the rational arc providing the
coefficients of \f$P'\f$ and \f$Q'\f$ to the constructor. In this case
the constructor normalizes the coefficients and generates the desired
polynomials \f$P\f$ and \f$Q\f$. To this end, the curve constructors
of both types, namely \link Arr_rational_function_traits_2::Curve_2Curve_2\endlink and \link
Arr_rational_function_traits_2::X_monotone_curve_2X_monotone_curve_2\endlink, have operators that accept ranges of
polynomial coefficients as well as polynomials. The coefficients in a
given range must be in the order of the degrees of the corresponding
variables starting from the constant term.
\paragraph arr_sssectr_bez A Traits Class for Planar Bézier Curves
A planar Bézier curve \f$B\f$ is a parametric curve
defined by a sequence of control points \f$p_0, \ldots,
p_n\f$ as follows:
\f{eqnarray*}{
B(t) = \left(X(t), Y(t)\right)
= \ccSum{k=0}{n}{p_k \cdot \frac{n!}{k! (n-k)!} \cdot
t^k (1-t)^{n-k}}\ .
\f}
where \f$t \in [0, 1]\f$. The degree of the curve is therefore \f$n\f$;
namely, \f$X(t)\f$ and \f$Y(t)\f$ are polynomials of degree
\f$n\f$. Bézier curves have numerous applications in computer
graphics and solid modeling. They are used, for example, in free-form
sketches and for defining the true-type fonts.
Using the Arr_Bezier_curve_traits_2<RatKernel, AlgKernel, NtTraits>
class template you can construct and maintain arrangements induced by
Bézier curves (including self-intersecting Bézier
curves) of arbitrary degree. The curves are given by rational control
points, that is, a sequence of objects of the RatKernel::Point_2
type. (In general, a sequence of \f$n+1\f$ control points define a
Bézier curve of degree \f$n\f$.) The three types that
substitute the template parameters RatKernel, AlgKernel, andNtTraits, respectively, when the traits is instantiated must fulfill
the same requirements of the corresponding types used to instantiate
the Arr_conic_traits_2 class template. Here, the use of theCORE_algebraic_number_traits class is also recommended with
%Cartesian kernels instantiated with the Rational and Algebraic
number types defined by this class. The examples given in this manual
use the type definitions listed below. These types are defined in the
header file arr_Bezier.h.
\code{.cpp}
#include <CGAL/Cartesian.h>
#include <CGAL/CORE_algebraic_number_traits.h>
#include <CGAL/Arr_Bezier_curve_traits_2.h>
#include <CGAL/Arrangement_2.h>
using Nt_traits = CGAL::CORE_algebraic_number_traits;
using NT = Nt_traits::Rational;
using Rational = Nt_traits::Rational;
using Algebraic = Nt_traits::Algebraic;
using Rat_kernel = CGAL::Cartesian
using Alg_kernel = CGAL::Cartesian
using Rat_point = Rat_kernel::Point_2;
using Traits =
CGAL::Arr_Bezier_curve_traits_2<Rat_kernel, Alg_kernel, Nt_traits>;
using Bezier_curve = Traits::Curve_2;
using Arrangement = CGAL::Arrangement_2;
\endcode
As mentioned above, we assume that the coordinates of all control
points that define a Bézier curve are rational numbers, so both
\f$X(t)\f$ and \f$Y(t)\f$ are polynomials with rational
coefficients. The intersection points between curves are, however,
algebraic numbers, and their exact computation is time-consuming. The
traits class therefore contains a layer of geometric filtering that
performs all computations in an approximate manner whenever
possible. Thus, it resorts to exact computations only when the
approximate computation fails to produce an unambiguous result. Most
arrangement vertices are therefore associated with approximated
points. You cannot access the coordinates of such points as algebraic
numbers. However, access to the approximate coordinates is possible.
See the Reference Manual for the exact interface of the \link
Arr_Bezier_curve_traits_2::Point_2 Point_2\endlink, \link
Arr_Bezier_curve_traits_2::Curve_2 Curve_2\endlink, and \link
Arr_Bezier_curve_traits_2::X_monotone_curve_2X_monotone_curve_2\endlink types defined by the traits class.
Every instance of the Arr_Bezier_curve_traits_2 class templates
models the concept AosTraits_2 (but it does not model theAosLandmarkTraits_2 concept). It also models the refined
concept AosDirectionalXMonotoneTraits_2, which enables
Boolean set operations; see Package \ref PkgBooleanSetOperations2.
\cgalFigureBegin{aos_fig-bezier_curves,bezier_curves.png}
An arrangement of ten Bézier curves of degree \f$5\f$, as
constructed in \ref Arrangement_on_surface_2/Bezier_curves.cpp.
\cgalFigureEnd
The following example reads a set of Bézier curves from an
input file, where each file is specified by an integer stating its
number of control points, followed by the sequence of control points
given as integer or rational coordinates. By default, the program uses
the Bezier.dat file, which contains ten curves of degree \f$5\f$
each; their resulting arrangement is depicted in
\cgalFigureRef{aos_fig-bezier_curves}.
\cgalExample{Arrangement_on_surface_2/Bezier_curves.cpp}
\paragraph arr_sssectr_alg A Traits Class for Planar Algebraic Curves of Arbitrary Degree
The traits class, namely Arr_algebraic_segment_traits_2, is based on
the Algebraic_kernel_d_1 class template, which models the
algebraic-kernel concept AlgebraicKernel_d_1; see Section \ref
arr_sssectr_ratfunc. The traits class handles (i) algebraic curves and
(ii) continuous (weakly) \f$x\f$-monotone segments of algebraic
curves, which, however, are not necessarily maximal. (A formal
definition is given below.) Observe that non-\f$x\f$-monotone
segments are not supported. Still, it is the traits class that
supports the most general type of curves among the traits classes
included in the package.
Recall that an algebraic curve \f$c\f$ in the plane is defined as the
(real) zero set of a bivariate polynomial \f$f(x,y)\f$. The curve is
uniquely defined by \f$f\f$, although several polynomials might define
the same curve. We call \f$f\f$ a defining polynomial of
\f$c\f$.
A formal definition of (weakly) \f$x\f$-monotone segments of algebraic
curves follows. A point \f$p\f$ on a curve \f$c_f \subset \mathbb{R}^2\f$
(with defining polynomial \f$f\f$) is called semi-regular if,
locally around \f$p\f$, the curve \f$c_f\f$ can be written as a
function graph of some continuous function in \f$x\f$ or in
\f$y\f$. (We also say that \f$p\f$ is parameterizable in \f$x\f$ or
\f$y\f$, respectively.) The only two cases of non-semi-regular points
are isolated points and self-intersections. A segment of a
curve in this context is a closed and continuous point set such that
each interior point is semi-regular. It follows that a segment is
either vertical or a closed connected point set, with all interior
points parameterizable in \f$x\f$.
Every instance of the Arr_algebraic_segment_traits_2<Coefficient>
class template models the AosTraits_2 andAosOpenBoundaryTraits_2 concepts (but it does not model theAosLandmarkTraits_2 concept). The template argumentCoefficient determines the type of the scalar coefficients of the
polynomial. Currently supported integral number types are Gmpz,leda_integer, and CORE::BigInt. This is reflected in the
statements included in the header file integer_type.h, the listings
of which are omitted here. This header file is used by the two example
programs listed in this section. The template parameter Coefficient
can be substituted in addition by an instance of theSqrt_extension<A,B> class template, where the template parametersNT and Root are substituted in turn by one of the integral
number types above. Finally, the template parameter Coefficient can
be substituted also by a rational number type, where the type of the
numerator and denominator is one of the types above.
The type \link Arr_algebraic_segment_traits_2::Curve_2Curve_2\endlink nested in the Arr_algebraic_segment_traits_2 class
template defines an algebraic curve. An object of this type can be
constructed by the \link
Arr_algebraic_segment_traits_2::Construct_curve_2Construct_curve_2\endlink functor also nested in the class
template. Its function operator accepts as an argument an object of
type \link Arr_algebraic_segment_traits_2::Polynomial_2Polynomial_2\endlink, nested as well in the traits class-template.
The type \link Arr_algebraic_segment_traits_2::Polynomial_2Polynomial_2\endlink models the concept Polynomial_d. An object of
the nested type \link Arr_algebraic_segment_traits_2::Polynomial_2Polynomial_2\endlink represents a bivariate polynomial. It can be
constructed in a few convenient ways, some are exemplified by the
programs listed below. Consult the reference guide for the complete
set of options.
\cgalFigureBegin{aos_fig-algebraic_curves,algebraic_curves.png}
An arrangement of algebraic curves of degrees \f$1\f$, \f$2\f$,
\f$3\f$, and \f$6\f$, as constructed in
\ref Arrangement_on_surface_2/algebraic_curves.cpp.
\cgalFigureEnd
The following examples computes the arrangement depicted in
\cgalFigureRef{aos_fig-algebraic_curves}. The arrangement is induced
by four algebraic curves, \f$c_1\f$, \f$c_2\f$, \f$c_3\f$, and
\f$c_4\f$, of degrees \f$1\f$, \f$2\f$, \f$3\f$, and \f$6\f$,
respectively. For each curve the defining polynomial is constructed
first. Then, the algebraic curve is constructed using the \link
Arr_algebraic_segment_traits_2::Construct_curve_2Construct_curve_2\endlink functor. Finally, the curve is inserted
into the arrangement.
\cgalExample{Arrangement_on_surface_2/algebraic_curves.cpp}
The Arr_algebraic_segment_traits_2 class template carries state to
expedite some of its computations. Thus, it is essential to have only
one copy of the traits object during the life time of a program that
utilizes this traits class. To this end, the example above uses the
constructor of the Arrangement_2 data structure that accepts the traits
object as input. Carrying state is not a unique property of theArr_algebraic_segment_traits_2 class template; it is common to many
traits classes, especially to traits classes that handle algebraic
curves. Therefore, as a general rule, if your application requires
direct access to a traits object, define it locally, and pass it to
the constructor of the Arrangement_2 data structure to avoid the
construction of a duplicate traits-class object.
A weakly \f$x\f$-monotone segment of an algebraic curve is represented
by the \link Arr_algebraic_segment_traits_2::X_monotone_curve_2X_monotone_curve_2\endlink type nested in the traits
class-template. You can construct such segments in two ways as
follows: (i) using the \link AosTraits_2::Make_x_monotone_2Make_x_monotone_2\endlink functor or (ii) using the \link
Arr_algebraic_segment_traits_2::Construct_x_monotone_segment_2Construct_x_monotone_segment_2\endlink functor. Both functors are
nested in the traits class-template. The former is required by the
concept AosTraits_2 our traits class models; see Section
\ref aos_sssec-geom_traits-concepts_arbitrary. The latter enables the
construction of individual segments. The \link
Arr_algebraic_segment_traits_2::X_monotone_curve_2X_monotone_curve_2\endlink type represents weakly \f$x\f$-monotone
segments of a curve; however, for technical reasons, segments may need
to be further subdivided into several sub-segments, called terminal
segments. Therefore, \link
Arr_algebraic_segment_traits_2::Construct_x_monotone_segment_2Construct_x_monotone_segment_2\endlink constructs a sequence of
\link Arr_algebraic_segment_traits_2::X_monotone_curve_2X_monotone_curve_2\endlink objects, whose union represents the
desired weakly \f$x\f$-monotone segment. The function operator of the
\link Arr_algebraic_segment_traits_2::Construct_x_monotone_segment_2Construct_x_monotone_segment_2\endlink functor accepts as arguments
the underlying algebraic curve, the leftmost point of the segment, the
rightmost point of the segment, and an output iterator associated with
a container of output terminal segments. The function operator is
overloaded. In addition to the variant above, there is one that
accepts the underlying algebraic curve, a single point \f$p\f$, an
enumerator that delimits the segment, and an output iterator. It
returns the maximal \f$x\f$-monotone segment that contains
\f$p\f$. The enumerator specifies whether \f$p\f$ is interior to the
returned segment, its left endpoint, or its right endpoint. The third
variant accepts only two delimiting points and an output iterator. It
constructs line segments.
\cgalAdvancedBegin The subdivision terminal segments is due to the
internal representation of \f$x\f$-monotone segments, which is based
on a vertical decomposition. We assume the defining polynomial
\f$f\f$ of the curve \f$c\f$ to be square-free. That is, it
contains no divisor \f$g^2\f$ of total degree greater than zero. We
define a (complex) critical point \f$p\in\mathbb{C}^2\f$ by
\f[ f(p)=0=\frac{\partial f}{\partial y}(p). \f] An \f$x\f$-coordinate
\f$\alpha\in\mathbb{R}\f$ is critical either if some critical
point has \f$x\f$-coordinate \f$\alpha\f$, or if the leading
coefficient of \f$f\f$, considered as a polynomial in \f$y\f$,
vanishes. In particular, vertical lines and isolated points of \f$c\f$
can only take place at critical \f$x\f$-coordinates. Between two
consecutive critical \f$x\f$-coordinates the curve decomposes into a
finite number of \f$x\f$-monotone segments (the same holds to the left
of the leftmost, and to the right of the rightmost critical
\f$x\f$-coordinate). The type \link
Arr_algebraic_segment_traits_2::X_monotone_curve_2X_monotone_curve_2\endlink is only capable of representing such
segments (and sub-segments of them). Formally, a terminal segment is
either a vertical line-segment or a segment of an \f$x\f$-monotone
curve whose \f$x\f$-range does not contain critical points in its
interior. See \cgalFigureRef{aos_fig-algebraic_curves_decomposition}
for an example of a quartic curve and its decomposition into terminal
segments. Notice that six vertices split the curve into
\f$x\f$-monotone segments, and four additional vertices further split
the corresponding \f$x\f$-monotone segments into terminal segments.
\cgalAdvancedEnd
\cgalFigureBegin{aos_fig-algebraic_curves_decomposition,algebraic_curves_decomposition.png}
The critical \f$x\f$-coordinates of an algebraic curve (dashed
lines), and its decomposition into terminal segments (in different
colors). The segment from \f$p\f$ to \f$q\f$ consists of the union
of three terminal segments.
\cgalFigureEnd
The type Algebraic_real_1 must be defined by any model of theAlgebraicKernel_d_1 concept. The traits class-templateArr_algebraic_segment_traits_2 exploits an instance of theAlgebraic_kernel_d_1 class template, which models the conceptAlgebraicKernel_d_1. The exploited instance is nested in the traits
class-template. You can use this model to create algebraic numbers as
roots of univariate polynomials, and process them, for instance,
compare them or approximate them to any precision. See the
documentation of the concept AlgebraicKernel_1 for more information.
Coordinates of points are represented by the type Algebraic_real_1
nested in the traits class-template. This type is defined as the
corresponding type nested in the instance of Algebraic_kernel_d_1.
You can construct an object of type \link
Arr_algebraic_segment_traits_2::Point_2 Point_2\endlink using the
\link Arr_algebraic_segment_traits_2::Construct_point_2Construct_point_2\endlink functor nested in the traits
class-template. Its function operator is overloaded with a couple of
variants that accepts the \f$x\f$ and \f$y\f$ coordinates of the
point. Their types must be either Algebraic_real_1 orCoefficient. Another efficient variant accepts a triple
\f$(x_0,c,i)\f$, which identifies the \f$i\f$-th point (counted from
below) in the fiber of \f$c\f$ at the \f$x\f$-coordinate
\f$x_0\f$. \cgalFootnote{The fiber of a curve \f$c\f$ at some
\f$x\f$-coordinate \f$x'\f$ is the set of all points on \f$c\f$ with
\f$x\f$-coordinate \f$x'\f$. Formally, for a curve \f$c\f$ and \f$x'
\in \mathbb{R}\f$, the fiber of \f$c\f$ at \f$x'\f$ is \f$c \cap
{(x',b),|,b \in \mathbb{R}}\f$.} In the example depicted in
\cgalFigureRef{aos_fig-algebraic_curves_decomposition}, if \f$x_1\f$
denotes the \f$x\f$-coordinate of \f$p\f$, and \f$c\f$ represents the
algebraic curve, then \f$p\f$ could be represented by
\f$(x_1,c,3)\f$. If \f$x_2\f$ is the \f$x\f$-coordinate of \f$q\f$,
then \f$(x_2,c,1)\f$ is a valid representation of \f$q\f$. Points are
represented internally using the triple described above. Although the
\f$y\f$-coordinate of a point represented by an object of the nested
type Algebraic_real_1 can be obtained, we advise caution with that
option, since computing an explicit representation of the
\f$y\f$-coordinate can be rather expensive.
\cgalFigureBegin{aos_fig-algebraic_segments,algebraic_segments.png}
An arrangement of algebraic segments (solid lines), as constructed in
\ref Arrangement_on_surface_2/algebraic_segments.cpp. The supporting
curves are drawn as dashed lines.
\cgalFigureEnd
The following code exemplifies the method to construct points and the
various methods to construct algebraic segments. The computed
arrangement is depicted in \cgalFigureRef{aos_fig-algebraic_segments}.
\cgalExample{Arrangement_on_surface_2/algebraic_segments.cpp}
\subsubsection arr_ssectr_spherical Arcs of Great Circles Embedded in the Sphere
A great circle of a sphere is the intersection of the sphere and a
plane that passes through the center point of the sphere. For all
pairs of distinct points on the sphere except for antipodal points,
there is a unique great circle through the two points. There are
infinitely many great circles through antipodal points. The minor arc
of a great circle between two points is the shortest spherical-path
between them. In this sense, minor arcs are analogous to line
segments in Euclidean geometry.
The Arr_geodesic_arc_on_sphere_traits_2<Kernel,X,Y> class template
handles arcs of great circles (also known as geodesic arcs) of a unit
sphere (centered at the origin in \f$\mathbb{R}^3\f$). It is a model of the
concept AosSphericalBoundaryTraits_2; see Section \ref
aos_ssec-traits-curved. An arrangement type (an instance of the
template Arrangement_on_surface_2<GeomTraits, TopolTraits>) that
uses an instance of this traits class as the geometry traits must use
a matching topology traits. Currently, this package provides one
template, namely, Arr_spherical_topology_traits_2<GeometryTraits_2, Dcel> an instance of which can be used as the topology traits.
The unit sphere is defined over a parameter space \f$\Phi =
[\tau,2\pi+\tau] \times [-\frac{\pi}{2}, \frac{\pi}{2}]\f$, where
\f$\tau = \arctan(X, Y)\f$. By default, \f$X = -1, Y = 0\f$, which
implies \f$\tau = -\pi\f$, which implies a default parameterization
\f$\Phi = [-\pi, \pi] \times [-\frac{\pi}{2}, \frac{\pi}{2}]\f$. The
equator curve, for example, is given by \f$\gamma(t) = (2t\pi + \tau,
0)\f$, for \f$t \in [0,1]\f$. The surface is given by
\f$\phi_S(\alpha,\beta) = (\cos \alpha \cos \beta, \sin \alpha \cos
\beta, \sin \beta)\f$; see Section \ref aos_sec-curved_surfaces for
more details. This parameterization induces two contraction points
\f$(0,0,-1) = \phi_S(\alpha,-\frac{\pi}{2})\f$ and \f$(0,0,1) =
\phi_S(\alpha,\frac{\pi}{2})\f$, for all \f$\alpha\f$, referred to as
the south and north poles, respectively, and an identification curve
\f${\phi_S(\alpha,\beta),|,-\frac{\pi}{2} \leq \beta \leq
\frac{\pi}{2}}\f$, as \f$\phi_S(\pi,\beta) = \phi_S(2\pi +
\tau,\beta)\f$ for all \f$\beta\f$. When \f$\tau = -\pi\f$ (the
default), the identification curve coincides with the opposite Prime
(Greenwich) Meridian. In other words, the left and right boundary
sides of the parameter space are identified and the top and bottom
boundary sides are contracted. The arguments that substitute the
template parameters X and Y whenArr_geodesic_arc_on_sphere_traits_2<Kernel, X, Y> is instantiated
determine the value of \f$\tau\f$. Essentially, overriding their default
values rotates the identification curve about the vertical axes of the
image coordinate system (that is, the sphere coordinate system). These
arguments must be integral values. They define a not-necessarily
normalized vector \f$(x,y)\f$ in the \f$xy\f$-plane in the image
coordinate system, that bisects the identification curve. The explicit
expression for the surface above is not used at all in the
implementation of the traits. Indeed, all the required geometric
operations listed in the traits concept are implemented using only
rational arithmetic.
The following example constructs an arrangement induced by 12 arcs of
great circles embedded in the sphere. The arrangement is depicted in
\cgalFigureRef{spherical_insert}.
\cgalFigureBegin{spherical_insert,spherical_insert.png}
An arrangement induced by 12 arcs of great circles, as constructed in
\ref Arrangement_on_surface_2/spherical_insert.cpp. The number of
vertices, edges, and faces of the arrangement is 7, 13, and 8,
respectively. The intersection of the curve \f$(-1,0,0),(0,1,0)\f$
with the identification curve induces a vertex at
\f$(\frac{-11}{\sqrt{11^2+7^2}},\frac{7}{\sqrt{11^2+7^2}},0)\f$ drawn
in green. The north and south poles are drawn as little spheres. The
identification curve is drawn as a gray tube.
\cgalFigureEnd
\cgalExample{Arrangement_on_surface_2/spherical_insert.cpp}
Use the Arr_geodesic_arc_on_sphere_traits_2::Construct_point_2,Arr_geodesic_arc_on_sphere_traits_2::Construct_x_monotone_curve_2,
and Arr_geodesic_arc_on_sphere_traits_2::Construct_curve_2 functors
to construct a point, an \f$X\f$-monotone curve and a curve objects,
respectively. Observe that an \f$X\f$-monotone curve cannot intersect
the identification curve in its interior. A curve can be constructed
from either (i) two endpoints, (ii) two endpoints and a normal, or
(iii) a normal. The two endpoints determine the plane. The normal
determines the orientation of the plane and the final arc (whether it
is the minor arc or the major arc). If the normal is not provided, the
minor arc is constructed. If a curve is constructed and only the
normal is provided a full great circle is constructed. If an
\f$X\f$-monotone curve is constructed and only the normal is provided
an arc that resembles a full circle is constructed; this arc has one
endpoint that lies on the identification curve; this point is
considered both the source and target (and also the left and right)
point of the arc. See \cgalFigureRef{aos_fig-right_hand_rule} for an
illustration of the right-hand rule, which depicts the relation
between the arc and the normal.
\cgalFigureBegin{aos_fig-right_hand_rule,right_hand_rule.png}
To use the right hand rule, point your right thumb in the direction of
the normal and curl your fingers in the direction of the arc starting
with source endpoint and ending at the target endpoint.
\cgalFigureEnd
\subsection arr_ssec_tr_decorators Traits-Class Decorators
Decorator is a structural pattern that allows adding new behaviors to
objects by placing them inside special wrapper objects, called
decorators. One type of decorators attaches auxiliary data to the
geometric objects handled by the original traits. Another type enables
the retrieval of debugging and informative metadata.
\subsubsection arr_sssec_tr_data_decorators Data Decorators
Geometric traits-class decorators allow you to attach auxiliary data
to the geometric objects (curves and to points). The data is
automatically manipulated by the decorators and distributed to the
constructed geometric entities. Additional information can
alternatively be maintained by extending the vertex, halfedge, or face
types provided by the \dcel class used by the arrangement; see
Section \ref arr_ssecex_dcel for details. In many cases, however, it is
convenient to attach the data to the curve itself, exploiting the
automatic proliferation of the additional data fields from each curve
to all its induced subcurves. Moreover, as two halfedges are
associated with a single curve, storing the data once in the curve
record either saves space or avoids an indirect access from one
halfedge to its twin.
The \ref PkgArrangementOnSurface2 package includes a traits-class
decorator used to attach a data field to curves and to
\f$x\f$-monotone curves. It is a class template namedArr_curve_data_traits_2 parameterized by a base-traits class, which must
be substituted by one of the geometric traits models described in
the previous subsections or with a user-defined traits model, when the
decorator is instantiated. The curve-data decorator derives from the
base-traits class, and in particular inherits its \link
Arr_curve_data_traits_2::Point_2 Point_2\endlink type. The remaining
nested types are defined as follows:
Note that the nested \link Arr_curve_data_traits_2::Curve_2Curve_2\endlink and \link
Arr_curve_data_traits_2::X_monotone_curve_2X_monotone_curve_2\endlink are not the same, even if theBaseTraits::Curve_2 and BaseTraits::X_monotone_curve_2 are (as in
the case of the segment-traits class, for example). The extended curve
types support the additional methods \link
CGAL::Arr_curve_data_traits_2::Curve_2::data() data()\endlink and \link
CGAL::Arr_curve_data_traits_2::Curve_2::set_data() set_data()\endlink for accessing and
modifying the data field.
You can create an extended curve (or an extended \f$x\f$-monotone curve)
from a basic curve and a curve-data object. When curves are
inserted into an arrangement, they may be split, and the
decorator handles their data fields automatically as follows:
Note that by default, the CurveData type is identical to the
XMonotoneCurveData type (and the conversion functor Convert is
trivially defined). Thus, the data field associated with the
original curve is just duplicated and stored with the
\f$x\f$-monotone subcurves.