├── .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 | 
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 |
--------------------------------------------------------------------------------