├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── Modules │ └── FindSFML.cmake ├── images └── voronoi.png └── src ├── Arc.h ├── Beachline.cpp ├── Beachline.h ├── Box.cpp ├── Box.h ├── Event.cpp ├── Event.h ├── FortuneAlgorithm.cpp ├── FortuneAlgorithm.h ├── PriorityQueue.h ├── Vector2.cpp ├── Vector2.h ├── VoronoiDiagram.cpp ├── VoronoiDiagram.h └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Vim 35 | *.swp 36 | 37 | # YouCompleteMe 38 | .ycm_extra_conf.py 39 | compile_commands.json 40 | 41 | # Build 42 | build 43 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | cmake_minimum_required(VERSION 3.1) 4 | 5 | project(Fortune) 6 | 7 | # Compiler 8 | 9 | set(CMAKE_BUILD_TYPE Release) 10 | set(CMAKE_CXX_STANDARD 14) 11 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 12 | set(CMAKE_CXX_EXTENSIONS OFF) 13 | set(CMAKE_CXX_FLAGS "-Wall -Wextra") 14 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 15 | set(CMAKE_CXX_FLAGS_RELEASE "-O3 -s") 16 | 17 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 18 | 19 | # Files 20 | 21 | file(GLOB_RECURSE SRCS src/*.cpp) 22 | file(GLOB_RECURSE HEADERS src/*.h) 23 | 24 | # Search directories 25 | 26 | include_directories(src/) 27 | 28 | # Executable 29 | 30 | set(EXECUTABLE_NAME "Fortune") 31 | add_executable(${EXECUTABLE_NAME} ${SRCS} ${HEADERS}) 32 | 33 | # Libraries 34 | 35 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules" ${CMAKE_MODULE_PATH}) 36 | 37 | find_package(SFML 2.4 REQUIRED network audio graphics window system) 38 | if(SFML_FOUND) 39 | include_directories(${SFML_INCLUDE_DIR}) 40 | target_link_libraries(${EXECUTABLE_NAME} ${SFML_LIBRARIES} ${SFML_DEPENDENCIES}) 41 | else() 42 | message(FATAL_ERROR "SFML not found") 43 | endif() 44 | 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FortuneAlgorithm 2 | 3 | A C++ implementation of the Fortune's algorithm for Voronoi diagram construction. 4 | 5 | I wrote an [article](https://pvigier.github.io/2018/11/18/fortune-algorithm-details.html) about this project on my blog, feel free to read it! 6 | 7 | You may be interested by [MyGAL](https://github.com/pvigier/MyGAL) which is the follow-up of this project. I turned it into a library and fix many edge cases making it way more robust. 8 | 9 | ![Screenshot 1](https://github.com/pvigier/FortuneAlgorithm/raw/master/images/voronoi.png) 10 | 11 | ## Build 12 | 13 | The demo requires the [SFML](https://www.sfml-dev.org/) library. 14 | 15 | Then you can build the project using [cmake](https://cmake.org/): 16 | 17 | ``` 18 | cmake . 19 | make 20 | ``` 21 | 22 | ## License 23 | 24 | Distributed under the [GNU Lesser GENERAL PUBLIC LICENSE version 3](https://www.gnu.org/licenses/lgpl-3.0.en.html) 25 | -------------------------------------------------------------------------------- /cmake/Modules/FindSFML.cmake: -------------------------------------------------------------------------------- 1 | # This script locates the SFML library 2 | # ------------------------------------ 3 | # 4 | # Usage 5 | # ----- 6 | # 7 | # When you try to locate the SFML libraries, you must specify which modules you want to use (system, window, graphics, network, audio, main). 8 | # If none is given, the SFML_LIBRARIES variable will be empty and you'll end up linking to nothing. 9 | # example: 10 | # find_package(SFML COMPONENTS graphics window system) # find the graphics, window and system modules 11 | # 12 | # You can enforce a specific version, either MAJOR.MINOR or only MAJOR. 13 | # If nothing is specified, the version won't be checked (i.e. any version will be accepted). 14 | # example: 15 | # find_package(SFML COMPONENTS ...) # no specific version required 16 | # find_package(SFML 2 COMPONENTS ...) # any 2.x version 17 | # find_package(SFML 2.4 COMPONENTS ...) # version 2.4 or greater 18 | # 19 | # By default, the dynamic libraries of SFML will be found. To find the static ones instead, 20 | # you must set the SFML_STATIC_LIBRARIES variable to TRUE before calling find_package(SFML ...). 21 | # Since you have to link yourself all the SFML dependencies when you link it statically, the following 22 | # additional variables are defined: SFML_XXX_DEPENDENCIES and SFML_DEPENDENCIES (see their detailed 23 | # description below). 24 | # In case of static linking, the SFML_STATIC macro will also be defined by this script. 25 | # example: 26 | # set(SFML_STATIC_LIBRARIES TRUE) 27 | # find_package(SFML 2 COMPONENTS network system) 28 | # 29 | # On Mac OS X if SFML_STATIC_LIBRARIES is not set to TRUE then by default CMake will search for frameworks unless 30 | # CMAKE_FIND_FRAMEWORK is set to "NEVER" for example. Please refer to CMake documentation for more details. 31 | # Moreover, keep in mind that SFML frameworks are only available as release libraries unlike dylibs which 32 | # are available for both release and debug modes. 33 | # 34 | # If SFML is not installed in a standard path, you can use the SFML_ROOT CMake (or environment) variable 35 | # to tell CMake where SFML is. 36 | # 37 | # Output 38 | # ------ 39 | # 40 | # This script defines the following variables: 41 | # - For each specified module XXX (system, window, graphics, network, audio, main): 42 | # - SFML_XXX_LIBRARY_DEBUG: the name of the debug library of the xxx module (set to SFML_XXX_LIBRARY_RELEASE is no debug version is found) 43 | # - SFML_XXX_LIBRARY_RELEASE: the name of the release library of the xxx module (set to SFML_XXX_LIBRARY_DEBUG is no release version is found) 44 | # - SFML_XXX_LIBRARY: the name of the library to link to for the xxx module (includes both debug and optimized names if necessary) 45 | # - SFML_XXX_FOUND: true if either the debug or release library of the xxx module is found 46 | # - SFML_XXX_DEPENDENCIES: the list of libraries the module depends on, in case of static linking 47 | # - SFML_LIBRARIES: the list of all libraries corresponding to the required modules 48 | # - SFML_FOUND: true if all the required modules are found 49 | # - SFML_INCLUDE_DIR: the path where SFML headers are located (the directory containing the SFML/Config.hpp file) 50 | # - SFML_DEPENDENCIES: the list of libraries SFML depends on, in case of static linking 51 | # 52 | # example: 53 | # find_package(SFML 2 COMPONENTS system window graphics audio REQUIRED) 54 | # include_directories(${SFML_INCLUDE_DIR}) 55 | # add_executable(myapp ...) 56 | # target_link_libraries(myapp ${SFML_LIBRARIES}) 57 | 58 | # define the SFML_STATIC macro if static build was chosen 59 | if(SFML_STATIC_LIBRARIES) 60 | add_definitions(-DSFML_STATIC) 61 | endif() 62 | 63 | # define the list of search paths for headers and libraries 64 | set(FIND_SFML_PATHS 65 | ${SFML_ROOT} 66 | $ENV{SFML_ROOT} 67 | ~/Library/Frameworks 68 | /Library/Frameworks 69 | /usr/local 70 | /usr 71 | /sw 72 | /opt/local 73 | /opt/csw 74 | /opt) 75 | 76 | # find the SFML include directory 77 | find_path(SFML_INCLUDE_DIR SFML/Config.hpp 78 | PATH_SUFFIXES include 79 | PATHS ${FIND_SFML_PATHS}) 80 | 81 | # check the version number 82 | set(SFML_VERSION_OK TRUE) 83 | if(SFML_FIND_VERSION AND SFML_INCLUDE_DIR) 84 | # extract the major and minor version numbers from SFML/Config.hpp 85 | # we have to handle framework a little bit differently: 86 | if("${SFML_INCLUDE_DIR}" MATCHES "SFML.framework") 87 | set(SFML_CONFIG_HPP_INPUT "${SFML_INCLUDE_DIR}/Headers/Config.hpp") 88 | else() 89 | set(SFML_CONFIG_HPP_INPUT "${SFML_INCLUDE_DIR}/SFML/Config.hpp") 90 | endif() 91 | FILE(READ "${SFML_CONFIG_HPP_INPUT}" SFML_CONFIG_HPP_CONTENTS) 92 | STRING(REGEX REPLACE ".*#define SFML_VERSION_MAJOR ([0-9]+).*" "\\1" SFML_VERSION_MAJOR "${SFML_CONFIG_HPP_CONTENTS}") 93 | STRING(REGEX REPLACE ".*#define SFML_VERSION_MINOR ([0-9]+).*" "\\1" SFML_VERSION_MINOR "${SFML_CONFIG_HPP_CONTENTS}") 94 | STRING(REGEX REPLACE ".*#define SFML_VERSION_PATCH ([0-9]+).*" "\\1" SFML_VERSION_PATCH "${SFML_CONFIG_HPP_CONTENTS}") 95 | if (NOT "${SFML_VERSION_PATCH}" MATCHES "^[0-9]+$") 96 | set(SFML_VERSION_PATCH 0) 97 | endif() 98 | math(EXPR SFML_REQUESTED_VERSION "${SFML_FIND_VERSION_MAJOR} * 10000 + ${SFML_FIND_VERSION_MINOR} * 100 + ${SFML_FIND_VERSION_PATCH}") 99 | 100 | # if we could extract them, compare with the requested version number 101 | if (SFML_VERSION_MAJOR) 102 | # transform version numbers to an integer 103 | math(EXPR SFML_VERSION "${SFML_VERSION_MAJOR} * 10000 + ${SFML_VERSION_MINOR} * 100 + ${SFML_VERSION_PATCH}") 104 | 105 | # compare them 106 | if(SFML_VERSION LESS SFML_REQUESTED_VERSION) 107 | set(SFML_VERSION_OK FALSE) 108 | endif() 109 | else() 110 | # SFML version is < 2.0 111 | if (SFML_REQUESTED_VERSION GREATER 10900) 112 | set(SFML_VERSION_OK FALSE) 113 | set(SFML_VERSION_MAJOR 1) 114 | set(SFML_VERSION_MINOR x) 115 | set(SFML_VERSION_PATCH x) 116 | endif() 117 | endif() 118 | endif() 119 | 120 | # find the requested modules 121 | set(SFML_FOUND TRUE) # will be set to false if one of the required modules is not found 122 | foreach(FIND_SFML_COMPONENT ${SFML_FIND_COMPONENTS}) 123 | string(TOLOWER ${FIND_SFML_COMPONENT} FIND_SFML_COMPONENT_LOWER) 124 | string(TOUPPER ${FIND_SFML_COMPONENT} FIND_SFML_COMPONENT_UPPER) 125 | set(FIND_SFML_COMPONENT_NAME sfml-${FIND_SFML_COMPONENT_LOWER}) 126 | 127 | # no suffix for sfml-main, it is always a static library 128 | if(FIND_SFML_COMPONENT_LOWER STREQUAL "main") 129 | # release library 130 | find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE 131 | NAMES ${FIND_SFML_COMPONENT_NAME} 132 | PATH_SUFFIXES lib64 lib 133 | PATHS ${FIND_SFML_PATHS}) 134 | 135 | # debug library 136 | find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG 137 | NAMES ${FIND_SFML_COMPONENT_NAME}-d 138 | PATH_SUFFIXES lib64 lib 139 | PATHS ${FIND_SFML_PATHS}) 140 | else() 141 | # static release library 142 | find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE 143 | NAMES ${FIND_SFML_COMPONENT_NAME}-s 144 | PATH_SUFFIXES lib64 lib 145 | PATHS ${FIND_SFML_PATHS}) 146 | 147 | # static debug library 148 | find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG 149 | NAMES ${FIND_SFML_COMPONENT_NAME}-s-d 150 | PATH_SUFFIXES lib64 lib 151 | PATHS ${FIND_SFML_PATHS}) 152 | 153 | # dynamic release library 154 | find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE 155 | NAMES ${FIND_SFML_COMPONENT_NAME} 156 | PATH_SUFFIXES lib64 lib 157 | PATHS ${FIND_SFML_PATHS}) 158 | 159 | # dynamic debug library 160 | find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG 161 | NAMES ${FIND_SFML_COMPONENT_NAME}-d 162 | PATH_SUFFIXES lib64 lib 163 | PATHS ${FIND_SFML_PATHS}) 164 | 165 | # choose the entries that fit the requested link type 166 | if(SFML_STATIC_LIBRARIES) 167 | if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE) 168 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE}) 169 | endif() 170 | if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG) 171 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG}) 172 | endif() 173 | else() 174 | if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE) 175 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE}) 176 | endif() 177 | if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG) 178 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG}) 179 | endif() 180 | endif() 181 | endif() 182 | 183 | if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG OR SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE) 184 | # library found 185 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_FOUND TRUE) 186 | 187 | # if both are found, set SFML_XXX_LIBRARY to contain both 188 | if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG AND SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE) 189 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY debug ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG} 190 | optimized ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE}) 191 | endif() 192 | 193 | # if only one debug/release variant is found, set the other to be equal to the found one 194 | if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG AND NOT SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE) 195 | # debug and not release 196 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG}) 197 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG}) 198 | endif() 199 | if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE AND NOT SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG) 200 | # release and not debug 201 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE}) 202 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE}) 203 | endif() 204 | else() 205 | # library not found 206 | set(SFML_FOUND FALSE) 207 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_FOUND FALSE) 208 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY "") 209 | set(FIND_SFML_MISSING "${FIND_SFML_MISSING} SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY") 210 | endif() 211 | 212 | # mark as advanced 213 | MARK_AS_ADVANCED(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY 214 | SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE 215 | SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG 216 | SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE 217 | SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG 218 | SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE 219 | SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG) 220 | 221 | # add to the global list of libraries 222 | set(SFML_LIBRARIES ${SFML_LIBRARIES} "${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY}") 223 | endforeach() 224 | 225 | # in case of static linking, we must also define the list of all the dependencies of SFML libraries 226 | if(SFML_STATIC_LIBRARIES) 227 | 228 | # detect the OS 229 | if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 230 | set(FIND_SFML_OS_WINDOWS 1) 231 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 232 | set(FIND_SFML_OS_LINUX 1) 233 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") 234 | set(FIND_SFML_OS_FREEBSD 1) 235 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 236 | set(FIND_SFML_OS_MACOSX 1) 237 | endif() 238 | 239 | # start with an empty list 240 | set(SFML_DEPENDENCIES) 241 | set(FIND_SFML_DEPENDENCIES_NOTFOUND) 242 | 243 | # macro that searches for a 3rd-party library 244 | macro(find_sfml_dependency output friendlyname) 245 | # No lookup in environment variables (PATH on Windows), as they may contain wrong library versions 246 | find_library(${output} NAMES ${ARGN} PATHS ${FIND_SFML_PATHS} PATH_SUFFIXES lib NO_SYSTEM_ENVIRONMENT_PATH) 247 | if(${${output}} STREQUAL "${output}-NOTFOUND") 248 | unset(output) 249 | set(FIND_SFML_DEPENDENCIES_NOTFOUND "${FIND_SFML_DEPENDENCIES_NOTFOUND} ${friendlyname}") 250 | endif() 251 | endmacro() 252 | 253 | # sfml-system 254 | list(FIND SFML_FIND_COMPONENTS "system" FIND_SFML_SYSTEM_COMPONENT) 255 | if(NOT ${FIND_SFML_SYSTEM_COMPONENT} EQUAL -1) 256 | 257 | # update the list -- these are only system libraries, no need to find them 258 | if(FIND_SFML_OS_LINUX OR FIND_SFML_OS_FREEBSD OR FIND_SFML_OS_MACOSX) 259 | set(SFML_SYSTEM_DEPENDENCIES "pthread") 260 | endif() 261 | if(FIND_SFML_OS_LINUX) 262 | set(SFML_SYSTEM_DEPENDENCIES ${SFML_SYSTEM_DEPENDENCIES} "rt") 263 | endif() 264 | if(FIND_SFML_OS_WINDOWS) 265 | set(SFML_SYSTEM_DEPENDENCIES "winmm") 266 | endif() 267 | set(SFML_DEPENDENCIES ${SFML_SYSTEM_DEPENDENCIES} ${SFML_DEPENDENCIES}) 268 | endif() 269 | 270 | # sfml-network 271 | list(FIND SFML_FIND_COMPONENTS "network" FIND_SFML_NETWORK_COMPONENT) 272 | if(NOT ${FIND_SFML_NETWORK_COMPONENT} EQUAL -1) 273 | 274 | # update the list -- these are only system libraries, no need to find them 275 | if(FIND_SFML_OS_WINDOWS) 276 | set(SFML_NETWORK_DEPENDENCIES "ws2_32") 277 | endif() 278 | set(SFML_DEPENDENCIES ${SFML_NETWORK_DEPENDENCIES} ${SFML_DEPENDENCIES}) 279 | endif() 280 | 281 | # sfml-window 282 | list(FIND SFML_FIND_COMPONENTS "window" FIND_SFML_WINDOW_COMPONENT) 283 | if(NOT ${FIND_SFML_WINDOW_COMPONENT} EQUAL -1) 284 | 285 | # find libraries 286 | if(FIND_SFML_OS_LINUX OR FIND_SFML_OS_FREEBSD) 287 | find_sfml_dependency(X11_LIBRARY "X11" X11) 288 | find_sfml_dependency(XRANDR_LIBRARY "Xrandr" Xrandr) 289 | endif() 290 | 291 | if(FIND_SFML_OS_LINUX) 292 | find_sfml_dependency(UDEV_LIBRARIES "UDev" udev libudev) 293 | endif() 294 | 295 | # update the list 296 | if(FIND_SFML_OS_WINDOWS) 297 | set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "opengl32" "winmm" "gdi32") 298 | elseif(FIND_SFML_OS_LINUX) 299 | set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "GL" ${X11_LIBRARY} ${XRANDR_LIBRARY} ${UDEV_LIBRARIES}) 300 | elseif(FIND_SFML_OS_FREEBSD) 301 | set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "GL" ${X11_LIBRARY} ${XRANDR_LIBRARY} "usbhid") 302 | elseif(FIND_SFML_OS_MACOSX) 303 | set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "-framework OpenGL -framework Foundation -framework AppKit -framework IOKit -framework Carbon") 304 | endif() 305 | set(SFML_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} ${SFML_DEPENDENCIES}) 306 | endif() 307 | 308 | # sfml-graphics 309 | list(FIND SFML_FIND_COMPONENTS "graphics" FIND_SFML_GRAPHICS_COMPONENT) 310 | if(NOT ${FIND_SFML_GRAPHICS_COMPONENT} EQUAL -1) 311 | 312 | # find libraries 313 | find_sfml_dependency(FREETYPE_LIBRARY "FreeType" freetype) 314 | 315 | # update the list 316 | set(SFML_GRAPHICS_DEPENDENCIES ${FREETYPE_LIBRARY}) 317 | set(SFML_DEPENDENCIES ${SFML_GRAPHICS_DEPENDENCIES} ${SFML_DEPENDENCIES}) 318 | endif() 319 | 320 | # sfml-audio 321 | list(FIND SFML_FIND_COMPONENTS "audio" FIND_SFML_AUDIO_COMPONENT) 322 | if(NOT ${FIND_SFML_AUDIO_COMPONENT} EQUAL -1) 323 | 324 | # find libraries 325 | find_sfml_dependency(OPENAL_LIBRARY "OpenAL" openal openal32) 326 | find_sfml_dependency(OGG_LIBRARY "Ogg" ogg) 327 | find_sfml_dependency(VORBIS_LIBRARY "Vorbis" vorbis) 328 | find_sfml_dependency(VORBISFILE_LIBRARY "VorbisFile" vorbisfile) 329 | find_sfml_dependency(VORBISENC_LIBRARY "VorbisEnc" vorbisenc) 330 | find_sfml_dependency(FLAC_LIBRARY "FLAC" FLAC) 331 | 332 | # update the list 333 | set(SFML_AUDIO_DEPENDENCIES ${OPENAL_LIBRARY} ${FLAC_LIBRARY} ${VORBISENC_LIBRARY} ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY}) 334 | set(SFML_DEPENDENCIES ${SFML_DEPENDENCIES} ${SFML_AUDIO_DEPENDENCIES}) 335 | endif() 336 | 337 | endif() 338 | 339 | # handle errors 340 | if(NOT SFML_VERSION_OK) 341 | # SFML version not ok 342 | set(FIND_SFML_ERROR "SFML found but version too low (requested: ${SFML_FIND_VERSION}, found: ${SFML_VERSION_MAJOR}.${SFML_VERSION_MINOR}.${SFML_VERSION_PATCH})") 343 | set(SFML_FOUND FALSE) 344 | elseif(SFML_STATIC_LIBRARIES AND FIND_SFML_DEPENDENCIES_NOTFOUND) 345 | set(FIND_SFML_ERROR "SFML found but some of its dependencies are missing (${FIND_SFML_DEPENDENCIES_NOTFOUND})") 346 | set(SFML_FOUND FALSE) 347 | elseif(NOT SFML_FOUND) 348 | # include directory or library not found 349 | set(FIND_SFML_ERROR "Could NOT find SFML (missing: ${FIND_SFML_MISSING})") 350 | endif() 351 | if (NOT SFML_FOUND) 352 | if(SFML_FIND_REQUIRED) 353 | # fatal error 354 | message(FATAL_ERROR ${FIND_SFML_ERROR}) 355 | elseif(NOT SFML_FIND_QUIETLY) 356 | # error but continue 357 | message("${FIND_SFML_ERROR}") 358 | endif() 359 | endif() 360 | 361 | # handle success 362 | if(SFML_FOUND AND NOT SFML_FIND_QUIETLY) 363 | message(STATUS "Found SFML ${SFML_VERSION_MAJOR}.${SFML_VERSION_MINOR}.${SFML_VERSION_PATCH} in ${SFML_INCLUDE_DIR}") 364 | endif() 365 | -------------------------------------------------------------------------------- /images/voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pvigier/FortuneAlgorithm/80c94c4c805d1adffe07a0f43ce925909a09512a/images/voronoi.png -------------------------------------------------------------------------------- /src/Arc.h: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | // My includes 21 | #include "VoronoiDiagram.h" 22 | 23 | class Event; 24 | 25 | struct Arc 26 | { 27 | enum class Color{RED, BLACK}; 28 | 29 | // Hierarchy 30 | Arc* parent; 31 | Arc* left; 32 | Arc* right; 33 | // Diagram 34 | VoronoiDiagram::Site* site; 35 | VoronoiDiagram::HalfEdge* leftHalfEdge; 36 | VoronoiDiagram::HalfEdge* rightHalfEdge; 37 | Event* event; 38 | // Optimizations 39 | Arc* prev; 40 | Arc* next; 41 | // Only for balancing 42 | Color color; 43 | }; 44 | 45 | -------------------------------------------------------------------------------- /src/Beachline.cpp: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "Beachline.h" 19 | // STL 20 | #include 21 | #include 22 | // My includes 23 | #include "Arc.h" 24 | 25 | Beachline::Beachline() : mNil(new Arc), mRoot(mNil) 26 | { 27 | mNil->color = Arc::Color::BLACK; 28 | } 29 | 30 | Beachline::~Beachline() 31 | { 32 | free(mRoot); 33 | delete mNil; 34 | } 35 | 36 | Arc* Beachline::createArc(VoronoiDiagram::Site* site) 37 | { 38 | return new Arc{mNil, mNil, mNil, site, nullptr, nullptr, nullptr, mNil, mNil, Arc::Color::RED}; 39 | } 40 | 41 | bool Beachline::isEmpty() const 42 | { 43 | return isNil(mRoot); 44 | } 45 | 46 | bool Beachline::isNil(const Arc* x) const 47 | { 48 | return x == mNil; 49 | } 50 | 51 | void Beachline::setRoot(Arc* x) 52 | { 53 | mRoot = x; 54 | mRoot->color = Arc::Color::BLACK; 55 | } 56 | 57 | Arc* Beachline::getLeftmostArc() const 58 | { 59 | Arc* x = mRoot; 60 | while (!isNil(x->prev)) 61 | x = x->prev; 62 | return x; 63 | } 64 | 65 | Arc* Beachline::locateArcAbove(const Vector2& point, double l) const 66 | { 67 | Arc* node = mRoot; 68 | bool found = false; 69 | while (!found) 70 | { 71 | double breakpointLeft = -std::numeric_limits::infinity(); 72 | double breakpointRight = std::numeric_limits::infinity(); 73 | if (!isNil(node->prev)) 74 | breakpointLeft = computeBreakpoint(node->prev->site->point, node->site->point, l); 75 | if (!isNil(node->next)) 76 | breakpointRight = computeBreakpoint(node->site->point, node->next->site->point, l); 77 | if (point.x < breakpointLeft) 78 | node = node->left; 79 | else if (point.x > breakpointRight) 80 | node = node->right; 81 | else 82 | found = true; 83 | } 84 | return node; 85 | } 86 | 87 | void Beachline::insertBefore(Arc* x, Arc* y) 88 | { 89 | // Find the right place 90 | if (isNil(x->left)) 91 | { 92 | x->left = y; 93 | y->parent = x; 94 | } 95 | else 96 | { 97 | x->prev->right = y; 98 | y->parent = x->prev; 99 | } 100 | // Set the pointers 101 | y->prev = x->prev; 102 | if (!isNil(y->prev)) 103 | y->prev->next = y; 104 | y->next = x; 105 | x->prev = y; 106 | // Balance the tree 107 | insertFixup(y); 108 | } 109 | 110 | void Beachline::insertAfter(Arc* x, Arc* y) 111 | { 112 | // Find the right place 113 | if (isNil(x->right)) 114 | { 115 | x->right = y; 116 | y->parent = x; 117 | } 118 | else 119 | { 120 | x->next->left = y; 121 | y->parent = x->next; 122 | } 123 | // Set the pointers 124 | y->next = x->next; 125 | if (!isNil(y->next)) 126 | y->next->prev = y; 127 | y->prev = x; 128 | x->next = y; 129 | // Balance the tree 130 | insertFixup(y); 131 | } 132 | 133 | void Beachline::replace(Arc* x, Arc* y) 134 | { 135 | transplant(x, y); 136 | y->left = x->left; 137 | y->right = x->right; 138 | if (!isNil(y->left)) 139 | y->left->parent = y; 140 | if (!isNil(y->right)) 141 | y->right->parent = y; 142 | y->prev = x->prev; 143 | y->next = x->next; 144 | if (!isNil(y->prev)) 145 | y->prev->next = y; 146 | if (!isNil(y->next)) 147 | y->next->prev = y; 148 | y->color = x->color; 149 | } 150 | 151 | void Beachline::remove(Arc* z) 152 | { 153 | Arc* y = z; 154 | Arc::Color yOriginalColor = y->color; 155 | Arc* x; 156 | if (isNil(z->left)) 157 | { 158 | x = z->right; 159 | transplant(z, z->right); 160 | } 161 | else if (isNil(z->right)) 162 | { 163 | x = z->left; 164 | transplant(z, z->left); 165 | } 166 | else 167 | { 168 | y = minimum(z->right); 169 | yOriginalColor = y->color; 170 | x = y->right; 171 | if (y->parent == z) 172 | x->parent = y; // Because x could be Nil 173 | else 174 | { 175 | transplant(y, y->right); 176 | y->right = z->right; 177 | y->right->parent = y; 178 | } 179 | transplant(z, y); 180 | y->left = z->left; 181 | y->left->parent = y; 182 | y->color = z->color; 183 | } 184 | if (yOriginalColor == Arc::Color::BLACK) 185 | removeFixup(x); 186 | // Update next and prev 187 | if (!isNil(z->prev)) 188 | z->prev->next = z->next; 189 | if (!isNil(z->next)) 190 | z->next->prev = z->prev; 191 | } 192 | 193 | std::ostream& Beachline::print(std::ostream& os) const 194 | { 195 | //return printArc(os, mRoot); 196 | Arc* arc = getLeftmostArc(); 197 | while (!isNil(arc)) 198 | { 199 | os << arc->site->index << ' '; 200 | arc = arc->next; 201 | } 202 | return os; 203 | } 204 | 205 | Arc* Beachline::minimum(Arc* x) const 206 | { 207 | while (!isNil(x->left)) 208 | x = x->left; 209 | return x; 210 | } 211 | 212 | void Beachline::transplant(Arc* u, Arc* v) 213 | { 214 | if (isNil(u->parent)) 215 | mRoot = v; 216 | else if (u == u->parent->left) 217 | u->parent->left = v; 218 | else 219 | u->parent->right = v; 220 | v->parent = u->parent; 221 | } 222 | 223 | void Beachline::insertFixup(Arc* z) 224 | { 225 | while (z->parent->color == Arc::Color::RED) 226 | { 227 | if (z->parent == z->parent->parent->left) 228 | { 229 | Arc* y = z->parent->parent->right; 230 | // Case 1 231 | if (y->color == Arc::Color::RED) 232 | { 233 | z->parent->color = Arc::Color::BLACK; 234 | y->color = Arc::Color::BLACK; 235 | z->parent->parent->color = Arc::Color::RED; 236 | z = z->parent->parent; 237 | } 238 | else 239 | { 240 | // Case 2 241 | if (z == z->parent->right) 242 | { 243 | z = z->parent; 244 | leftRotate(z); 245 | } 246 | // Case 3 247 | z->parent->color = Arc::Color::BLACK; 248 | z->parent->parent->color = Arc::Color::RED; 249 | rightRotate(z->parent->parent); 250 | } 251 | } 252 | else 253 | { 254 | Arc* y = z->parent->parent->left; 255 | // Case 1 256 | if (y->color == Arc::Color::RED) 257 | { 258 | z->parent->color = Arc::Color::BLACK; 259 | y->color = Arc::Color::BLACK; 260 | z->parent->parent->color = Arc::Color::RED; 261 | z = z->parent->parent; 262 | } 263 | else 264 | { 265 | // Case 2 266 | if (z == z->parent->left) 267 | { 268 | z = z->parent; 269 | rightRotate(z); 270 | } 271 | // Case 3 272 | z->parent->color = Arc::Color::BLACK; 273 | z->parent->parent->color = Arc::Color::RED; 274 | leftRotate(z->parent->parent); 275 | } 276 | } 277 | } 278 | mRoot->color = Arc::Color::BLACK; 279 | } 280 | 281 | void Beachline::removeFixup(Arc* x) 282 | { 283 | 284 | while (x != mRoot && x->color == Arc::Color::BLACK) 285 | { 286 | Arc* w; 287 | if (x == x->parent->left) 288 | { 289 | w = x->parent->right; 290 | // Case 1 291 | if (w->color == Arc::Color::RED) 292 | { 293 | w->color = Arc::Color::BLACK; 294 | x->parent->color = Arc::Color::RED; 295 | leftRotate(x->parent); 296 | w = x->parent->right; 297 | } 298 | // Case 2 299 | if (w->left->color == Arc::Color::BLACK && w->right->color == Arc::Color::BLACK) 300 | { 301 | w->color = Arc::Color::RED; 302 | x = x->parent; 303 | } 304 | else 305 | { 306 | // Case 3 307 | if (w->right->color == Arc::Color::BLACK) 308 | { 309 | w->left->color = Arc::Color::BLACK; 310 | w->color = Arc::Color::RED; 311 | rightRotate(w); 312 | w = x->parent->right; 313 | } 314 | // Case 4 315 | w->color = x->parent->color; 316 | x->parent->color = Arc::Color::BLACK; 317 | w->right->color = Arc::Color::BLACK; 318 | leftRotate(x->parent); 319 | x = mRoot; 320 | } 321 | } 322 | else 323 | { 324 | w = x->parent->left; 325 | // Case 1 326 | if (w->color == Arc::Color::RED) 327 | { 328 | w->color = Arc::Color::BLACK; 329 | x->parent->color = Arc::Color::RED; 330 | rightRotate(x->parent); 331 | w = x->parent->left; 332 | } 333 | // Case 2 334 | if (w->left->color == Arc::Color::BLACK && w->right->color == Arc::Color::BLACK) 335 | { 336 | w->color = Arc::Color::RED; 337 | x = x->parent; 338 | } 339 | else 340 | { 341 | // Case 3 342 | if (w->left->color == Arc::Color::BLACK) 343 | { 344 | w->right->color = Arc::Color::BLACK; 345 | w->color = Arc::Color::RED; 346 | leftRotate(w); 347 | w = x->parent->left; 348 | } 349 | // Case 4 350 | w->color = x->parent->color; 351 | x->parent->color = Arc::Color::BLACK; 352 | w->left->color = Arc::Color::BLACK; 353 | rightRotate(x->parent); 354 | x = mRoot; 355 | } 356 | } 357 | } 358 | x->color = Arc::Color::BLACK; 359 | } 360 | 361 | void Beachline::leftRotate(Arc* x) 362 | { 363 | Arc* y = x->right; 364 | x->right = y->left; 365 | if (!isNil(y->left)) 366 | y->left->parent = x; 367 | y->parent = x->parent; 368 | if (isNil(x->parent)) 369 | mRoot = y; 370 | else if (x->parent->left == x) 371 | x->parent->left = y; 372 | else 373 | x->parent->right = y; 374 | y->left = x; 375 | x->parent = y; 376 | } 377 | 378 | void Beachline::rightRotate(Arc* y) 379 | { 380 | Arc* x = y->left; 381 | y->left = x->right; 382 | if (!isNil(x->right)) 383 | x->right->parent = y; 384 | x->parent = y->parent; 385 | if (isNil(y->parent)) 386 | mRoot = x; 387 | else if (y->parent->left == y) 388 | y->parent->left = x; 389 | else 390 | y->parent->right = x; 391 | x->right = y; 392 | y->parent = x; 393 | } 394 | 395 | double Beachline::computeBreakpoint(const Vector2& point1, const Vector2& point2, double l) const 396 | { 397 | double x1 = point1.x, y1 = point1.y, x2 = point2.x, y2 = point2.y; 398 | double d1 = 1.0 / (2.0 * (y1 - l)); 399 | double d2 = 1.0 / (2.0 * (y2 - l)); 400 | double a = d1 - d2; 401 | double b = 2.0 * (x2 * d2 - x1 * d1); 402 | double c = (y1 * y1 + x1 * x1 - l * l) * d1 - (y2 * y2 + x2 * x2 - l * l) * d2; 403 | double delta = b * b - 4.0 * a * c; 404 | return (-b + std::sqrt(delta)) / (2.0 * a); 405 | } 406 | 407 | void Beachline::free(Arc* x) 408 | { 409 | if (isNil(x)) 410 | return; 411 | else 412 | { 413 | free(x->left); 414 | free(x->right); 415 | delete x; 416 | } 417 | } 418 | 419 | std::ostream& Beachline::printArc(std::ostream& os, const Arc* arc, std::string tabs) const 420 | { 421 | os << tabs << arc->site->index << ' ' << arc->leftHalfEdge << ' ' << arc->rightHalfEdge << std::endl; 422 | if (!isNil(arc->left)) 423 | printArc(os, arc->left, tabs + '\t'); 424 | if (!isNil(arc->right)) 425 | printArc(os, arc->right, tabs + '\t'); 426 | return os; 427 | } 428 | 429 | std::ostream& operator<<(std::ostream& os, const Beachline& beachline) 430 | { 431 | return beachline.print(os); 432 | } 433 | -------------------------------------------------------------------------------- /src/Beachline.h: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | // My includes 21 | #include "Vector2.h" 22 | #include "VoronoiDiagram.h" 23 | 24 | class Arc; 25 | 26 | class Beachline 27 | { 28 | public: 29 | Beachline(); 30 | ~Beachline(); 31 | 32 | // Remove copy and move operations 33 | Beachline(const Beachline&) = delete; 34 | Beachline& operator=(const Beachline&) = delete; 35 | Beachline(Beachline&&) = delete; 36 | Beachline& operator=(Beachline&&) = delete; 37 | 38 | Arc* createArc(VoronoiDiagram::Site* site); 39 | 40 | bool isEmpty() const; 41 | bool isNil(const Arc* x) const; 42 | void setRoot(Arc* x); 43 | Arc* getLeftmostArc() const; 44 | 45 | Arc* locateArcAbove(const Vector2& point, double l) const; 46 | void insertBefore(Arc* x, Arc* y); 47 | void insertAfter(Arc* x, Arc* y); 48 | void replace(Arc* x, Arc* y); 49 | void remove(Arc* z); 50 | 51 | std::ostream& print(std::ostream& os) const; 52 | 53 | private: 54 | Arc* mNil; 55 | Arc* mRoot; 56 | 57 | // Utility methods 58 | Arc* minimum(Arc* x) const; 59 | void transplant(Arc* u, Arc* v); 60 | 61 | // Fixup functions 62 | void insertFixup(Arc* z); 63 | void removeFixup(Arc* x); 64 | 65 | // Rotations 66 | void leftRotate(Arc* x); 67 | void rightRotate(Arc* y); 68 | 69 | double computeBreakpoint(const Vector2& point1, const Vector2& point2, double l) const; 70 | 71 | void free(Arc* x); 72 | 73 | std::ostream& printArc(std::ostream& os, const Arc* arc, std::string tabs = "") const; 74 | }; 75 | 76 | std::ostream& operator<<(std::ostream& os, const Beachline& beachline); 77 | -------------------------------------------------------------------------------- /src/Box.cpp: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "Box.h" 19 | 20 | bool Box::contains(const Vector2& point) const 21 | { 22 | return point.x >= left - EPSILON && point.x <= right + EPSILON && 23 | point.y >= bottom - EPSILON && point.y <= top + EPSILON; 24 | } 25 | 26 | Box::Intersection Box::getFirstIntersection(const Vector2& origin, const Vector2& direction) const 27 | { 28 | // origin must be in the box 29 | Intersection intersection; 30 | double t = std::numeric_limits::infinity(); 31 | if (direction.x > 0.0) 32 | { 33 | t = (right - origin.x) / direction.x; 34 | intersection.side = Side::RIGHT; 35 | intersection.point = origin + t * direction; 36 | } 37 | else if (direction.x < 0.0) 38 | { 39 | t = (left - origin.x) / direction.x; 40 | intersection.side = Side::LEFT; 41 | intersection.point = origin + t * direction; 42 | } 43 | if (direction.y > 0.0) 44 | { 45 | double newT = (top - origin.y) / direction.y; 46 | if (newT < t) 47 | { 48 | intersection.side = Side::TOP; 49 | intersection.point = origin + newT * direction; 50 | } 51 | } 52 | else if (direction.y < 0.0) 53 | { 54 | double newT = (bottom - origin.y) / direction.y; 55 | if (newT < t) 56 | { 57 | intersection.side = Side::BOTTOM; 58 | intersection.point = origin + newT * direction; 59 | } 60 | } 61 | return intersection; 62 | } 63 | 64 | int Box::getIntersections(const Vector2& origin, const Vector2& destination, std::array& intersections) const 65 | { 66 | // WARNING: If the intersection is a corner, both intersections are equals 67 | Vector2 direction = destination - origin; 68 | std::array t; 69 | std::size_t i = 0; // index of the current intersection 70 | // Left 71 | if (origin.x < left - EPSILON || destination.x < left - EPSILON) 72 | { 73 | t[i] = (left - origin.x) / direction.x; 74 | if (t[i] > EPSILON && t[i] < 1.0 - EPSILON) 75 | { 76 | intersections[i].side = Side::LEFT; 77 | intersections[i].point = origin + t[i] * direction; 78 | if (intersections[i].point.y >= bottom - EPSILON && intersections[i].point.y <= top + EPSILON) 79 | ++i; 80 | } 81 | } 82 | // Right 83 | if (origin.x > right + EPSILON || destination.x > right + EPSILON) 84 | { 85 | t[i] = (right - origin.x) / direction.x; 86 | if (t[i] > EPSILON && t[i] < 1.0 - EPSILON) 87 | { 88 | intersections[i].side = Side::RIGHT; 89 | intersections[i].point = origin + t[i] * direction; 90 | if (intersections[i].point.y >= bottom - EPSILON && intersections[i].point.y <= top + EPSILON) 91 | ++i; 92 | } 93 | } 94 | // Bottom 95 | if (origin.y < bottom - EPSILON || destination.y < bottom - EPSILON) 96 | { 97 | t[i] = (bottom - origin.y) / direction.y; 98 | if (i < 2 && t[i] > EPSILON && t[i] < 1.0 - EPSILON) 99 | { 100 | intersections[i].side = Side::BOTTOM; 101 | intersections[i].point = origin + t[i] * direction; 102 | if (intersections[i].point.x >= left - EPSILON && intersections[i].point.x <= right + EPSILON) 103 | ++i; 104 | } 105 | } 106 | // Top 107 | if (origin.y > top + EPSILON || destination.y > top + EPSILON) 108 | { 109 | t[i] = (top - origin.y) / direction.y; 110 | if (i < 2 && t[i] > EPSILON && t[i] < 1.0 - EPSILON) 111 | { 112 | intersections[i].side = Side::TOP; 113 | intersections[i].point = origin + t[i] * direction; 114 | if (intersections[i].point.x >= left - EPSILON && intersections[i].point.x <= right + EPSILON) 115 | ++i; 116 | } 117 | } 118 | // Sort the intersections from the nearest to the farthest 119 | if (i == 2 && t[0] > t[1]) 120 | std::swap(intersections[0], intersections[1]); 121 | return i; 122 | } 123 | -------------------------------------------------------------------------------- /src/Box.h: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | // STL 21 | #include 22 | #include 23 | // My includes 24 | #include "Vector2.h" 25 | 26 | class Box 27 | { 28 | public: 29 | // Be careful, y-axis is oriented to the top like in math 30 | enum class Side : int {LEFT, BOTTOM, RIGHT, TOP}; 31 | 32 | struct Intersection 33 | { 34 | Side side; 35 | Vector2 point; 36 | }; 37 | 38 | double left; 39 | double bottom; 40 | double right; 41 | double top; 42 | 43 | bool contains(const Vector2& point) const; 44 | Intersection getFirstIntersection(const Vector2& origin, const Vector2& direction) const; // Useful for Fortune's algorithm 45 | int getIntersections(const Vector2& origin, const Vector2& destination, std::array& intersections) const; // Useful for diagram intersection 46 | 47 | private: 48 | static constexpr double EPSILON = std::numeric_limits::epsilon(); 49 | }; 50 | 51 | -------------------------------------------------------------------------------- /src/Event.cpp: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "Event.h" 19 | 20 | Event::Event(VoronoiDiagram::Site* site) : type(Type::SITE), y(site->point.y), index(-1), site(site) 21 | { 22 | 23 | } 24 | 25 | Event::Event(double y, Vector2 point, Arc* arc) : type(Type::CIRCLE), y(y), index(-1), point(point), arc(arc) 26 | { 27 | 28 | 29 | } 30 | bool operator<(const Event& lhs, const Event& rhs) 31 | { 32 | return lhs.y < rhs.y; 33 | } 34 | 35 | std::ostream& operator<<(std::ostream& os, const Event& event) 36 | { 37 | if(event.type == Event::Type::SITE) 38 | os << "S(" << event.site->index << ", " << event.y << ")"; 39 | else 40 | os << "C(" << event.arc << ", " << event.y << ", " << event.point << ")"; 41 | return os; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/Event.h: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | // My includes 21 | #include "Vector2.h" 22 | #include "VoronoiDiagram.h" 23 | 24 | class Arc; 25 | 26 | class Event 27 | { 28 | public: 29 | enum class Type{SITE, CIRCLE}; 30 | 31 | // Site event 32 | Event(VoronoiDiagram::Site* site); 33 | // Circle event 34 | Event(double y, Vector2 point, Arc* arc); 35 | 36 | const Type type; 37 | double y; 38 | int index; 39 | // Site event 40 | VoronoiDiagram::Site* site; 41 | // Circle event 42 | Vector2 point; 43 | Arc* arc; 44 | 45 | }; 46 | 47 | bool operator<(const Event& lhs, const Event& rhs); 48 | std::ostream& operator<<(std::ostream& os, const Event& event); 49 | 50 | -------------------------------------------------------------------------------- /src/FortuneAlgorithm.cpp: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "FortuneAlgorithm.h" 19 | // My includes 20 | #include "Arc.h" 21 | #include "Event.h" 22 | 23 | FortuneAlgorithm::FortuneAlgorithm(std::vector points) : mDiagram(std::move(points)) 24 | { 25 | 26 | } 27 | 28 | FortuneAlgorithm::~FortuneAlgorithm() = default; 29 | 30 | void FortuneAlgorithm::construct() 31 | { 32 | // Initialize event queue 33 | for (std::size_t i = 0; i < mDiagram.getNbSites(); ++i) 34 | mEvents.push(std::make_unique(mDiagram.getSite(i))); 35 | 36 | // Process events 37 | while (!mEvents.isEmpty()) 38 | { 39 | std::unique_ptr event = mEvents.pop(); 40 | mBeachlineY = event->y; 41 | if(event->type == Event::Type::SITE) 42 | handleSiteEvent(event.get()); 43 | else 44 | handleCircleEvent(event.get()); 45 | } 46 | } 47 | 48 | VoronoiDiagram FortuneAlgorithm::getDiagram() 49 | { 50 | return std::move(mDiagram); 51 | } 52 | 53 | void FortuneAlgorithm::handleSiteEvent(Event* event) 54 | { 55 | VoronoiDiagram::Site* site = event->site; 56 | // 1. Check if the bachline is empty 57 | if (mBeachline.isEmpty()) 58 | { 59 | mBeachline.setRoot(mBeachline.createArc(site)); 60 | return; 61 | } 62 | // 2. Look for the arc above the site 63 | Arc* arcToBreak = mBeachline.locateArcAbove(site->point, mBeachlineY); 64 | deleteEvent(arcToBreak); 65 | // 3. Replace this arc by the new arcs 66 | Arc* middleArc = breakArc(arcToBreak, site); 67 | Arc* leftArc = middleArc->prev; 68 | Arc* rightArc = middleArc->next; 69 | // 4. Add an edge in the diagram 70 | addEdge(leftArc, middleArc); 71 | middleArc->rightHalfEdge = middleArc->leftHalfEdge; 72 | rightArc->leftHalfEdge = leftArc->rightHalfEdge; 73 | // 5. Check circle events 74 | // Left triplet 75 | if (!mBeachline.isNil(leftArc->prev)) 76 | addEvent(leftArc->prev, leftArc, middleArc); 77 | // Right triplet 78 | if (!mBeachline.isNil(rightArc->next)) 79 | addEvent(middleArc, rightArc, rightArc->next); 80 | } 81 | 82 | void FortuneAlgorithm::handleCircleEvent(Event* event) 83 | { 84 | Vector2 point = event->point; 85 | Arc* arc = event->arc; 86 | // 1. Add vertex 87 | VoronoiDiagram::Vertex* vertex = mDiagram.createVertex(point); 88 | // 2. Delete all the events with this arc 89 | Arc* leftArc = arc->prev; 90 | Arc* rightArc = arc->next; 91 | deleteEvent(leftArc); 92 | deleteEvent(rightArc); 93 | // 3. Update the beachline and the diagram 94 | removeArc(arc, vertex); 95 | // 4. Add new circle events 96 | // Left triplet 97 | if (!mBeachline.isNil(leftArc->prev)) 98 | addEvent(leftArc->prev, leftArc, rightArc); 99 | // Right triplet 100 | if (!mBeachline.isNil(rightArc->next)) 101 | addEvent(leftArc, rightArc, rightArc->next); 102 | } 103 | 104 | Arc* FortuneAlgorithm::breakArc(Arc* arc, VoronoiDiagram::Site* site) 105 | { 106 | // Create the new subtree 107 | Arc* middleArc = mBeachline.createArc(site); 108 | Arc* leftArc = mBeachline.createArc(arc->site); 109 | leftArc->leftHalfEdge = arc->leftHalfEdge; 110 | Arc* rightArc = mBeachline.createArc(arc->site); 111 | rightArc->rightHalfEdge = arc->rightHalfEdge; 112 | // Insert the subtree in the beachline 113 | mBeachline.replace(arc, middleArc); 114 | mBeachline.insertBefore(middleArc, leftArc); 115 | mBeachline.insertAfter(middleArc, rightArc); 116 | // Delete old arc 117 | delete arc; 118 | // Return the middle arc 119 | return middleArc; 120 | } 121 | 122 | void FortuneAlgorithm::removeArc(Arc* arc, VoronoiDiagram::Vertex* vertex) 123 | { 124 | // End edges 125 | setDestination(arc->prev, arc, vertex); 126 | setDestination(arc, arc->next, vertex); 127 | // Join the edges of the middle arc 128 | arc->leftHalfEdge->next = arc->rightHalfEdge; 129 | arc->rightHalfEdge->prev = arc->leftHalfEdge; 130 | // Update beachline 131 | mBeachline.remove(arc); 132 | // Create a new edge 133 | VoronoiDiagram::HalfEdge* prevHalfEdge = arc->prev->rightHalfEdge; 134 | VoronoiDiagram::HalfEdge* nextHalfEdge = arc->next->leftHalfEdge; 135 | addEdge(arc->prev, arc->next); 136 | setOrigin(arc->prev, arc->next, vertex); 137 | setPrevHalfEdge(arc->prev->rightHalfEdge, prevHalfEdge); 138 | setPrevHalfEdge(nextHalfEdge, arc->next->leftHalfEdge); 139 | // Delete node 140 | delete arc; 141 | } 142 | 143 | bool FortuneAlgorithm::isMovingRight(const Arc* left, const Arc* right) const 144 | { 145 | return left->site->point.y < right->site->point.y; 146 | } 147 | 148 | double FortuneAlgorithm::getInitialX(const Arc* left, const Arc* right, bool movingRight) const 149 | { 150 | return movingRight ? left->site->point.x : right->site->point.x; 151 | } 152 | 153 | void FortuneAlgorithm::addEdge(Arc* left, Arc* right) 154 | { 155 | // Create two new half edges 156 | left->rightHalfEdge = mDiagram.createHalfEdge(left->site->face); 157 | right->leftHalfEdge = mDiagram.createHalfEdge(right->site->face); 158 | // Set the two half edges twins 159 | left->rightHalfEdge->twin = right->leftHalfEdge; 160 | right->leftHalfEdge->twin = left->rightHalfEdge; 161 | } 162 | 163 | void FortuneAlgorithm::setOrigin(Arc* left, Arc* right, VoronoiDiagram::Vertex* vertex) 164 | { 165 | left->rightHalfEdge->destination = vertex; 166 | right->leftHalfEdge->origin = vertex; 167 | } 168 | 169 | void FortuneAlgorithm::setDestination(Arc* left, Arc* right, VoronoiDiagram::Vertex* vertex) 170 | { 171 | left->rightHalfEdge->origin = vertex; 172 | right->leftHalfEdge->destination = vertex; 173 | } 174 | 175 | void FortuneAlgorithm::setPrevHalfEdge(VoronoiDiagram::HalfEdge* prev, VoronoiDiagram::HalfEdge* next) 176 | { 177 | prev->next = next; 178 | next->prev = prev; 179 | } 180 | 181 | void FortuneAlgorithm::addEvent(Arc* left, Arc* middle, Arc* right) 182 | { 183 | double y; 184 | Vector2 convergencePoint = computeConvergencePoint(left->site->point, middle->site->point, right->site->point, y); 185 | bool isBelow = y <= mBeachlineY; 186 | bool leftBreakpointMovingRight = isMovingRight(left, middle); 187 | bool rightBreakpointMovingRight = isMovingRight(middle, right); 188 | double leftInitialX = getInitialX(left, middle, leftBreakpointMovingRight); 189 | double rightInitialX = getInitialX(middle, right, rightBreakpointMovingRight); 190 | bool isValid = 191 | ((leftBreakpointMovingRight && leftInitialX < convergencePoint.x) || 192 | (!leftBreakpointMovingRight && leftInitialX > convergencePoint.x)) && 193 | ((rightBreakpointMovingRight && rightInitialX < convergencePoint.x) || 194 | (!rightBreakpointMovingRight && rightInitialX > convergencePoint.x)); 195 | if (isValid && isBelow) 196 | { 197 | std::unique_ptr event = std::make_unique(y, convergencePoint, middle); 198 | middle->event = event.get(); 199 | mEvents.push(std::move(event)); 200 | } 201 | } 202 | 203 | void FortuneAlgorithm::deleteEvent(Arc* arc) 204 | { 205 | if (arc->event != nullptr) 206 | { 207 | mEvents.remove(arc->event->index); 208 | arc->event = nullptr; 209 | } 210 | } 211 | 212 | Vector2 FortuneAlgorithm::computeConvergencePoint(const Vector2& point1, const Vector2& point2, const Vector2& point3, double& y) const 213 | { 214 | Vector2 v1 = (point1 - point2).getOrthogonal(); 215 | Vector2 v2 = (point2 - point3).getOrthogonal(); 216 | Vector2 delta = 0.5 * (point3 - point1); 217 | double t = delta.getDet(v2) / v1.getDet(v2); 218 | Vector2 center = 0.5 * (point1 + point2) + t * v1; 219 | double r = center.getDistance(point1); 220 | y = center.y - r; 221 | return center; 222 | } 223 | 224 | // Bound 225 | 226 | #include 227 | #include 228 | 229 | bool FortuneAlgorithm::bound(Box box) 230 | { 231 | // Make sure the bounding box contains all the vertices 232 | for (const auto& vertex : mDiagram.getVertices()) // Much faster when using vector, maybe we can test vertices in border cells to speed up 233 | { 234 | box.left = std::min(vertex.point.x, box.left); 235 | box.bottom = std::min(vertex.point.y, box.bottom); 236 | box.right = std::max(vertex.point.x, box.right); 237 | box.top = std::max(vertex.point.y, box.top); 238 | } 239 | // Retrieve all non bounded half edges from the beach line 240 | std::list linkedVertices; 241 | std::unordered_map> vertices(mDiagram.getNbSites()); 242 | if (!mBeachline.isEmpty()) 243 | { 244 | Arc* leftArc = mBeachline.getLeftmostArc(); 245 | Arc* rightArc = leftArc->next; 246 | while (!mBeachline.isNil(rightArc)) 247 | { 248 | // Bound the edge 249 | Vector2 direction = (leftArc->site->point - rightArc->site->point).getOrthogonal(); 250 | Vector2 origin = (leftArc->site->point + rightArc->site->point) * 0.5f; 251 | // Line-box intersection 252 | Box::Intersection intersection = box.getFirstIntersection(origin, direction); 253 | // Create a new vertex and ends the half edges 254 | VoronoiDiagram::Vertex* vertex = mDiagram.createVertex(intersection.point); 255 | setDestination(leftArc, rightArc, vertex); 256 | // Initialize pointers 257 | if (vertices.find(leftArc->site->index) == vertices.end()) 258 | vertices[leftArc->site->index].fill(nullptr); 259 | if (vertices.find(rightArc->site->index) == vertices.end()) 260 | vertices[rightArc->site->index].fill(nullptr); 261 | // Store the vertex on the boundaries 262 | linkedVertices.emplace_back(LinkedVertex{nullptr, vertex, leftArc->rightHalfEdge}); 263 | vertices[leftArc->site->index][2 * static_cast(intersection.side) + 1] = &linkedVertices.back(); 264 | linkedVertices.emplace_back(LinkedVertex{rightArc->leftHalfEdge, vertex, nullptr}); 265 | vertices[rightArc->site->index][2 * static_cast(intersection.side)] = &linkedVertices.back(); 266 | // Next edge 267 | leftArc = rightArc; 268 | rightArc = rightArc->next; 269 | } 270 | } 271 | // Add corners 272 | for (auto& kv : vertices) 273 | { 274 | auto& cellVertices = kv.second; 275 | // We check twice the first side to be sure that all necessary corners are added 276 | for (std::size_t i = 0; i < 5; ++i) 277 | { 278 | std::size_t side = i % 4; 279 | std::size_t nextSide = (side + 1) % 4; 280 | // Add first corner 281 | if (cellVertices[2 * side] == nullptr && cellVertices[2 * side + 1] != nullptr) 282 | { 283 | std::size_t prevSide = (side + 3) % 4; 284 | VoronoiDiagram::Vertex* corner = mDiagram.createCorner(box, static_cast(side)); 285 | linkedVertices.emplace_back(LinkedVertex{nullptr, corner, nullptr}); 286 | cellVertices[2 * prevSide + 1] = &linkedVertices.back(); 287 | cellVertices[2 * side] = &linkedVertices.back(); 288 | } 289 | // Add second corner 290 | else if (cellVertices[2 * side] != nullptr && cellVertices[2 * side + 1] == nullptr) 291 | { 292 | VoronoiDiagram::Vertex* corner = mDiagram.createCorner(box, static_cast(nextSide)); 293 | linkedVertices.emplace_back(LinkedVertex{nullptr, corner, nullptr}); 294 | cellVertices[2 * side + 1] = &linkedVertices.back(); 295 | cellVertices[2 * nextSide] = &linkedVertices.back(); 296 | } 297 | } 298 | } 299 | // Join the half edges 300 | for (auto& kv : vertices) 301 | { 302 | std::size_t i = kv.first; 303 | auto& cellVertices = kv.second; 304 | for (std::size_t side = 0; side < 4; ++side) 305 | { 306 | if (cellVertices[2 * side] != nullptr) 307 | { 308 | // Link vertices 309 | VoronoiDiagram::HalfEdge* halfEdge = mDiagram.createHalfEdge(mDiagram.getFace(i)); 310 | halfEdge->origin = cellVertices[2 * side]->vertex; 311 | halfEdge->destination = cellVertices[2 * side + 1]->vertex; 312 | cellVertices[2 * side]->nextHalfEdge = halfEdge; 313 | halfEdge->prev = cellVertices[2 * side]->prevHalfEdge; 314 | if (cellVertices[2 * side]->prevHalfEdge != nullptr) 315 | cellVertices[2 * side]->prevHalfEdge->next = halfEdge; 316 | cellVertices[2 * side + 1]->prevHalfEdge = halfEdge; 317 | halfEdge->next = cellVertices[2 * side + 1]->nextHalfEdge; 318 | if (cellVertices[2 * side + 1]->nextHalfEdge != nullptr) 319 | cellVertices[2 * side + 1]->nextHalfEdge->prev = halfEdge; 320 | } 321 | } 322 | } 323 | return true; // TO DO: detect errors 324 | } 325 | 326 | -------------------------------------------------------------------------------- /src/FortuneAlgorithm.h: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | // My includes 21 | #include "PriorityQueue.h" 22 | #include "VoronoiDiagram.h" 23 | #include "Beachline.h" 24 | 25 | class Arc; 26 | class Event; 27 | 28 | class FortuneAlgorithm 29 | { 30 | public: 31 | 32 | FortuneAlgorithm(std::vector points); 33 | ~FortuneAlgorithm(); 34 | 35 | void construct(); 36 | bool bound(Box box); 37 | 38 | VoronoiDiagram getDiagram(); 39 | 40 | private: 41 | VoronoiDiagram mDiagram; 42 | Beachline mBeachline; 43 | PriorityQueue mEvents; 44 | double mBeachlineY; 45 | 46 | // Algorithm 47 | void handleSiteEvent(Event* event); 48 | void handleCircleEvent(Event* event); 49 | 50 | // Arcs 51 | Arc* breakArc(Arc* arc, VoronoiDiagram::Site* site); 52 | void removeArc(Arc* arc, VoronoiDiagram::Vertex* vertex); 53 | 54 | // Breakpoint 55 | bool isMovingRight(const Arc* left, const Arc* right) const; 56 | double getInitialX(const Arc* left, const Arc* right, bool movingRight) const; 57 | 58 | // Edges 59 | void addEdge(Arc* left, Arc* right); 60 | void setOrigin(Arc* left, Arc* right, VoronoiDiagram::Vertex* vertex); 61 | void setDestination(Arc* left, Arc* right, VoronoiDiagram::Vertex* vertex); 62 | void setPrevHalfEdge(VoronoiDiagram::HalfEdge* prev, VoronoiDiagram::HalfEdge* next); 63 | 64 | // Events 65 | void addEvent(Arc* left, Arc* middle, Arc* right); 66 | void deleteEvent(Arc* arc); 67 | Vector2 computeConvergencePoint(const Vector2& point1, const Vector2& point2, const Vector2& point3, double& y) const; 68 | 69 | // Bounding 70 | 71 | struct LinkedVertex 72 | { 73 | VoronoiDiagram::HalfEdge* prevHalfEdge; 74 | VoronoiDiagram::Vertex* vertex; 75 | VoronoiDiagram::HalfEdge* nextHalfEdge; 76 | }; 77 | }; 78 | 79 | -------------------------------------------------------------------------------- /src/PriorityQueue.h: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | // TO DO: Replace raw pointers by std::unique_ptr 19 | 20 | #pragma once 21 | 22 | // STL 23 | #include 24 | #include 25 | #include 26 | 27 | template 28 | class PriorityQueue 29 | { 30 | public: 31 | PriorityQueue() 32 | { 33 | 34 | } 35 | 36 | // Accessors 37 | 38 | bool isEmpty() const 39 | { 40 | return mElements.empty(); 41 | } 42 | 43 | // Operations 44 | 45 | std::unique_ptr pop() 46 | { 47 | swap(0, mElements.size() - 1); 48 | auto top = std::move(mElements.back()); 49 | mElements.pop_back(); 50 | siftDown(0); 51 | return top; 52 | } 53 | 54 | void push(std::unique_ptr elem) 55 | { 56 | elem->index = mElements.size(); 57 | mElements.emplace_back(std::move(elem)); 58 | siftUp(mElements.size() - 1); 59 | } 60 | 61 | void update(std::size_t i) 62 | { 63 | int parent = getParent(i); 64 | if(parent >= 0 && *mElements[parent] < *mElements[i]) 65 | siftUp(i); 66 | else 67 | siftDown(i); 68 | } 69 | 70 | void remove(std::size_t i) 71 | { 72 | swap(i, mElements.size() - 1); 73 | mElements.pop_back(); 74 | if (i < mElements.size()) 75 | update(i); 76 | } 77 | 78 | // Print 79 | 80 | std::ostream& print(std::ostream& os, int i = 0, std::string tabs = "") const 81 | { 82 | if(i < mElements.size()) 83 | { 84 | os << tabs << *mElements[i] << std::endl; 85 | display(getLeftChild(i), tabs + '\t'); 86 | display(getRightChild(i), tabs + '\t'); 87 | } 88 | return os; 89 | } 90 | 91 | private: 92 | std::vector> mElements; 93 | 94 | // Accessors 95 | 96 | int getParent(int i) const 97 | { 98 | return (i + 1) / 2 - 1; 99 | } 100 | 101 | std::size_t getLeftChild(std::size_t i) const 102 | { 103 | return 2 * (i + 1) - 1; 104 | } 105 | 106 | std::size_t getRightChild(std::size_t i) const 107 | { 108 | return 2 * (i + 1); 109 | } 110 | 111 | // Operations 112 | 113 | void siftDown(std::size_t i) 114 | { 115 | std::size_t left = getLeftChild(i); 116 | std::size_t right = getRightChild(i); 117 | std::size_t j = i; 118 | if(left < mElements.size() && *mElements[j] < *mElements[left]) 119 | j = left; 120 | if(right < mElements.size() && *mElements[j] < *mElements[right]) 121 | j = right; 122 | if(j != i) 123 | { 124 | swap(i, j); 125 | siftDown(j); 126 | } 127 | } 128 | 129 | void siftUp(std::size_t i) 130 | { 131 | int parent = getParent(i); 132 | if(parent >= 0 && *mElements[parent] < *mElements[i]) 133 | { 134 | swap(i, parent); 135 | siftUp(parent); 136 | } 137 | } 138 | 139 | inline void swap(std::size_t i, std::size_t j) 140 | { 141 | auto tmp = std::move(mElements[i]); 142 | mElements[i] = std::move(mElements[j]); 143 | mElements[j] = std::move(tmp); 144 | mElements[i]->index = i; 145 | mElements[j]->index = j; 146 | } 147 | }; 148 | 149 | template 150 | std::ostream& operator<<(std::ostream& os, const PriorityQueue& queue) 151 | { 152 | return queue.print(os); 153 | } 154 | -------------------------------------------------------------------------------- /src/Vector2.cpp: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "Vector2.h" 19 | // STL 20 | #include 21 | 22 | Vector2::Vector2(double x, double y) : x(x), y(y) 23 | { 24 | 25 | } 26 | 27 | // Unary operators 28 | 29 | Vector2 Vector2::operator-() const 30 | { 31 | return Vector2(-x, -y); 32 | } 33 | 34 | Vector2& Vector2::operator+=(const Vector2& other) 35 | { 36 | x += other.x; 37 | y += other.y; 38 | return *this; 39 | } 40 | 41 | Vector2& Vector2::operator-=(const Vector2& other) 42 | { 43 | x -= other.x; 44 | y -= other.y; 45 | return *this; 46 | } 47 | 48 | Vector2& Vector2::operator*=(double t) 49 | { 50 | x *= t; 51 | y *= t; 52 | return *this; 53 | } 54 | 55 | // Other operations 56 | 57 | Vector2 Vector2::getOrthogonal() const 58 | { 59 | return Vector2(-y, x); 60 | } 61 | 62 | double Vector2::dot(const Vector2& other) const 63 | { 64 | return x * other.x + y * other.y; 65 | } 66 | 67 | double Vector2::getNorm() const 68 | { 69 | return std::sqrt(x * x + y * y); 70 | } 71 | 72 | double Vector2::getDistance(const Vector2& other) const 73 | { 74 | return (*this - other).getNorm(); 75 | } 76 | 77 | double Vector2::getDet(const Vector2& other) const 78 | { 79 | return x * other.y - y * other.x; 80 | } 81 | 82 | Vector2 operator+(Vector2 lhs, const Vector2& rhs) 83 | { 84 | lhs += rhs; 85 | return lhs; 86 | } 87 | 88 | Vector2 operator-(Vector2 lhs, const Vector2& rhs) 89 | { 90 | lhs -= rhs; 91 | return lhs; 92 | } 93 | 94 | Vector2 operator*(double t, Vector2 vec) 95 | { 96 | vec *= t; 97 | return vec; 98 | } 99 | 100 | Vector2 operator*(Vector2 vec, double t) 101 | { 102 | return t * vec; 103 | } 104 | 105 | std::ostream& operator<<(std::ostream& os, const Vector2& vec) 106 | { 107 | os << "(" << vec.x << ", " << vec.y << ")"; 108 | return os; 109 | } 110 | 111 | -------------------------------------------------------------------------------- /src/Vector2.h: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | // STL 21 | #include 22 | 23 | // Declarations 24 | 25 | class Vector2; 26 | Vector2 operator-(Vector2 lhs, const Vector2& rhs); 27 | 28 | // Implementations 29 | 30 | class Vector2 31 | { 32 | public: 33 | double x; 34 | double y; 35 | 36 | Vector2(double x = 0.0, double y = 0.0); 37 | 38 | // Unary operators 39 | 40 | Vector2 operator-() const; 41 | Vector2& operator+=(const Vector2& other); 42 | Vector2& operator-=(const Vector2& other); 43 | Vector2& operator*=(double t); 44 | 45 | // Other operations 46 | 47 | Vector2 getOrthogonal() const; 48 | double dot(const Vector2& other) const; 49 | double getNorm() const; 50 | double getDistance(const Vector2& other) const; 51 | double getDet(const Vector2& other) const; 52 | }; 53 | 54 | // Binary operators 55 | 56 | Vector2 operator+(Vector2 lhs, const Vector2& rhs); 57 | Vector2 operator-(Vector2 lhs, const Vector2& rhs); 58 | Vector2 operator*(double t, Vector2 vec); 59 | Vector2 operator*(Vector2 vec, double t); 60 | std::ostream& operator<<(std::ostream& os, const Vector2& vec); 61 | 62 | -------------------------------------------------------------------------------- /src/VoronoiDiagram.cpp: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "VoronoiDiagram.h" 19 | // STL 20 | #include 21 | 22 | VoronoiDiagram::VoronoiDiagram(const std::vector& points) 23 | { 24 | mSites.reserve(points.size()); 25 | mFaces.reserve(points.size()); 26 | for(std::size_t i = 0; i < points.size(); ++i) 27 | { 28 | mSites.push_back(VoronoiDiagram::Site{i, points[i], nullptr}); 29 | mFaces.push_back(VoronoiDiagram::Face{&mSites.back(), nullptr}); 30 | mSites.back().face = &mFaces.back(); 31 | } 32 | } 33 | 34 | VoronoiDiagram::Site* VoronoiDiagram::getSite(std::size_t i) 35 | { 36 | return &mSites[i]; 37 | } 38 | 39 | std::size_t VoronoiDiagram::getNbSites() const 40 | { 41 | return mSites.size(); 42 | } 43 | 44 | VoronoiDiagram::Face* VoronoiDiagram::getFace(std::size_t i) 45 | { 46 | return &mFaces[i]; 47 | } 48 | 49 | const std::list& VoronoiDiagram::getVertices() const 50 | { 51 | return mVertices; 52 | } 53 | 54 | const std::list& VoronoiDiagram::getHalfEdges() const 55 | { 56 | return mHalfEdges; 57 | } 58 | 59 | bool VoronoiDiagram::intersect(Box box) 60 | { 61 | bool error = false; 62 | std::unordered_set processedHalfEdges; 63 | std::unordered_set verticesToRemove; 64 | for (const Site& site : mSites) 65 | { 66 | HalfEdge* halfEdge = site.face->outerComponent; 67 | bool inside = box.contains(halfEdge->origin->point); 68 | bool outerComponentDirty = !inside; 69 | HalfEdge* incomingHalfEdge = nullptr; // First half edge coming in the box 70 | HalfEdge* outgoingHalfEdge = nullptr; // Last half edge going out the box 71 | Box::Side incomingSide, outgoingSide; 72 | do 73 | { 74 | std::array intersections; 75 | int nbIntersections = box.getIntersections(halfEdge->origin->point, halfEdge->destination->point, intersections); 76 | bool nextInside = box.contains(halfEdge->destination->point); 77 | HalfEdge* nextHalfEdge = halfEdge->next; 78 | // The two points are outside the box 79 | if (!inside && !nextInside) 80 | { 81 | // The edge is outside the box 82 | if (nbIntersections == 0) 83 | { 84 | verticesToRemove.emplace(halfEdge->origin); 85 | removeHalfEdge(halfEdge); 86 | } 87 | // The edge crosses twice the frontiers of the box 88 | else if (nbIntersections == 2) 89 | { 90 | verticesToRemove.emplace(halfEdge->origin); 91 | if (processedHalfEdges.find(halfEdge->twin) != processedHalfEdges.end()) 92 | { 93 | halfEdge->origin = halfEdge->twin->destination; 94 | halfEdge->destination = halfEdge->twin->origin; 95 | } 96 | else 97 | { 98 | halfEdge->origin = createVertex(intersections[0].point); 99 | halfEdge->destination = createVertex(intersections[1].point); 100 | } 101 | if (outgoingHalfEdge != nullptr) 102 | link(box, outgoingHalfEdge, outgoingSide, halfEdge, intersections[0].side); 103 | if (incomingHalfEdge == nullptr) 104 | { 105 | incomingHalfEdge = halfEdge; 106 | incomingSide = intersections[0].side; 107 | } 108 | outgoingHalfEdge = halfEdge; 109 | outgoingSide = intersections[1].side; 110 | processedHalfEdges.emplace(halfEdge); 111 | } 112 | else 113 | error = true; 114 | } 115 | // The edge is going outside the box 116 | else if (inside && !nextInside) 117 | { 118 | if (nbIntersections == 1) 119 | { 120 | if (processedHalfEdges.find(halfEdge->twin) != processedHalfEdges.end()) 121 | halfEdge->destination = halfEdge->twin->origin; 122 | else 123 | halfEdge->destination = createVertex(intersections[0].point); 124 | outgoingHalfEdge = halfEdge; 125 | outgoingSide = intersections[0].side; 126 | processedHalfEdges.emplace(halfEdge); 127 | } 128 | else 129 | error = true; 130 | } 131 | // The edge is coming inside the box 132 | else if (!inside && nextInside) 133 | { 134 | if (nbIntersections == 1) 135 | { 136 | verticesToRemove.emplace(halfEdge->origin); 137 | if (processedHalfEdges.find(halfEdge->twin) != processedHalfEdges.end()) 138 | halfEdge->origin = halfEdge->twin->destination; 139 | else 140 | halfEdge->origin = createVertex(intersections[0].point); 141 | if (outgoingHalfEdge != nullptr) 142 | link(box, outgoingHalfEdge, outgoingSide, halfEdge, intersections[0].side); 143 | if (incomingHalfEdge == nullptr) 144 | { 145 | incomingHalfEdge = halfEdge; 146 | incomingSide = intersections[0].side; 147 | } 148 | processedHalfEdges.emplace(halfEdge); 149 | } 150 | else 151 | error = true; 152 | } 153 | halfEdge = nextHalfEdge; 154 | // Update inside 155 | inside = nextInside; 156 | } while (halfEdge != site.face->outerComponent); 157 | // Link the last and the first half edges inside the box 158 | if (outerComponentDirty && incomingHalfEdge != nullptr) 159 | link(box, outgoingHalfEdge, outgoingSide, incomingHalfEdge, incomingSide); 160 | // Set outer component 161 | if (outerComponentDirty) 162 | site.face->outerComponent = incomingHalfEdge; 163 | } 164 | // Remove vertices 165 | for (auto& vertex : verticesToRemove) 166 | removeVertex(vertex); 167 | // Return the status 168 | return !error; 169 | } 170 | 171 | VoronoiDiagram::Vertex* VoronoiDiagram::createVertex(Vector2 point) 172 | { 173 | mVertices.emplace_back(); 174 | mVertices.back().point = point; 175 | mVertices.back().it = std::prev(mVertices.end()); 176 | return &mVertices.back(); 177 | } 178 | 179 | VoronoiDiagram::Vertex* VoronoiDiagram::createCorner(Box box, Box::Side side) 180 | { 181 | switch (side) 182 | { 183 | case Box::Side::LEFT: 184 | return createVertex(Vector2(box.left, box.top)); 185 | case Box::Side::BOTTOM: 186 | return createVertex(Vector2(box.left, box.bottom)); 187 | case Box::Side::RIGHT: 188 | return createVertex(Vector2(box.right, box.bottom)); 189 | case Box::Side::TOP: 190 | return createVertex(Vector2(box.right, box.top)); 191 | default: 192 | return nullptr; 193 | } 194 | } 195 | 196 | VoronoiDiagram::HalfEdge* VoronoiDiagram::createHalfEdge(Face* face) 197 | { 198 | mHalfEdges.emplace_back(); 199 | mHalfEdges.back().incidentFace = face; 200 | mHalfEdges.back().it = std::prev(mHalfEdges.end()); 201 | if(face->outerComponent == nullptr) 202 | face->outerComponent = &mHalfEdges.back(); 203 | return &mHalfEdges.back(); 204 | } 205 | 206 | void VoronoiDiagram::link(Box box, HalfEdge* start, Box::Side startSide, HalfEdge* end, Box::Side endSide) 207 | { 208 | HalfEdge* halfEdge = start; 209 | int side = static_cast(startSide); 210 | while (side != static_cast(endSide)) 211 | { 212 | side = (side + 1) % 4; 213 | halfEdge->next = createHalfEdge(start->incidentFace); 214 | halfEdge->next->prev = halfEdge; 215 | halfEdge->next->origin = halfEdge->destination; 216 | halfEdge->next->destination = createCorner(box, static_cast(side)); 217 | halfEdge = halfEdge->next; 218 | } 219 | halfEdge->next = createHalfEdge(start->incidentFace); 220 | halfEdge->next->prev = halfEdge; 221 | end->prev = halfEdge->next; 222 | halfEdge->next->next = end; 223 | halfEdge->next->origin = halfEdge->destination; 224 | halfEdge->next->destination = end->origin; 225 | } 226 | 227 | void VoronoiDiagram::removeVertex(Vertex* vertex) 228 | { 229 | mVertices.erase(vertex->it); 230 | } 231 | 232 | void VoronoiDiagram::removeHalfEdge(HalfEdge* halfEdge) 233 | { 234 | mHalfEdges.erase(halfEdge->it); 235 | } 236 | 237 | -------------------------------------------------------------------------------- /src/VoronoiDiagram.h: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | // STL 21 | #include 22 | #include 23 | // My includes 24 | #include "Box.h" 25 | 26 | class FortuneAlgorithm; 27 | 28 | class VoronoiDiagram 29 | { 30 | public: 31 | struct HalfEdge; 32 | struct Face; 33 | 34 | struct Site 35 | { 36 | std::size_t index; 37 | Vector2 point; 38 | Face* face; 39 | }; 40 | 41 | struct Vertex 42 | { 43 | Vector2 point; 44 | 45 | private: 46 | friend VoronoiDiagram; 47 | std::list::iterator it; 48 | }; 49 | 50 | struct HalfEdge 51 | { 52 | Vertex* origin = nullptr; 53 | Vertex* destination = nullptr; 54 | HalfEdge* twin = nullptr; 55 | Face* incidentFace; 56 | HalfEdge* prev = nullptr; 57 | HalfEdge* next = nullptr; 58 | 59 | private: 60 | friend VoronoiDiagram; 61 | std::list::iterator it; 62 | }; 63 | 64 | struct Face 65 | { 66 | Site* site; 67 | HalfEdge* outerComponent; 68 | }; 69 | 70 | VoronoiDiagram(const std::vector& points); 71 | 72 | // Remove copy operations 73 | VoronoiDiagram(const VoronoiDiagram&) = delete; 74 | VoronoiDiagram& operator=(const VoronoiDiagram&) = delete; 75 | 76 | // Move operations 77 | VoronoiDiagram(VoronoiDiagram&&) = default; 78 | VoronoiDiagram& operator=(VoronoiDiagram&&) = default; 79 | 80 | // Accessors 81 | Site* getSite(std::size_t i); 82 | std::size_t getNbSites() const; 83 | Face* getFace(std::size_t i); 84 | const std::list& getVertices() const; 85 | const std::list& getHalfEdges() const; 86 | 87 | // Intersection with a box 88 | bool intersect(Box box); 89 | 90 | private: 91 | std::vector mSites; 92 | std::vector mFaces; 93 | std::list mVertices; 94 | std::list mHalfEdges; 95 | 96 | // Diagram construction 97 | friend FortuneAlgorithm; 98 | 99 | Vertex* createVertex(Vector2 point); 100 | Vertex* createCorner(Box box, Box::Side side); 101 | HalfEdge* createHalfEdge(Face* face); 102 | 103 | // Intersection with a box 104 | void link(Box box, HalfEdge* start, Box::Side startSide, HalfEdge* end, Box::Side endSide); 105 | void removeVertex(Vertex* vertex); 106 | void removeHalfEdge(HalfEdge* halfEdge); 107 | }; 108 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* FortuneAlgorithm 2 | * Copyright (C) 2018 Pierre Vigier 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | // STL 19 | #include 20 | #include 21 | #include 22 | #include 23 | // SFML 24 | #include 25 | // My includes 26 | #include "FortuneAlgorithm.h" 27 | 28 | constexpr float WINDOW_WIDTH = 600.0f; 29 | constexpr float WINDOW_HEIGHT = 600.0f; 30 | constexpr float POINT_RADIUS = 0.005f; 31 | constexpr float OFFSET = 1.0f; 32 | 33 | std::vector generatePoints(int nbPoints) 34 | { 35 | uint64_t seed = std::chrono::system_clock::now().time_since_epoch().count(); 36 | std::cout << "seed: " << seed << '\n'; 37 | std::default_random_engine generator(seed); 38 | std::uniform_real_distribution distribution (0.0, 1.0); 39 | 40 | std::vector points; 41 | for (int i = 0; i < nbPoints; ++i) 42 | points.push_back(Vector2{distribution(generator), distribution(generator)}); 43 | 44 | return points; 45 | } 46 | 47 | void drawPoint(sf::RenderWindow& window, Vector2 point, sf::Color color) 48 | { 49 | sf::CircleShape shape(POINT_RADIUS); 50 | shape.setPosition(sf::Vector2f(point.x - POINT_RADIUS, 1 - point.y - POINT_RADIUS)); 51 | shape.setFillColor(color); 52 | window.draw(shape); 53 | } 54 | 55 | void drawEdge(sf::RenderWindow& window, Vector2 origin, Vector2 destination, sf::Color color) 56 | { 57 | sf::Vertex line[] = 58 | { 59 | sf::Vertex(sf::Vector2f(origin.x, 1.0f - origin.y), color), 60 | sf::Vertex(sf::Vector2f(destination.x, 1.0f - destination.y), color) 61 | }; 62 | window.draw(line, 2, sf::Lines); 63 | } 64 | 65 | void drawPoints(sf::RenderWindow& window, VoronoiDiagram& diagram) 66 | { 67 | for (std::size_t i = 0; i < diagram.getNbSites(); ++i) 68 | drawPoint(window, diagram.getSite(i)->point, sf::Color(100, 250, 50)); 69 | } 70 | 71 | void drawDiagram(sf::RenderWindow& window, VoronoiDiagram& diagram) 72 | { 73 | for (std::size_t i = 0; i < diagram.getNbSites(); ++i) 74 | { 75 | const VoronoiDiagram::Site* site = diagram.getSite(i); 76 | Vector2 center = site->point; 77 | VoronoiDiagram::Face* face = site->face; 78 | VoronoiDiagram::HalfEdge* halfEdge = face->outerComponent; 79 | if (halfEdge == nullptr) 80 | continue; 81 | while (halfEdge->prev != nullptr) 82 | { 83 | halfEdge = halfEdge->prev; 84 | if (halfEdge == face->outerComponent) 85 | break; 86 | } 87 | VoronoiDiagram::HalfEdge* start = halfEdge; 88 | while (halfEdge != nullptr) 89 | { 90 | if (halfEdge->origin != nullptr && halfEdge->destination != nullptr) 91 | { 92 | Vector2 origin = (halfEdge->origin->point - center) * OFFSET + center; 93 | Vector2 destination = (halfEdge->destination->point - center) * OFFSET + center; 94 | drawEdge(window, origin, destination, sf::Color::Red); 95 | } 96 | halfEdge = halfEdge->next; 97 | if (halfEdge == start) 98 | break; 99 | } 100 | } 101 | } 102 | 103 | VoronoiDiagram generateRandomDiagram(std::size_t nbPoints) 104 | { 105 | // Generate points 106 | std::vector points = generatePoints(nbPoints); 107 | 108 | // Construct diagram 109 | FortuneAlgorithm algorithm(points); 110 | auto start = std::chrono::steady_clock::now(); 111 | algorithm.construct(); 112 | auto duration = std::chrono::steady_clock::now() - start; 113 | std::cout << "construction: " << std::chrono::duration_cast(duration).count() << "ms" << '\n'; 114 | 115 | // Bound the diagram 116 | start = std::chrono::steady_clock::now(); 117 | algorithm.bound(Box{-0.05, -0.05, 1.05, 1.05}); // Take the bounding box slightly bigger than the intersection box 118 | duration = std::chrono::steady_clock::now() - start; 119 | std::cout << "bounding: " << std::chrono::duration_cast(duration).count() << "ms" << '\n'; 120 | VoronoiDiagram diagram = algorithm.getDiagram(); 121 | 122 | // Intersect the diagram with a box 123 | start = std::chrono::steady_clock::now(); 124 | bool valid = diagram.intersect(Box{0.0, 0.0, 1.0, 1.0}); 125 | duration = std::chrono::steady_clock::now() - start; 126 | std::cout << "intersection: " << std::chrono::duration_cast(duration).count() << "ms" << '\n'; 127 | if (!valid) 128 | throw std::runtime_error("An error occured in the box intersection algorithm"); 129 | 130 | return diagram; 131 | } 132 | 133 | int main() 134 | { 135 | std::size_t nbPoints = 100; 136 | VoronoiDiagram diagram = generateRandomDiagram(nbPoints); 137 | 138 | // Display the diagram 139 | sf::ContextSettings settings; 140 | settings.antialiasingLevel = 8; 141 | sf::RenderWindow window(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Fortune's algorithm", sf::Style::Default, settings); 142 | window.setView(sf::View(sf::FloatRect(-0.1f, -0.1f, 1.2f, 1.2f))); 143 | 144 | while (window.isOpen()) 145 | { 146 | sf::Event event; 147 | while (window.pollEvent(event)) 148 | { 149 | if (event.type == sf::Event::Closed) 150 | window.close(); 151 | else if (event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::Key::N) 152 | diagram = generateRandomDiagram(nbPoints); 153 | } 154 | 155 | window.clear(sf::Color::Black); 156 | 157 | drawDiagram(window, diagram); 158 | drawPoints(window, diagram); 159 | 160 | window.display(); 161 | } 162 | 163 | return 0; 164 | } 165 | --------------------------------------------------------------------------------