├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── FindEigen.cmake ├── example ├── matrix.txt └── sphere.obj ├── res └── teaser.png └── src ├── arrangement.cc ├── arrangement.h ├── camera.cc ├── camera.h ├── divide_conquer_construct.cc ├── divide_conquer_construct.h ├── main.cc ├── mesh.cc ├── mesh.h ├── plane_param.cc ├── plane_param.h ├── postprocess.cc ├── postprocess.h ├── simple_svg.h ├── split_data.cc ├── split_data.h └── types.h /.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 | *.DS_Store 34 | # Build 35 | build/* 36 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(VectorRender LANGUAGES CXX) 3 | 4 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") 5 | set(Boost_USE_STATIC_LIBS ON) 6 | find_package(Eigen REQUIRED) 7 | find_package(Boost REQUIRED) 8 | find_package(CGAL REQUIRED) 9 | 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | set(CMAKE_CXX_STANDARD 14) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 14 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") 15 | set(CMAKE_CXX_FLAGS_RELEASE "-O3") # enable assert 16 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g") # enable assert 17 | set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS}") 18 | set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG}") 19 | 20 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 21 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fopenmp -Wno-int-in-bool-context -Wno-sign-compare") 22 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address") 23 | set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS}") 24 | set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address") 25 | endif() 26 | 27 | if(MSVC) 28 | ADD_DEFINITIONS(-D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS) 29 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /wd4267 /wd4244 /wd4018 /wd4800") 30 | endif() 31 | 32 | include(${CGAL_USE_FILE}) 33 | include_directories(${Boost_INCLUDE_DIRS}) 34 | include_directories(${EIGEN_INCLUDE_DIRS}) 35 | include_directories(${CGAL_INCLUDE_DIRS}) 36 | 37 | set( 38 | VectorRender_SRC 39 | src/main.cc 40 | src/types.h 41 | src/arrangement.cc 42 | src/arrangement.h 43 | src/camera.cc 44 | src/camera.h 45 | src/divide_conquer_construct.cc 46 | src/divide_conquer_construct.h 47 | src/mesh.cc 48 | src/mesh.h 49 | src/plane_param.cc 50 | src/plane_param.h 51 | src/postprocess.cc 52 | src/postprocess.h 53 | src/simple_svg.h 54 | src/split_data.cc 55 | src/split_data.h 56 | ) 57 | 58 | add_executable( 59 | vector_render 60 | ${VectorRender_SRC} 61 | ) 62 | 63 | target_link_libraries( 64 | vector_render 65 | ${CGAL_LIBRARIES} 66 | ) 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jingwei Huang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VectorGraphRenderer 2 | A renderer that takes a triangle mesh, a camera pose and produce a vector graph. 3 | 4 | ![VectorGraphRender Teaser](https://github.com/hjwdzh/VectorGraphRenderer/raw/master/res/teaser.png) 5 | 6 | ### Compile 7 | ``` 8 | mkdir build 9 | cmake .. -DCMAKE_BUILD_TYPE=Release 10 | make -j8 11 | ``` 12 | 13 | ### Run example 14 | Run binary with three arguments. The first is the input mesh, the second the camera parameters, and the third the output file. 15 | 16 | If the output file ends with svg, we produce a svg image file. If the output file ends with obj, we produce a obj mesh output. 17 | 18 | Example of running the script: 19 | ``` 20 | ./vector_render ../example/sphere.obj ../example/matrix.txt result.svg 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /cmake/FindEigen.cmake: -------------------------------------------------------------------------------- 1 | # Ceres Solver - A fast non-linear least squares minimizer 2 | # Copyright 2015 Google Inc. All rights reserved. 3 | # http://ceres-solver.org/ 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # * Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # * Neither the name of Google Inc. nor the names of its contributors may be 14 | # used to endorse or promote products derived from this software without 15 | # specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | # POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | # Author: alexs.mac@gmail.com (Alex Stewart) 30 | # 31 | 32 | # FindEigen.cmake - Find Eigen library, version >= 3. 33 | # 34 | # This module defines the following variables: 35 | # 36 | # EIGEN_FOUND: TRUE iff Eigen is found. 37 | # EIGEN_INCLUDE_DIRS: Include directories for Eigen. 38 | # EIGEN_VERSION: Extracted from Eigen/src/Core/util/Macros.h 39 | # EIGEN_WORLD_VERSION: Equal to 3 if EIGEN_VERSION = 3.2.0 40 | # EIGEN_MAJOR_VERSION: Equal to 2 if EIGEN_VERSION = 3.2.0 41 | # EIGEN_MINOR_VERSION: Equal to 0 if EIGEN_VERSION = 3.2.0 42 | # FOUND_INSTALLED_EIGEN_CMAKE_CONFIGURATION: True iff the version of Eigen 43 | # found was built & installed / 44 | # exported as a CMake package. 45 | # 46 | # The following variables control the behaviour of this module: 47 | # 48 | # EIGEN_PREFER_EXPORTED_EIGEN_CMAKE_CONFIGURATION: TRUE/FALSE, iff TRUE then 49 | # then prefer using an exported CMake configuration 50 | # generated by Eigen over searching for the 51 | # Eigen components manually. Otherwise (FALSE) 52 | # ignore any exported Eigen CMake configurations and 53 | # always perform a manual search for the components. 54 | # Default: TRUE iff user does not define this variable 55 | # before we are called, and does NOT specify 56 | # EIGEN_INCLUDE_DIR_HINTS, otherwise FALSE. 57 | # EIGEN_INCLUDE_DIR_HINTS: List of additional directories in which to 58 | # search for eigen includes, e.g: /timbuktu/eigen3. 59 | # 60 | # The following variables are also defined by this module, but in line with 61 | # CMake recommended FindPackage() module style should NOT be referenced directly 62 | # by callers (use the plural variables detailed above instead). These variables 63 | # do however affect the behaviour of the module via FIND_[PATH/LIBRARY]() which 64 | # are NOT re-called (i.e. search for library is not repeated) if these variables 65 | # are set with valid values _in the CMake cache_. This means that if these 66 | # variables are set directly in the cache, either by the user in the CMake GUI, 67 | # or by the user passing -DVAR=VALUE directives to CMake when called (which 68 | # explicitly defines a cache variable), then they will be used verbatim, 69 | # bypassing the HINTS variables and other hard-coded search locations. 70 | # 71 | # EIGEN_INCLUDE_DIR: Include directory for CXSparse, not including the 72 | # include directory of any dependencies. 73 | 74 | # Called if we failed to find Eigen or any of it's required dependencies, 75 | # unsets all public (designed to be used externally) variables and reports 76 | # error message at priority depending upon [REQUIRED/QUIET/] argument. 77 | macro(EIGEN_REPORT_NOT_FOUND REASON_MSG) 78 | unset(EIGEN_FOUND) 79 | unset(EIGEN_INCLUDE_DIRS) 80 | unset(FOUND_INSTALLED_EIGEN_CMAKE_CONFIGURATION) 81 | # Make results of search visible in the CMake GUI if Eigen has not 82 | # been found so that user does not have to toggle to advanced view. 83 | mark_as_advanced(CLEAR EIGEN_INCLUDE_DIR) 84 | # Note _FIND_[REQUIRED/QUIETLY] variables defined by FindPackage() 85 | # use the camelcase library name, not uppercase. 86 | if (Eigen_FIND_QUIETLY) 87 | message(STATUS "Failed to find Eigen - " ${REASON_MSG} ${ARGN}) 88 | elseif (Eigen_FIND_REQUIRED) 89 | message(FATAL_ERROR "Failed to find Eigen - " ${REASON_MSG} ${ARGN}) 90 | else() 91 | # Neither QUIETLY nor REQUIRED, use no priority which emits a message 92 | # but continues configuration and allows generation. 93 | message("-- Failed to find Eigen - " ${REASON_MSG} ${ARGN}) 94 | endif () 95 | return() 96 | endmacro(EIGEN_REPORT_NOT_FOUND) 97 | 98 | # Protect against any alternative find_package scripts for this library having 99 | # been called previously (in a client project) which set EIGEN_FOUND, but not 100 | # the other variables we require / set here which could cause the search logic 101 | # here to fail. 102 | unset(EIGEN_FOUND) 103 | 104 | # ----------------------------------------------------------------- 105 | # By default, if the user has expressed no preference for using an exported 106 | # Eigen CMake configuration over performing a search for the installed 107 | # components, and has not specified any hints for the search locations, then 108 | # prefer an exported configuration if available. 109 | if (NOT DEFINED EIGEN_PREFER_EXPORTED_EIGEN_CMAKE_CONFIGURATION 110 | AND NOT EIGEN_INCLUDE_DIR_HINTS) 111 | message(STATUS "No preference for use of exported Eigen CMake configuration " 112 | "set, and no hints for include directory provided. " 113 | "Defaulting to preferring an installed/exported Eigen CMake configuration " 114 | "if available.") 115 | set(EIGEN_PREFER_EXPORTED_EIGEN_CMAKE_CONFIGURATION TRUE) 116 | endif() 117 | 118 | if (EIGEN_PREFER_EXPORTED_EIGEN_CMAKE_CONFIGURATION) 119 | # Try to find an exported CMake configuration for Eigen. 120 | # 121 | # We search twice, s/t we can invert the ordering of precedence used by 122 | # find_package() for exported package build directories, and installed 123 | # packages (found via CMAKE_SYSTEM_PREFIX_PATH), listed as items 6) and 7) 124 | # respectively in [1]. 125 | # 126 | # By default, exported build directories are (in theory) detected first, and 127 | # this is usually the case on Windows. However, on OS X & Linux, the install 128 | # path (/usr/local) is typically present in the PATH environment variable 129 | # which is checked in item 4) in [1] (i.e. before both of the above, unless 130 | # NO_SYSTEM_ENVIRONMENT_PATH is passed). As such on those OSs installed 131 | # packages are usually detected in preference to exported package build 132 | # directories. 133 | # 134 | # To ensure a more consistent response across all OSs, and as users usually 135 | # want to prefer an installed version of a package over a locally built one 136 | # where both exist (esp. as the exported build directory might be removed 137 | # after installation), we first search with NO_CMAKE_PACKAGE_REGISTRY which 138 | # means any build directories exported by the user are ignored, and thus 139 | # installed directories are preferred. If this fails to find the package 140 | # we then research again, but without NO_CMAKE_PACKAGE_REGISTRY, so any 141 | # exported build directories will now be detected. 142 | # 143 | # To prevent confusion on Windows, we also pass NO_CMAKE_BUILDS_PATH (which 144 | # is item 5) in [1]), to not preferentially use projects that were built 145 | # recently with the CMake GUI to ensure that we always prefer an installed 146 | # version if available. 147 | # 148 | # [1] http://www.cmake.org/cmake/help/v2.8.11/cmake.html#command:find_package 149 | find_package(Eigen3 QUIET 150 | NO_MODULE 151 | NO_CMAKE_PACKAGE_REGISTRY 152 | NO_CMAKE_BUILDS_PATH) 153 | if (EIGEN3_FOUND) 154 | message(STATUS "Found installed version of Eigen: ${Eigen3_DIR}") 155 | else() 156 | # Failed to find an installed version of Eigen, repeat search allowing 157 | # exported build directories. 158 | message(STATUS "Failed to find installed Eigen CMake configuration, " 159 | "searching for Eigen build directories exported with CMake.") 160 | # Again pass NO_CMAKE_BUILDS_PATH, as we know that Eigen is exported and 161 | # do not want to treat projects built with the CMake GUI preferentially. 162 | find_package(Eigen3 QUIET 163 | NO_MODULE 164 | NO_CMAKE_BUILDS_PATH) 165 | if (EIGEN3_FOUND) 166 | message(STATUS "Found exported Eigen build directory: ${Eigen3_DIR}") 167 | endif() 168 | endif() 169 | if (EIGEN3_FOUND) 170 | set(FOUND_INSTALLED_EIGEN_CMAKE_CONFIGURATION TRUE) 171 | set(EIGEN_FOUND ${EIGEN3_FOUND}) 172 | set(EIGEN_INCLUDE_DIR "${EIGEN3_INCLUDE_DIR}" CACHE STRING 173 | "Eigen include directory" FORCE) 174 | else() 175 | message(STATUS "Failed to find an installed/exported CMake configuration " 176 | "for Eigen, will perform search for installed Eigen components.") 177 | endif() 178 | endif() 179 | 180 | if (NOT EIGEN_FOUND) 181 | # Search user-installed locations first, so that we prefer user installs 182 | # to system installs where both exist. 183 | list(APPEND EIGEN_CHECK_INCLUDE_DIRS 184 | /usr/local/include 185 | /usr/local/homebrew/include # Mac OS X 186 | /opt/local/var/macports/software # Mac OS X. 187 | /opt/local/include 188 | /usr/include) 189 | # Additional suffixes to try appending to each search path. 190 | list(APPEND EIGEN_CHECK_PATH_SUFFIXES 191 | eigen3 # Default root directory for Eigen. 192 | Eigen/include/eigen3 # Windows (for C:/Program Files prefix) < 3.3 193 | Eigen3/include/eigen3 ) # Windows (for C:/Program Files prefix) >= 3.3 194 | 195 | # Search supplied hint directories first if supplied. 196 | find_path(EIGEN_INCLUDE_DIR 197 | NAMES Eigen/Core 198 | HINTS ${EIGEN_INCLUDE_DIR_HINTS} 199 | PATHS ${EIGEN_CHECK_INCLUDE_DIRS} 200 | PATH_SUFFIXES ${EIGEN_CHECK_PATH_SUFFIXES}) 201 | 202 | if (NOT EIGEN_INCLUDE_DIR OR 203 | NOT EXISTS ${EIGEN_INCLUDE_DIR}) 204 | eigen_report_not_found( 205 | "Could not find eigen3 include directory, set EIGEN_INCLUDE_DIR to " 206 | "path to eigen3 include directory, e.g. /usr/local/include/eigen3.") 207 | endif (NOT EIGEN_INCLUDE_DIR OR 208 | NOT EXISTS ${EIGEN_INCLUDE_DIR}) 209 | 210 | # Mark internally as found, then verify. EIGEN_REPORT_NOT_FOUND() unsets 211 | # if called. 212 | set(EIGEN_FOUND TRUE) 213 | endif() 214 | 215 | # Extract Eigen version from Eigen/src/Core/util/Macros.h 216 | if (EIGEN_INCLUDE_DIR) 217 | set(EIGEN_VERSION_FILE ${EIGEN_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h) 218 | if (NOT EXISTS ${EIGEN_VERSION_FILE}) 219 | eigen_report_not_found( 220 | "Could not find file: ${EIGEN_VERSION_FILE} " 221 | "containing version information in Eigen install located at: " 222 | "${EIGEN_INCLUDE_DIR}.") 223 | else (NOT EXISTS ${EIGEN_VERSION_FILE}) 224 | file(READ ${EIGEN_VERSION_FILE} EIGEN_VERSION_FILE_CONTENTS) 225 | 226 | string(REGEX MATCH "#define EIGEN_WORLD_VERSION [0-9]+" 227 | EIGEN_WORLD_VERSION "${EIGEN_VERSION_FILE_CONTENTS}") 228 | string(REGEX REPLACE "#define EIGEN_WORLD_VERSION ([0-9]+)" "\\1" 229 | EIGEN_WORLD_VERSION "${EIGEN_WORLD_VERSION}") 230 | 231 | string(REGEX MATCH "#define EIGEN_MAJOR_VERSION [0-9]+" 232 | EIGEN_MAJOR_VERSION "${EIGEN_VERSION_FILE_CONTENTS}") 233 | string(REGEX REPLACE "#define EIGEN_MAJOR_VERSION ([0-9]+)" "\\1" 234 | EIGEN_MAJOR_VERSION "${EIGEN_MAJOR_VERSION}") 235 | 236 | string(REGEX MATCH "#define EIGEN_MINOR_VERSION [0-9]+" 237 | EIGEN_MINOR_VERSION "${EIGEN_VERSION_FILE_CONTENTS}") 238 | string(REGEX REPLACE "#define EIGEN_MINOR_VERSION ([0-9]+)" "\\1" 239 | EIGEN_MINOR_VERSION "${EIGEN_MINOR_VERSION}") 240 | 241 | # This is on a single line s/t CMake does not interpret it as a list of 242 | # elements and insert ';' separators which would result in 3.;2.;0 nonsense. 243 | set(EIGEN_VERSION "${EIGEN_WORLD_VERSION}.${EIGEN_MAJOR_VERSION}.${EIGEN_MINOR_VERSION}") 244 | endif (NOT EXISTS ${EIGEN_VERSION_FILE}) 245 | endif (EIGEN_INCLUDE_DIR) 246 | 247 | # Set standard CMake FindPackage variables if found. 248 | if (EIGEN_FOUND) 249 | set(EIGEN_INCLUDE_DIRS ${EIGEN_INCLUDE_DIR}) 250 | endif (EIGEN_FOUND) 251 | 252 | # Handle REQUIRED / QUIET optional arguments and version. 253 | include(FindPackageHandleStandardArgs) 254 | find_package_handle_standard_args(Eigen 255 | REQUIRED_VARS EIGEN_INCLUDE_DIRS 256 | VERSION_VAR EIGEN_VERSION) 257 | 258 | # Only mark internal variables as advanced if we found Eigen, otherwise 259 | # leave it visible in the standard GUI for the user to set manually. 260 | if (EIGEN_FOUND) 261 | mark_as_advanced(FORCE EIGEN_INCLUDE_DIR 262 | Eigen3_DIR) # Autogenerated by find_package(Eigen3) 263 | endif (EIGEN_FOUND) 264 | -------------------------------------------------------------------------------- /example/matrix.txt: -------------------------------------------------------------------------------- 1 | 480 640 2 | 250.0 250.0 319.5 239.5 3 | 4 | 0.289032 -0.957319 0.000000 0.730949 5 | 0.240502 0.072612 -0.967929 0.510747 6 | 0.926617 0.279762 0.251225 1.849168 7 | 0.000000 0.000000 0.000000 1.000000 8 | 9 | 30.0 -------------------------------------------------------------------------------- /res/teaser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hjwdzh/VectorGraphRenderer/4af5a683fb1414f32101be22924a809db08d7cb5/res/teaser.png -------------------------------------------------------------------------------- /src/arrangement.cc: -------------------------------------------------------------------------------- 1 | #include "arrangement.h" 2 | 3 | #include 4 | 5 | void ComputeTriangleArrangement(const Mesh& mesh, int fid, Arrangement_2* pout) { 6 | *pout = Arrangement_2(); 7 | auto& out = *pout; 8 | 9 | auto& vertices = mesh.GetVertices(); 10 | auto& faces = mesh.GetFaces(); 11 | auto f = faces[fid]; 12 | 13 | const Eigen::Vector3d& v1 = vertices[f[0]]; 14 | const Eigen::Vector3d& v2 = vertices[f[1]]; 15 | const Eigen::Vector3d& v3 = vertices[f[2]]; 16 | 17 | auto off1 = v2 - v1; 18 | auto off2 = v3 - v1; 19 | double t = off1.x() * off2.y() - off1.y() * off2.x(); 20 | if (std::abs(t) < 1e-15) { 21 | for (auto fit = out.faces_begin(); fit != out.faces_end(); ++fit) 22 | fit->set_data(0); 23 | return; 24 | } 25 | 26 | for (int i = 0; i < 3; ++i) { 27 | auto v1 = vertices[f[i]]; 28 | auto v2 = vertices[f[(i + 1) % 3]]; 29 | Segment_2 s1 (Point_2(v1.x(), v1.y()), Point_2(v2.x(), v2.y())); 30 | insert_non_intersecting_curve (out, s1); 31 | } 32 | 33 | for (auto fit = out.faces_begin(); fit != out.faces_end(); ++fit) 34 | fit->set_data ((fid + 1) * (fit != out.unbounded_face())); 35 | } 36 | 37 | 38 | void MergeFaceInsideArrangement(Arrangement_2& out) { 39 | 40 | // Detect half edges with same original face ID and Remove them 41 | std::unordered_set halfedges; 42 | for (auto e = out.halfedges_begin(); e != out.halfedges_end(); ++e) { 43 | if (halfedges.count(e) || halfedges.count(e->twin())) 44 | continue; 45 | if (e->face() == out.unbounded_face() || e->twin()->face() == out.unbounded_face()) 46 | continue; 47 | if (e->face()->data() == e->twin()->face()->data()) { 48 | halfedges.insert(e); 49 | } 50 | } 51 | 52 | for (auto e : halfedges) { 53 | out.remove_edge(e); 54 | } 55 | 56 | // Remove isolated or redundant vertices 57 | for (auto it = out.vertices_begin(); it != out.vertices_end(); ++it) { 58 | if (it->is_isolated()) { 59 | remove_vertex(out, it); 60 | } 61 | else { 62 | auto p1 = it->point(); 63 | 64 | auto e1 = it->incident_halfedges(); 65 | auto e2 = e1; 66 | int incidents = 0; 67 | do { 68 | incidents += 1; 69 | if (incidents > 2) 70 | break; 71 | e2++; 72 | } while (e1 != e2); 73 | if (incidents == 2) { 74 | e2++; 75 | auto& p1 = it->point(); 76 | auto& p2 = e1->source()->point(); 77 | auto& p3 = e2->source()->point(); 78 | 79 | auto diff1 = p2 - p1; 80 | auto diff2 = p3 - p2; 81 | if (diff1.x() * diff2.y() - diff2.x() * diff1.y() == 0) { 82 | remove_vertex(out, it); 83 | } 84 | } 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /src/arrangement.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTORGRAPH_RENDER_ARRANGEMENT_H_ 2 | #define VECTORGRAPH_RENDER_ARRANGEMENT_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "mesh.h" 9 | #include "types.h" 10 | 11 | void ComputeTriangleArrangement(const Mesh& mesh, int fid, Arrangement_2* pout); 12 | 13 | void MergeFaceInsideArrangement(Arrangement_2& out); 14 | 15 | #endif -------------------------------------------------------------------------------- /src/camera.cc: -------------------------------------------------------------------------------- 1 | #include "camera.h" 2 | 3 | #include 4 | 5 | #include "mesh.h" 6 | 7 | Camera::Camera() 8 | {} 9 | 10 | void Camera::LoadFromFile(const char* filename) { 11 | std::ifstream is(filename); 12 | is >> height_ >> width_; 13 | is >> fx_ >> fy_ >> cx_ >> cy_; 14 | 15 | for (int i = 0; i < 4; ++i) { 16 | for (int j = 0; j < 4; ++j) { 17 | is >> world2cam_(i, j); 18 | } 19 | } 20 | is >> angle_; 21 | } 22 | 23 | void Camera::ApplyExtrinsic(Mesh& mesh) { 24 | auto& vertices = mesh.GetVertices(); 25 | 26 | // apply extrinsic transformation 27 | for (int i = 0; i < vertices.size(); ++i) { 28 | Eigen::Vector4d v(vertices[i][0], vertices[i][1], vertices[i][2], 1); 29 | v = world2cam_ * v; 30 | vertices[i] = Eigen::Vector3d(v[0],v[1],v[2]); 31 | } 32 | } 33 | 34 | void Camera::ApplyIntrinsic(Mesh& mesh) { 35 | auto& vertices = mesh.GetVertices(); 36 | 37 | // apply intrinsic projection 38 | for (int i = 0; i < vertices.size(); ++i) { 39 | Eigen::Vector3d v = vertices[i]; 40 | if (v[2] != 0) { 41 | vertices[i] = Eigen::Vector3d((v[0]/v[2]*fx_+cx_)/width_ - 0.5, 42 | (v[1]/v[2]*fy_+cy_)/height_-0.5,v[2]); 43 | } else { 44 | vertices[i] = Eigen::Vector3d(0, 0, 0); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/camera.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTORGRAPH_RENDERER_CAMERA_H_ 2 | #define VECTORGRAPH_RENDERER_CAMERA_H_ 3 | 4 | #include 5 | class Mesh; 6 | class Camera 7 | { 8 | public: 9 | Camera(); 10 | void LoadFromFile(const char* filename); 11 | void ApplyExtrinsic(Mesh& mesh); 12 | void ApplyIntrinsic(Mesh& mesh); 13 | 14 | double GetAngle() const { 15 | return angle_; 16 | } 17 | void Undistort(Eigen::Vector3d& v) const { 18 | v[0] = ((v[0]+0.5) * width_ - cx_) / fx_ * v[2]; 19 | v[1] = ((v[1]+0.5) * height_ - cy_) / fy_ * v[2]; 20 | } 21 | private: 22 | float fx_, fy_, cx_, cy_; 23 | int width_, height_; 24 | Eigen::Matrix4d world2cam_; 25 | 26 | double angle_; 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/divide_conquer_construct.cc: -------------------------------------------------------------------------------- 1 | #include "divide_conquer_construct.h" 2 | 3 | #include 4 | 5 | #include "arrangement.h" 6 | #include "split_data.h" 7 | 8 | void ConstructArrangement(const Mesh& mesh, 9 | int start, int end, Arrangement_2* overlay) { 10 | printf("\r<%d %d> ", start, end); 11 | fflush(stdout); 12 | if (start == end) { 13 | ComputeTriangleArrangement(mesh, start, overlay); 14 | } 15 | else { 16 | //printf("<%d %d>\n", start, end); 17 | Arrangement_2 overlay1, overlay2; 18 | int m = (start + end) / 2; 19 | ConstructArrangement(mesh, start, m, &overlay1); 20 | ConstructArrangement(mesh, m + 1, end, &overlay2); 21 | MergeArrangement(overlay1, overlay2, mesh, overlay); 22 | } 23 | printf("\rfinish <%d %d> ", start, end); 24 | fflush(stdout); 25 | } 26 | 27 | void MergeArrangement(const Arrangement_2& arr1, const Arrangement_2& arr2, 28 | const Mesh& mesh, 29 | Arrangement_2* pout) { 30 | 31 | Overlay_traits overlay_traits; 32 | overlay (arr1, arr2, *pout, overlay_traits); 33 | 34 | auto& out = *pout; 35 | auto& plane_params = mesh.GetPlanes(); 36 | 37 | std::vector > splits; 38 | std::unordered_set halfedges; 39 | for (auto e = out.halfedges_begin(); e != out.halfedges_end(); ++e) { 40 | halfedges.insert(e); 41 | } 42 | 43 | int count0 = 0, count1 = 0; 44 | for (auto fit = out.faces_begin(); fit != out.faces_end(); ++fit) { 45 | if (fit == out.unbounded_face()) 46 | continue; 47 | long long seg = 1 << 16; 48 | seg *= seg; 49 | int id1 = fit->data() / seg - 1; 50 | int id2 = fit->data() % seg - 1; 51 | count0 += 1; 52 | if (id1 != -1 && id2 != -1) { 53 | count1 += 1; 54 | std::vector split_points; 55 | 56 | CollectSelfIntersection((Arrangement_2::Ccb_halfedge_circulator)fit->outer_ccb(), id1, id2, halfedges, plane_params, &split_points); 57 | for (auto hi = fit->holes_begin(); hi != fit->holes_end(); ++hi) { 58 | Arrangement_2::Ccb_halfedge_circulator circ = *hi; 59 | CollectSelfIntersection(circ, id1, id2, halfedges, plane_params, &split_points); 60 | } 61 | 62 | if (split_points.size() != 0) { 63 | SortSplitPoint(&split_points); 64 | splits.push_back(split_points); 65 | } 66 | } 67 | } 68 | 69 | SubdivideAtIntersection(splits, &out); 70 | 71 | RelabelFaceFromArrangement(plane_params, &out); 72 | 73 | MergeFaceInsideArrangement(out); 74 | } 75 | 76 | void RelabelFaceFromArrangement(const std::vector& plane_params, Arrangement_2* pout) { 77 | auto& out = *pout; 78 | int count = 0; 79 | for (auto fit = out.faces_begin(); fit != out.faces_end(); ++fit) { 80 | if (fit == out.unbounded_face()) 81 | continue; 82 | long long seg = 1 << 16; 83 | seg *= seg; 84 | int id1 = fit->data() / seg - 1; 85 | int id2 = fit->data() % seg - 1; 86 | if (id1 == -1) { 87 | fit->set_data((id2 + 1)); 88 | } 89 | else if (id2 == -1) { 90 | fit->set_data((id1 + 1)); 91 | } 92 | else { 93 | count += 1; 94 | Arrangement_2::Ccb_halfedge_circulator curr = fit->outer_ccb(); 95 | int selected = -1; 96 | do { 97 | Arrangement_2::Halfedge_handle he = curr; 98 | auto z_src1 = ComputeDepth(plane_params[id1], he, 1); 99 | auto z_src2 = ComputeDepth(plane_params[id2], he, 1); 100 | z_src1 -= z_src2; 101 | if (z_src1 < 0) { 102 | selected = 0; 103 | break; 104 | } 105 | if (z_src1 > 0) { 106 | selected = 1; 107 | break; 108 | } 109 | } while (++curr != fit->outer_ccb()); 110 | 111 | if (selected == 0) 112 | fit->set_data(id1 + 1); 113 | else 114 | fit->set_data(id2 + 1); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/divide_conquer_construct.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTORGRAPH_RENDER_DIVIDE_CONQUER_CONSTRUCT_H_ 2 | #define VECTORGRAPH_RENDER_DIVIDE_CONQUER_CONSTRUCT_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "types.h" 9 | #include "mesh.h" 10 | #include "plane_param.h" 11 | #include "split_data.h" 12 | 13 | void ConstructArrangement(const Mesh& mesh, 14 | int start, int end, Arrangement_2* overlay); 15 | 16 | void MergeArrangement(const Arrangement_2& arr1, const Arrangement_2& arr2, 17 | const Mesh& mesh, 18 | Arrangement_2* pout); 19 | 20 | void RelabelFaceFromArrangement(const std::vector& plane_params, Arrangement_2* pout); 21 | 22 | #endif -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "arrangement.h" 16 | #include "camera.h" 17 | #include "mesh.h" 18 | #include "divide_conquer_construct.h" 19 | #include "plane_param.h" 20 | #include "postprocess.h" 21 | 22 | int main (int argc, char** argv) 23 | { 24 | Mesh mesh; 25 | Camera camera; 26 | mesh.LoadFromFile(argv[1]); 27 | camera.LoadFromFile(argv[2]); 28 | camera.ApplyExtrinsic(mesh); 29 | 30 | mesh.BoundaryClip(2, 1e-2, 0, false); 31 | camera.ApplyIntrinsic(mesh); 32 | 33 | mesh.BoundaryClip(0, -0.5, 0, true); 34 | mesh.BoundaryClip(1, -0.5, 0, true); 35 | mesh.BoundaryClip(0, 0.5, 1, true); 36 | mesh.BoundaryClip(1, 0.5, 1, true); 37 | 38 | 39 | mesh.ComputeNormals(); 40 | mesh.ComputePlaneParameters(); 41 | 42 | Arrangement_2 overlay; 43 | 44 | ConstructArrangement(mesh, 0, mesh.FaceNum() - 1, &overlay); 45 | //ConstructArrangement(mesh, 0, 1000, &overlay); 46 | printf("\n"); 47 | 48 | //exit(0); 49 | PostProcess process; 50 | process.CollectFaceAndVertices(mesh, overlay, camera.GetAngle()); 51 | 52 | // remove 2-degree vertex on edge 53 | process.RemoveRedundantVertices(); 54 | 55 | // merge close vertices 56 | //process.MergeDuplex(mesh); 57 | 58 | //process.CollectEdges(mesh); 59 | 60 | //recognize faces 61 | int l = strlen(argv[3]); 62 | if (l > 3 && argv[3][l - 1] == 'g' && argv[3][l - 2] == 'v' && argv[3][l - 3] == 's') 63 | process.SaveToSVG(mesh, argv[3]); 64 | else 65 | process.SaveToFile(mesh, argv[3]); 66 | } 67 | -------------------------------------------------------------------------------- /src/mesh.cc: -------------------------------------------------------------------------------- 1 | #include "mesh.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | Mesh::Mesh() 8 | {} 9 | 10 | void Mesh::SaveOBJ(const char* filename, const Camera& camera, bool readjust) { 11 | auto& vertices = vertices_; 12 | auto& faces = faces_; 13 | std::ofstream os(filename); 14 | for (int i = 0; i < vertices.size(); ++i) { 15 | auto v = vertices[i]; 16 | if (readjust) 17 | camera.Undistort(v); 18 | os << "v " << v[0] << " " << v[1] << " " << v[2] << "\n"; 19 | } 20 | for (int i = 0; i < faces.size(); ++i) { 21 | auto& f = faces[i]; 22 | os << "f " << f[0] + 1 << " " << f[1] + 1 << " " << f[2] + 1 << "\n"; 23 | } 24 | os.close(); 25 | } 26 | 27 | Eigen::Vector3d PerspectiveInterp(const Eigen::Vector3d& a, const Eigen::Vector3d& b, double step, bool perspective) { 28 | if (!perspective) 29 | return a * (1 - step) + b * step; 30 | Eigen::Vector3d v0(a[0] * a[2],a[1] * a[2],a[2]); 31 | Eigen::Vector3d v1(b[0] * b[2],b[1] * b[2],b[2]); 32 | Eigen::Vector3d n1 = (v1 - v0); 33 | if (n1.norm() == 0) 34 | return a; 35 | n1 /= n1.norm(); 36 | 37 | Eigen::Vector3d dir1(a[0] * (1 - step) + b[0] * step, a[1] * (1 - step) + b[1] * step, 1); 38 | 39 | Eigen::Vector3d dir = dir1 - dir1.dot(n1) * n1; 40 | 41 | double d_norm = dir.squaredNorm(); 42 | double z = v0.dot(dir) / d_norm; 43 | 44 | dir1[2] = z; 45 | return dir1; 46 | } 47 | 48 | void Mesh::LoadFromFile(const char* filename) { 49 | auto& vertices = vertices_; 50 | auto& faces = faces_; 51 | vertices.clear(); 52 | faces.clear(); 53 | char buffer[1024]; 54 | std::ifstream is(filename); 55 | while (is >> buffer) { 56 | if (strcmp(buffer, "v") == 0) { 57 | Eigen::Vector3d v; 58 | is >> v[0] >> v[1] >> v[2]; 59 | vertices.push_back(v); 60 | } 61 | else if (strcmp(buffer, "f") == 0) { 62 | Eigen::Vector3i f; 63 | for (int i = 0; i < 3; ++i) { 64 | is >> buffer; 65 | int t = 0; 66 | int p = 0; 67 | int l = strlen(buffer); 68 | while (p != l && buffer[p] >= '0' && buffer[p] <= '9') { 69 | t = t * 10 + (buffer[p] - '0'); 70 | p += 1; 71 | } 72 | f[i] = t - 1; 73 | } 74 | faces.push_back(f); 75 | } 76 | } 77 | is.close(); 78 | } 79 | 80 | void Mesh::BoundaryClip(int dim, double clamp_thres, int comp, bool perspective) { 81 | auto& vertices = vertices_; 82 | auto& faces = faces_; 83 | 84 | int i = 0; 85 | int faces_num = faces.size(); 86 | while (i < faces_num) { 87 | Eigen::Vector3d v[3]; 88 | for (int j = 0; j < 3; ++j) 89 | v[j] = vertices[faces[i][j]]; 90 | int valids[3]; 91 | for (int j = 0; j < 3; ++j) { 92 | if ((comp == 0 && v[j][dim] < clamp_thres + -1e-6) 93 | || (comp == 1 && v[j][dim] > clamp_thres + 1e-6)) { 94 | valids[j] = 0; 95 | } else { 96 | valids[j] = 1; 97 | } 98 | } 99 | int sum = valids[0] + valids[1] + valids[2]; 100 | if (sum == 0) { 101 | faces[i] = faces.back(); 102 | faces.pop_back(); 103 | faces_num = faces.size(); 104 | continue; 105 | } 106 | else if (sum == 3) { 107 | } 108 | else if (sum == 1) { 109 | int j = 0; 110 | while (valids[j] == 0) 111 | j += 1; 112 | double step0 = 0, step1 = 0; 113 | if (comp == 0) { 114 | step0 = (clamp_thres - v[j][dim]) / (v[(j + 2) % 3][dim] - v[j][dim]); 115 | step1 = (clamp_thres - v[j][dim]) / (v[(j + 1) % 3][dim] - v[j][dim]); 116 | } 117 | else { 118 | step0 = (clamp_thres - v[j][dim]) / (v[(j + 2) % 3][dim] - v[j][dim]); 119 | step1 = (clamp_thres - v[j][dim]) / (v[(j + 1) % 3][dim] - v[j][dim]); 120 | } 121 | int nx = vertices.size(); 122 | int nx2 = vertices.size() + 1; 123 | 124 | vertices.push_back(PerspectiveInterp(v[j], v[(j+2)%3], step0, perspective)); 125 | vertices.push_back(PerspectiveInterp(v[j], v[(j+1)%3], step1, perspective)); 126 | 127 | faces[i] = Eigen::Vector3i(nx, faces[i][j], nx2); 128 | } 129 | else { 130 | int j = 0; 131 | while (valids[j] == 1) 132 | j += 1; 133 | double step0 = 0, step1 = 0; 134 | if (comp == 0) { 135 | step0 = (clamp_thres - v[j][dim]) / (v[(j + 2) % 3][dim] - v[j][dim]); 136 | step1 = (clamp_thres - v[j][dim]) / (v[(j + 1) % 3][dim] - v[j][dim]); 137 | } 138 | else { 139 | step0 = (clamp_thres - v[j][dim]) / (v[(j + 2) % 3][dim] - v[j][dim]); 140 | step1 = (clamp_thres - v[j][dim]) / (v[(j + 1) % 3][dim] - v[j][dim]); 141 | } 142 | int v0 = faces[i][(j + 2) % 3]; 143 | int nx = vertices.size(); 144 | int nx2 = vertices.size() + 1; 145 | int v1 = faces[i][(j + 1) % 3]; 146 | 147 | vertices.push_back(PerspectiveInterp(v[j], v[(j+2)%3], step0, perspective)); 148 | vertices.push_back(PerspectiveInterp(v[j], v[(j+1)%3], step1, perspective)); 149 | 150 | faces[i] = Eigen::Vector3i(v0, nx, nx2); 151 | faces.push_back(Eigen::Vector3i(v0, nx2, v1)); 152 | } 153 | i += 1; 154 | } 155 | } 156 | 157 | void Mesh::Recenter() { 158 | auto& vertices = vertices_; 159 | for (int i = 0; i < vertices.size(); ++i) { 160 | vertices[i][0] -= 0.5; 161 | vertices[i][1] -= 0.5; 162 | } 163 | } 164 | 165 | void Mesh::ComputeNormals() 166 | { 167 | auto& face_normals = face_normals_; 168 | auto& vertices = vertices_; 169 | auto& faces = faces_; 170 | 171 | face_normals.resize(faces.size()); 172 | for (int i = 0; i < faces.size(); ++i) { 173 | auto v0 = vertices[faces[i][0]]; 174 | auto v1 = vertices[faces[i][1]]; 175 | auto v2 = vertices[faces[i][2]]; 176 | v0[0] *= v0[2]; 177 | v0[1] *= v0[2]; 178 | v1[0] *= v1[2]; 179 | v1[1] *= v1[2]; 180 | v2[0] *= v2[2]; 181 | v2[1] *= v2[2]; 182 | Eigen::Vector3d d1 = v1 - v0; 183 | Eigen::Vector3d d2 = v2 - v0; 184 | Eigen::Vector3d n = d1.cross(d2); 185 | if (n.norm() > 0) 186 | n = n.normalized(); 187 | face_normals[i] = n; 188 | } 189 | } 190 | 191 | void Mesh::ComputePlaneParameters() { 192 | auto& faces = faces_; 193 | auto& vertices = vertices_; 194 | auto& params = params_; 195 | 196 | params_.resize(faces.size()); 197 | for (int i = 0; i < faces.size(); ++i) { 198 | params[i] = PlaneParam(vertices[faces[i][0]],vertices[faces[i][1]],vertices[faces[i][2]]); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTORGRAPH_RENDERER_SCENE_H_ 2 | #define VECTORGRAPH_RENDERER_SCENE_H_ 3 | 4 | #include "plane_param.h" 5 | #include "camera.h" 6 | class Mesh 7 | { 8 | public: 9 | Mesh(); 10 | 11 | void LoadFromFile(const char* filename); 12 | 13 | void BoundaryClip(int dim, double clamp_thres, int comp, bool perspective); 14 | 15 | void Recenter(); 16 | 17 | void ComputeNormals(); 18 | 19 | void ComputePlaneParameters(); 20 | 21 | void SaveOBJ(const char* filename, const Camera& camera, bool readjust); 22 | 23 | int FaceNum() const { 24 | return faces_.size(); 25 | } 26 | 27 | const std::vector& GetVertices() const { 28 | return vertices_; 29 | } 30 | const std::vector& GetFaceNormals() const { 31 | return face_normals_; 32 | } 33 | const std::vector& GetFaces() const { 34 | return faces_; 35 | } 36 | const std::vector& GetPlanes() const { 37 | return params_; 38 | } 39 | 40 | std::vector& GetVertices() { 41 | return vertices_; 42 | } 43 | std::vector& GetFaceNormals() { 44 | return face_normals_; 45 | } 46 | std::vector& GetFaces() { 47 | return faces_; 48 | } 49 | std::vector& GetPlanes() { 50 | return params_; 51 | } 52 | private: 53 | std::vector vertices_; 54 | std::vector face_normals_; 55 | std::vector faces_; 56 | std::vector params_; 57 | }; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /src/plane_param.cc: -------------------------------------------------------------------------------- 1 | #include "plane_param.h" 2 | 3 | #include 4 | 5 | PlaneParam::PlaneParam() 6 | {} 7 | 8 | PlaneParam::PlaneParam( 9 | const Eigen::Vector3d& v0, 10 | const Eigen::Vector3d& v1, 11 | const Eigen::Vector3d& v2) { 12 | 13 | Eigen::Vector3d V0(v0[0] * v0[2], v0[1] * v0[2], v0[2]); 14 | Eigen::Vector3d V1(v1[0] * v1[2], v1[1] * v1[2], v1[2]); 15 | Eigen::Vector3d V2(v2[0] * v2[2], v2[1] * v2[2], v2[2]); 16 | /* 17 | Eigen::Vector3d V0(v0); 18 | Eigen::Vector3d V1(v1); 19 | Eigen::Vector3d V2(v2); 20 | */ 21 | Eigen::Vector3d norm = (V1 - V0).cross(V2 - V0); 22 | if (norm.norm() < 1e-10 || norm[2] == 0) { 23 | invalid_ = true; 24 | } 25 | norm.normalize(); 26 | d_ = -V0.dot(norm); 27 | n1_ = norm[0]; 28 | n2_ = norm[1]; 29 | n3_ = norm[2]; 30 | invalid_ = false; 31 | if (d_ == 0) 32 | invalid_ = true; 33 | } 34 | 35 | bool PlaneParam::ProjectiveIntersection(const PlaneParam& other, LineSegment* l) const { 36 | if (invalid_ || other.invalid_) 37 | return false; 38 | //figure out the intersection line direction 39 | l->n1 = n2_ * other.n3_ - n3_ * other.n2_; 40 | l->n2 = n3_ * other.n1_ - n1_ * other.n3_; 41 | l->n3 = n1_ * other.n2_ - n2_ * other.n1_; 42 | 43 | //figure out one intersection point 44 | if (l->n1 != 0) { 45 | l->x = 0; 46 | l->z = (other.n2_ * d_ - n2_ * other.d_) / l->n1; 47 | l->y = (-other.n3_ * d_ + n3_ * other.d_) / l->n1; 48 | } 49 | else if (l->n2 != 0) { 50 | l->y = 0; 51 | l->x = (other.n3_ * d_ - n3_ * other.d_) / l->n2; 52 | l->z = (-other.n1_ * d_ + n1_ * other.d_) / l->n2; 53 | } 54 | else { 55 | return false; 56 | } 57 | 58 | if (l->x == 0 && l->y == 0) 59 | return false; 60 | 61 | if (l->z == 0) { 62 | l->x += l->n1; 63 | l->y += l->n2; 64 | l->z += l->n3; 65 | } 66 | K nx, ny; 67 | if (l->z + l->n3 != 0) { 68 | nx = (l->x+l->n1) / (l->z+l->n3); 69 | ny = (l->y+l->n2) / (l->z+l->n3); 70 | } else if (l->z - l->n3 != 0) { 71 | nx = (l->x-l->n1) / (l->z-l->n3); 72 | ny = (l->y-l->n2) / (l->z-l->n3); 73 | } else { 74 | return false; 75 | } 76 | 77 | l->x /= l->z; 78 | l->y /= l->z; 79 | l->n1 = nx - l->x; 80 | l->n2 = ny - l->y; 81 | return true; 82 | } 83 | -------------------------------------------------------------------------------- /src/plane_param.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTORGRAPH_RENDERER_PLANE_PARAM_H_ 2 | #define VECTORGRAPH_RENDERER_PLANE_PARAM_H_ 3 | 4 | #include 5 | 6 | #include "types.h" 7 | 8 | struct LineSegment { 9 | bool Intersect(const Point_2& p0, const Point_2& p1, Point_2* p) { 10 | K a = p1.x() - p0.x(); 11 | K b = -n1; 12 | K c = x - p0.x(); 13 | K d = p1.y() - p0.y(); 14 | K e = -n2; 15 | K f = y - p0.y(); 16 | K n = a * e - b * d; 17 | if (n == 0) 18 | return false; 19 | K xx = (c * e - f * b) / n; 20 | K yy = (-c * d + f * a) / n; 21 | 22 | if (xx < 0 || xx >= 1) 23 | return false; 24 | *p = Point_2(xx * a + p0.x(), xx * d + p0.y()); 25 | 26 | return true; 27 | } 28 | K x, y, z; 29 | K n1, n2, n3; 30 | }; 31 | 32 | struct PlaneParam 33 | { 34 | public: 35 | PlaneParam(); 36 | PlaneParam( 37 | const Eigen::Vector3d& v0, 38 | const Eigen::Vector3d& v1, 39 | const Eigen::Vector3d& v2); 40 | 41 | // make this an inline funcion for fast computation 42 | /* 43 | K ComputeDepth(const Point_2& p) const { 44 | if (invalid_) 45 | return K(-1e30); 46 | return (d_ - p.x() * n1_ - p.y() * n2_) / n3_; 47 | } 48 | */ 49 | bool ProjectiveIntersection(const PlaneParam& other, LineSegment* pl) const; 50 | 51 | K ComputeDepth(const Point_2& p) const { 52 | if (invalid_ ) 53 | return K(-1e30); 54 | K dx = p.x(); 55 | K dy = p.y(); 56 | K dz = K(1); 57 | K dis = dx * n1_ + dy * n2_ + dz * n3_; 58 | 59 | K z = -d_ / dis; 60 | if (z < 0) 61 | printf("Inverse Depth!\n"); 62 | return z; 63 | } 64 | 65 | bool InsidePlane(K x, K y, K z) const { 66 | return (!invalid_) && (n1_ * x + n2_ * y + n3_ * z + d_ == 0); 67 | } 68 | 69 | bool invalid_; 70 | K n1_, n2_, n3_, d_; 71 | }; 72 | 73 | #endif -------------------------------------------------------------------------------- /src/postprocess.cc: -------------------------------------------------------------------------------- 1 | #include "postprocess.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "simple_svg.h" 9 | 10 | using namespace svg; 11 | 12 | int EvaluateEdgeType(Arrangement_2::Halfedge_const_handle he, const Mesh& mesh, const Arrangement_2& overlay, double angle_thres) { 13 | int fid = he->face()->data() - 1; 14 | auto& params = mesh.GetPlanes(); 15 | if (he->twin()->face() == overlay.unbounded_face()) { 16 | return 2; 17 | } 18 | else { 19 | int fid1 = he->twin()->face()->data() - 1; 20 | if (fid1 == -1) 21 | return 2; 22 | if (fid1 != fid) { 23 | K z = params[fid].ComputeDepth(he->source()->point()); 24 | K n_z = params[fid1].ComputeDepth(he->source()->point()); 25 | if (std::abs((z-n_z).convert_to()) > 1e-6) { 26 | return 2; 27 | } else { 28 | K z = params[fid].ComputeDepth(he->target()->point()); 29 | K n_z = params[fid1].ComputeDepth(he->target()->point()); 30 | if (std::abs((z-n_z).convert_to()) > 1e-6) 31 | return 2; 32 | 33 | auto& p1 = mesh.GetPlanes()[fid]; 34 | auto& p2 = mesh.GetPlanes()[fid1]; 35 | K dot = p1.n1_ * p2.n1_ + p1.n2_ * p2.n2_ + p1.n3_ * p2.n3_; 36 | double t1 = dot.convert_to(); 37 | double t2 = cos(angle_thres * 3.141592654 / 180.0); 38 | 39 | if (std::abs(t1) < t2) { 40 | return 1; 41 | } 42 | } 43 | } 44 | } 45 | 46 | return 0; 47 | } 48 | 49 | void PostProcess::CollectFaceAndVertices(const Mesh& mesh, const Arrangement_2& overlay, double angle_thres) { 50 | auto& points = points_; 51 | auto& facets_id = facets_id_; 52 | auto& facets = facets_; 53 | auto& params = mesh.GetPlanes(); 54 | auto& depths = depths_; 55 | std::map, int> vertexID; 56 | 57 | for (auto fit = overlay.faces_begin(); fit != overlay.faces_end(); ++fit) { 58 | int fid = fit->data() - 1; 59 | if (fit == overlay.unbounded_face() || fid < 0) 60 | continue; 61 | 62 | EdgeList e; 63 | auto e_handle = fit->outer_ccb(); 64 | Arrangement_2::Ccb_halfedge_const_circulator curr = e_handle; 65 | do { 66 | Arrangement_2::Halfedge_const_handle he = curr; 67 | auto key = std::make_pair(he->source(), fid); 68 | auto it = vertexID.find(key); 69 | if (it == vertexID.end()) { 70 | vertexID[key] = points.size(); 71 | e.outer_indices.push_back(points.size()); 72 | K z = params[fid].ComputeDepth(he->source()->point()); 73 | points.push_back(key); 74 | depths.push_back(z); 75 | } else { 76 | e.outer_indices.push_back(it->second); 77 | } 78 | e.outer_type.push_back(EvaluateEdgeType(he, mesh, overlay, angle_thres)); 79 | } while (++curr != e_handle); 80 | 81 | for (auto hi = fit->holes_begin(); hi != fit->holes_end(); ++hi) { 82 | auto circ = *hi; 83 | Arrangement_2::Ccb_halfedge_const_circulator curr = circ; 84 | e.inner_indices.push_back(std::vector()); 85 | e.inner_type.push_back(std::vector()); 86 | do { 87 | Arrangement_2::Halfedge_const_handle he = curr; 88 | auto key = std::make_pair(he->source(), fid); 89 | auto it = vertexID.find(key); 90 | if (it == vertexID.end()) { 91 | vertexID[key] = points.size(); 92 | e.inner_indices.back().push_back(points.size()); 93 | K z = params[fid].ComputeDepth(he->source()->point()); 94 | points.push_back(key); 95 | depths.push_back(z); 96 | 97 | } else { 98 | e.inner_indices.back().push_back(it->second); 99 | } 100 | e.inner_type.back().push_back(EvaluateEdgeType(he, mesh, overlay, angle_thres)); 101 | } while (++curr != circ); 102 | } 103 | facets.push_back(e); 104 | facets_id.push_back(fid); 105 | } 106 | } 107 | 108 | void PostProcess::RemoveRedundantVertices() 109 | { 110 | auto& facets = facets_; 111 | auto& points = points_; 112 | auto& depths = depths_; 113 | auto& degrees = degrees_; 114 | 115 | ComputeDegree(); 116 | 117 | for (auto& f : facets) { 118 | RemoveRedundantVerticesFromLoop(f.outer_indices, f.outer_type); 119 | for (int i = 0; i < f.inner_indices.size(); ++i) { 120 | RemoveRedundantVerticesFromLoop(f.inner_indices[i], f.inner_type[i]); 121 | } 122 | } 123 | 124 | ComputeDegree(); 125 | std::vector compressed_vertexID(degrees.size()); 126 | compressed_vertexID[0] = 0; 127 | for (int i = 1; i < compressed_vertexID.size(); ++i) { 128 | compressed_vertexID[i] = compressed_vertexID[i - 1] + (degrees[i - 1] > 0 ? 1 : 0); 129 | } 130 | 131 | int top = 0; 132 | for (int i = 0; i < points.size(); ++i) { 133 | if (degrees[i]) { 134 | points[top] = points[i]; 135 | depths[top] = depths[i]; 136 | top += 1; 137 | } 138 | } 139 | 140 | points.resize(top); 141 | depths.resize(top); 142 | for (auto& i : facets) { 143 | for (auto& e : i.outer_indices) { 144 | e = compressed_vertexID[e]; 145 | } 146 | for (auto& es : i.inner_indices) { 147 | for (auto& e : es) 148 | e = compressed_vertexID[e]; 149 | } 150 | } 151 | } 152 | 153 | void PostProcess::MergeDuplex(const Mesh& mesh) 154 | { 155 | auto & vertices = mesh.GetVertices(); 156 | auto & faces = mesh.GetFaces(); 157 | 158 | auto& facets = facets_; 159 | auto& points = points_; 160 | auto& depths = depths_; 161 | // merge duplex 162 | std::map >, int> vID; 163 | std::vector compressed_vertexID; 164 | 165 | int top = 0; 166 | compressed_vertexID.resize(points.size()); 167 | std::vector points_buf; 168 | std::vector depths_buf; 169 | for (int i = 0; i < points.size(); ++i) { 170 | double x = points[i].first->point().x().convert_to(); 171 | double y = points[i].first->point().y().convert_to(); 172 | double z = depths[i].convert_to(); 173 | 174 | auto key = std::make_pair(int(x * 1e5), std::make_pair(int(y * 1e5), int(z * 1e5))); 175 | auto it = vID.find(key); 176 | if (it == vID.end()) { 177 | compressed_vertexID[i] = top; 178 | points_buf.push_back(points[i]); 179 | depths_buf.push_back(depths[i]); 180 | vID[key] = top++; 181 | } else { 182 | compressed_vertexID[i] = it->second; 183 | } 184 | } 185 | points = points_buf; 186 | depths = depths_buf; 187 | 188 | auto shrink = [&](std::vector& v) { 189 | std::vector m(v.size(), 1); 190 | for (int i = 0; i < v.size(); ++i) { 191 | int curr = v[i]; 192 | int next = v[(i + 1) % v.size()]; 193 | if (curr == next) { 194 | m[i] = 0; 195 | } 196 | } 197 | int top = 0; 198 | for (int i = 0; i < v.size(); ++i) { 199 | if (m[i] == 1) 200 | v[top++] = v[i]; 201 | } 202 | v.resize(top); 203 | }; 204 | 205 | for (auto& i : facets) { 206 | for (auto& e : i.outer_indices) { 207 | e = compressed_vertexID[e]; 208 | } 209 | shrink(i.outer_indices); 210 | for (auto& es : i.inner_indices) { 211 | for (auto& e : es) { 212 | e = compressed_vertexID[e]; 213 | } 214 | shrink(es); 215 | } 216 | } 217 | } 218 | 219 | 220 | void PostProcess::CollectEdges(const Mesh& mesh) { 221 | auto & vertices = mesh.GetVertices(); 222 | auto & faces = mesh.GetFaces(); 223 | auto & face_normals = mesh.GetFaceNormals(); 224 | auto & facets_id = facets_id_; 225 | auto & edge2face = edge2face_; 226 | 227 | auto& facets = facets_; 228 | auto& points = points_; 229 | 230 | for (int i = 0; i < facets.size(); ++i) { 231 | for (int j = 0; j < facets[i].outer_indices.size(); ++j) { 232 | int v0 = facets[i].outer_indices[j]; 233 | int v1 = facets[i].outer_indices[(j + 1) % facets[i].outer_indices.size()]; 234 | auto key = (v0 < v1) ? std::make_pair(v0, v1) : std::make_pair(v1, v0); 235 | edge2face[key].insert(facets_id[i]); 236 | } 237 | for (auto& es : facets[i].inner_indices) { 238 | for (int j = 0; j < es.size(); ++j) { 239 | int v0 = es[j]; 240 | int v1 = es[(j + 1) % es.size()]; 241 | auto key = (v0 < v1) ? std::make_pair(v0, v1) : std::make_pair(v1, v0); 242 | edge2face[key].insert(facets_id[i]); 243 | } 244 | } 245 | } 246 | } 247 | 248 | void PostProcess::SaveToFile(const Mesh& mesh, const char* filename) { 249 | auto & vertices = mesh.GetVertices(); 250 | auto & faces = mesh.GetFaces(); 251 | auto & facets = facets_; 252 | auto & face_normals = mesh.GetFaceNormals(); 253 | auto & facets_id = facets_id_; 254 | auto & edge2face = edge2face_; 255 | auto & points = points_; 256 | auto & depths = depths_; 257 | 258 | std::ofstream os; 259 | 260 | os.open(filename); 261 | 262 | for (int i = 0; i < points.size(); ++i) { 263 | double x = points[i].first->point().x().convert_to(); 264 | double y = points[i].first->point().y().convert_to(); 265 | double z = depths[i].convert_to(); 266 | 267 | Eigen::Vector3d v(x * z, y * z, z); 268 | //Eigen::Vector3d v(x, y, z); 269 | os << "v " << v[0] << " " << v[1] << " " << v[2] << "\n"; 270 | } 271 | 272 | for (auto& i : facets) { 273 | if (i.outer_indices.size() < 3) 274 | continue; 275 | os << "f"; 276 | for (auto& e : i.outer_indices) { 277 | os << " " << e + 1; 278 | } 279 | os << "\n"; 280 | for (auto& es : i.inner_indices) { 281 | if (es.size() < 3) 282 | continue; 283 | os << "###holes### f"; 284 | for (auto& e : es) { 285 | os << " " << e + 1; 286 | } 287 | os << "\n"; 288 | } 289 | } 290 | 291 | for (int i = 0; i < points.size(); ++i) { 292 | double x = points[i].first->point().x().convert_to(); 293 | double y = points[i].first->point().y().convert_to(); 294 | double z = depths[i].convert_to(); 295 | 296 | Eigen::Vector3d v(x * z, y * z, z); 297 | //Eigen::Vector3d v(x, y, z); 298 | os << "v " << v[0] << " " << v[1] << " " << v[2] << "\n"; 299 | } 300 | os << "### occlude boundaries\n"; 301 | for (auto& i : facets) { 302 | if (i.outer_indices.size() < 3) 303 | continue; 304 | for (int j = 0; j < i.outer_indices.size(); ++j) { 305 | if (i.outer_type[j] == 2) { 306 | int next_j = (j + 1) % i.outer_indices.size(); 307 | os << "l " << i.outer_indices[j] + 1 308 | << " " << i.outer_indices[next_j] + 1 << "\n"; 309 | } 310 | } 311 | } 312 | 313 | for (int i = 0; i < points.size(); ++i) { 314 | double x = points[i].first->point().x().convert_to(); 315 | double y = points[i].first->point().y().convert_to(); 316 | double z = depths[i].convert_to(); 317 | 318 | Eigen::Vector3d v(x * z, y * z, z); 319 | //Eigen::Vector3d v(x, y, z); 320 | os << "v " << v[0] << " " << v[1] << " " << v[2] << "\n"; 321 | } 322 | os << "### sharp edges\n"; 323 | for (auto& i : facets) { 324 | if (i.outer_indices.size() < 3) 325 | continue; 326 | for (int j = 0; j < i.outer_indices.size(); ++j) { 327 | if (i.outer_type[j] == 1) { 328 | int next_j = (j + 1) % i.outer_indices.size(); 329 | os << "l " << i.outer_indices[j] + 1 330 | << " " << i.outer_indices[next_j] + 1 << "\n"; 331 | } 332 | } 333 | } 334 | os.close(); 335 | } 336 | 337 | void PostProcess::SaveToSVG(const Mesh& mesh, const char* filename) { 338 | auto & facets = facets_; 339 | auto & points = points_; 340 | auto & face_normals = mesh.GetFaceNormals(); 341 | auto & edge2face = edge2face_; 342 | 343 | Dimensions dimensions(1000, 1000); 344 | Document doc(filename, Layout(dimensions, Layout::BottomLeft)); 345 | 346 | // Red image border. 347 | Polygon border(Stroke(1, Color::Red)); 348 | border << Point(0, 0) << Point(dimensions.width, 0) 349 | << Point(dimensions.width, dimensions.height) << Point(0, dimensions.height); 350 | doc << border; 351 | // Render polygon 352 | 353 | for (auto& i : facets) { 354 | if (i.outer_indices.size() < 3) 355 | continue; 356 | 357 | Polygon poly(Color(128,128,128), Stroke(.0, Color(150, 160, 200))); 358 | 359 | for (auto& e : i.outer_indices) { 360 | auto& p = points[e].first->point(); 361 | int x = (p.x().convert_to()+0.5) * 1000; 362 | int y = (p.y().convert_to()+0.5) * 1000; 363 | poly << Point(x, 1000-y); 364 | } 365 | doc << poly; 366 | } 367 | 368 | for (int seq = 1; seq <= 2; ++seq) { 369 | for (auto& i : facets) { 370 | if (i.outer_indices.size() < 3) 371 | continue; 372 | for (int j = 0; j < i.outer_indices.size(); ++j) { 373 | if (i.outer_type[j] > 0 && i.outer_type[j] == seq) { 374 | Polyline poly(Stroke(2, Color::Blue)); 375 | if (i.outer_type[j] == 2) 376 | poly = Polyline(Stroke(2, Color::Red)); 377 | { 378 | auto& p1 = points[i.outer_indices[j]].first->point(); 379 | int x = (p1.x().convert_to()+0.5) * 1000; 380 | int y = (p1.y().convert_to()+0.5) * 1000; 381 | poly << Point(x, 1000-y); 382 | } 383 | { 384 | int next_j = (j + 1) % i.outer_indices.size(); 385 | auto& p1 = points[i.outer_indices[next_j]].first->point(); 386 | int x = (p1.x().convert_to()+0.5) * 1000; 387 | int y = (p1.y().convert_to()+0.5) * 1000; 388 | poly << Point(x, 1000-y); 389 | } 390 | doc << poly; 391 | } 392 | } 393 | } 394 | } 395 | 396 | doc.save(); 397 | } 398 | 399 | void PostProcess::ComputeDegree() 400 | { 401 | degrees_.resize(points_.size()); 402 | 403 | for (auto& d :degrees_) 404 | d = 0; 405 | for (auto& f : facets_) { 406 | for (auto& e : f.outer_indices) { 407 | degrees_[e] += 2; 408 | } 409 | for (auto& es : f.inner_indices) { 410 | for (auto& e : es) { 411 | degrees_[e] += 2; 412 | } 413 | } 414 | } 415 | } 416 | 417 | void PostProcess::RemoveRedundantVerticesFromLoop(std::vector& indices, std::vector& types) { 418 | auto& points = points_; 419 | auto& degrees = degrees_; 420 | 421 | std::vector mask(indices.size(), 0); 422 | for (int i = 0; i < indices.size(); ++i) { 423 | if (degrees[indices[i]] > 2) { 424 | mask[i] = 1; 425 | continue; 426 | } 427 | if (types[i] != types[(i + indices.size() - 1) % indices.size()]) { 428 | mask[i] = 1; 429 | continue; 430 | } 431 | int prev_id = indices[(i + indices.size() - 1) % indices.size()]; 432 | int next_id = indices[(i + 1) % indices.size()]; 433 | 434 | auto& p1 = points[prev_id]; 435 | auto& p2 = points[indices[i]]; 436 | auto& p3 = points[next_id]; 437 | 438 | auto diff1 = p2.first->point() - p1.first->point(); 439 | auto diff2 = p3.first->point() - p2.first->point(); 440 | 441 | K a = diff1.x() * diff2.y() - diff1.y() * diff2.x(); 442 | if (a != K(0)) { 443 | mask[i] = 1; 444 | } 445 | } 446 | int top = 0; 447 | for (int i = 0; i < indices.size(); ++i) { 448 | if (mask[i] == 1) { 449 | indices[top] = indices[i]; 450 | types[top] = types[i]; 451 | top += 1; 452 | } 453 | } 454 | indices.resize(top); 455 | types.resize(top); 456 | 457 | } -------------------------------------------------------------------------------- /src/postprocess.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTORGRAPH_RENDER_POSTPROCESS_H_ 2 | #define VECTORGRAPH_RENDER_POSTPROCESS_H_ 3 | 4 | #include "mesh.h" 5 | #include "types.h" 6 | 7 | class PostProcess 8 | { 9 | public: 10 | typedef std::pair VertexSignature; 11 | struct EdgeList { 12 | std::vector outer_indices; 13 | std::vector outer_type; 14 | std::vector > inner_indices; 15 | std::vector > inner_type; 16 | }; 17 | 18 | void CollectFaceAndVertices(const Mesh& mesh, const Arrangement_2& overlay, double angle_thres); 19 | void RemoveRedundantVertices(); 20 | void MergeDuplex(const Mesh& mesh); 21 | void CollectEdges(const Mesh& mesh); 22 | 23 | void SaveToFile(const Mesh& mesh, const char* filename); 24 | void SaveToSVG(const Mesh& mesh, const char* filename); 25 | protected: 26 | void RemoveRedundantVerticesFromLoop(std::vector& indices, std::vector& types); 27 | void ComputeDegree(); 28 | 29 | private: 30 | std::vector facets_; 31 | 32 | std::vector facets_id_; 33 | std::vector points_; 34 | std::vector depths_; 35 | 36 | std::vector degrees_; 37 | 38 | std::map, std::set > edge2face_; 39 | }; 40 | 41 | #endif -------------------------------------------------------------------------------- /src/simple_svg.h: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************* 3 | * The "New BSD License" : http://www.opensource.org/licenses/bsd-license.php * 4 | ******************************************************************************** 5 | 6 | Copyright (c) 2010, Mark Turney 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | * Neither the name of the nor the 17 | names of its contributors may be used to endorse or promote products 18 | derived from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 24 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | ******************************************************************************/ 32 | 33 | #ifndef SIMPLE_SVG_HPP 34 | #define SIMPLE_SVG_HPP 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include 42 | 43 | namespace svg 44 | { 45 | // Utility XML/String Functions. 46 | template 47 | std::string attribute(std::string const & attribute_name, 48 | T const & value, std::string const & unit = "") 49 | { 50 | std::stringstream ss; 51 | ss << attribute_name << "=\"" << value << unit << "\" "; 52 | return ss.str(); 53 | } 54 | std::string elemStart(std::string const & element_name) 55 | { 56 | return "\t<" + element_name + " "; 57 | } 58 | std::string elemEnd(std::string const & element_name) 59 | { 60 | return "\n"; 61 | } 62 | std::string emptyElemEnd() 63 | { 64 | return "/>\n"; 65 | } 66 | 67 | // Quick optional return type. This allows functions to return an invalid 68 | // value if no good return is possible. The user checks for validity 69 | // before using the returned value. 70 | template 71 | class optional 72 | { 73 | public: 74 | optional(T const & type) 75 | : valid(true), type(type) { } 76 | optional() : valid(false), type(T()) { } 77 | T * operator->() 78 | { 79 | // If we try to access an invalid value, an exception is thrown. 80 | if (!valid) 81 | throw std::exception(); 82 | 83 | return &type; 84 | } 85 | // Test for validity. 86 | bool operator!() const { return !valid; } 87 | private: 88 | bool valid; 89 | T type; 90 | }; 91 | 92 | struct Dimensions 93 | { 94 | Dimensions(double width, double height) : width(width), height(height) { } 95 | Dimensions(double combined = 0) : width(combined), height(combined) { } 96 | double width; 97 | double height; 98 | }; 99 | 100 | struct Point 101 | { 102 | Point(double x = 0, double y = 0) : x(x), y(y) { } 103 | double x; 104 | double y; 105 | }; 106 | optional getMinPoint(std::vector const & points) 107 | { 108 | if (points.empty()) 109 | return optional(); 110 | 111 | Point min = points[0]; 112 | for (unsigned i = 0; i < points.size(); ++i) { 113 | if (points[i].x < min.x) 114 | min.x = points[i].x; 115 | if (points[i].y < min.y) 116 | min.y = points[i].y; 117 | } 118 | return optional(min); 119 | } 120 | optional getMaxPoint(std::vector const & points) 121 | { 122 | if (points.empty()) 123 | return optional(); 124 | 125 | Point max = points[0]; 126 | for (unsigned i = 0; i < points.size(); ++i) { 127 | if (points[i].x > max.x) 128 | max.x = points[i].x; 129 | if (points[i].y > max.y) 130 | max.y = points[i].y; 131 | } 132 | return optional(max); 133 | } 134 | 135 | // Defines the dimensions, scale, origin, and origin offset of the document. 136 | struct Layout 137 | { 138 | enum Origin { TopLeft, BottomLeft, TopRight, BottomRight }; 139 | 140 | Layout(Dimensions const & dimensions = Dimensions(400, 300), Origin origin = BottomLeft, 141 | double scale = 1, Point const & origin_offset = Point(0, 0)) 142 | : dimensions(dimensions), scale(scale), origin(origin), origin_offset(origin_offset) { } 143 | Dimensions dimensions; 144 | double scale; 145 | Origin origin; 146 | Point origin_offset; 147 | }; 148 | 149 | // Convert coordinates in user space to SVG native space. 150 | double translateX(double x, Layout const & layout) 151 | { 152 | if (layout.origin == Layout::BottomRight || layout.origin == Layout::TopRight) 153 | return layout.dimensions.width - ((x + layout.origin_offset.x) * layout.scale); 154 | else 155 | return (layout.origin_offset.x + x) * layout.scale; 156 | } 157 | 158 | double translateY(double y, Layout const & layout) 159 | { 160 | if (layout.origin == Layout::BottomLeft || layout.origin == Layout::BottomRight) 161 | return layout.dimensions.height - ((y + layout.origin_offset.y) * layout.scale); 162 | else 163 | return (layout.origin_offset.y + y) * layout.scale; 164 | } 165 | double translateScale(double dimension, Layout const & layout) 166 | { 167 | return dimension * layout.scale; 168 | } 169 | 170 | class Serializeable 171 | { 172 | public: 173 | Serializeable() { } 174 | virtual ~Serializeable() { }; 175 | virtual std::string toString(Layout const & layout) const = 0; 176 | }; 177 | 178 | class Color : public Serializeable 179 | { 180 | public: 181 | enum Defaults { Transparent = -1, Aqua, Black, Blue, Brown, Cyan, Fuchsia, 182 | Green, Lime, Magenta, Orange, Purple, Red, Silver, White, Yellow }; 183 | 184 | Color(int r, int g, int b) : transparent(false), red(r), green(g), blue(b) { } 185 | Color(Defaults color) 186 | : transparent(false), red(0), green(0), blue(0) 187 | { 188 | switch (color) 189 | { 190 | case Aqua: assign(0, 255, 255); break; 191 | case Black: assign(0, 0, 0); break; 192 | case Blue: assign(0, 0, 255); break; 193 | case Brown: assign(165, 42, 42); break; 194 | case Cyan: assign(0, 255, 255); break; 195 | case Fuchsia: assign(255, 0, 255); break; 196 | case Green: assign(0, 128, 0); break; 197 | case Lime: assign(0, 255, 0); break; 198 | case Magenta: assign(255, 0, 255); break; 199 | case Orange: assign(255, 165, 0); break; 200 | case Purple: assign(128, 0, 128); break; 201 | case Red: assign(255, 0, 0); break; 202 | case Silver: assign(192, 192, 192); break; 203 | case White: assign(255, 255, 255); break; 204 | case Yellow: assign(255, 255, 0); break; 205 | default: transparent = true; break; 206 | } 207 | } 208 | virtual ~Color() { } 209 | std::string toString(Layout const &) const 210 | { 211 | std::stringstream ss; 212 | if (transparent) 213 | ss << "transparent"; 214 | else 215 | ss << "rgb(" << red << "," << green << "," << blue << ")"; 216 | return ss.str(); 217 | } 218 | private: 219 | bool transparent; 220 | int red; 221 | int green; 222 | int blue; 223 | 224 | void assign(int r, int g, int b) 225 | { 226 | red = r; 227 | green = g; 228 | blue = b; 229 | } 230 | }; 231 | 232 | class Fill : public Serializeable 233 | { 234 | public: 235 | Fill(Color::Defaults color) : color(color) { } 236 | Fill(Color color = Color::Transparent) 237 | : color(color) { } 238 | std::string toString(Layout const & layout) const 239 | { 240 | std::stringstream ss; 241 | ss << attribute("fill", color.toString(layout)); 242 | return ss.str(); 243 | } 244 | private: 245 | Color color; 246 | }; 247 | 248 | class Stroke : public Serializeable 249 | { 250 | public: 251 | Stroke(double width = -1, Color color = Color::Transparent) 252 | : width(width), color(color) { } 253 | std::string toString(Layout const & layout) const 254 | { 255 | // If stroke width is invalid. 256 | if (width < 0) 257 | return std::string(); 258 | 259 | std::stringstream ss; 260 | ss << attribute("stroke-width", translateScale(width, layout)) << attribute("stroke", color.toString(layout)); 261 | return ss.str(); 262 | } 263 | private: 264 | double width; 265 | Color color; 266 | }; 267 | 268 | class Font : public Serializeable 269 | { 270 | public: 271 | Font(double size = 12, std::string const & family = "Verdana") : size(size), family(family) { } 272 | std::string toString(Layout const & layout) const 273 | { 274 | std::stringstream ss; 275 | ss << attribute("font-size", translateScale(size, layout)) << attribute("font-family", family); 276 | return ss.str(); 277 | } 278 | private: 279 | double size; 280 | std::string family; 281 | }; 282 | 283 | class Shape : public Serializeable 284 | { 285 | public: 286 | Shape(Fill const & fill = Fill(), Stroke const & stroke = Stroke()) 287 | : fill(fill), stroke(stroke) { } 288 | virtual ~Shape() { } 289 | virtual std::string toString(Layout const & layout) const = 0; 290 | virtual void offset(Point const & offset) = 0; 291 | protected: 292 | Fill fill; 293 | Stroke stroke; 294 | }; 295 | template 296 | std::string vectorToString(std::vector collection, Layout const & layout) 297 | { 298 | std::string combination_str; 299 | for (unsigned i = 0; i < collection.size(); ++i) 300 | combination_str += collection[i].toString(layout); 301 | 302 | return combination_str; 303 | } 304 | 305 | class Circle : public Shape 306 | { 307 | public: 308 | Circle(Point const & center, double diameter, Fill const & fill, 309 | Stroke const & stroke = Stroke()) 310 | : Shape(fill, stroke), center(center), radius(diameter / 2) { } 311 | std::string toString(Layout const & layout) const 312 | { 313 | std::stringstream ss; 314 | ss << elemStart("circle") << attribute("cx", translateX(center.x, layout)) 315 | << attribute("cy", translateY(center.y, layout)) 316 | << attribute("r", translateScale(radius, layout)) << fill.toString(layout) 317 | << stroke.toString(layout) << emptyElemEnd(); 318 | return ss.str(); 319 | } 320 | void offset(Point const & offset) 321 | { 322 | center.x += offset.x; 323 | center.y += offset.y; 324 | } 325 | private: 326 | Point center; 327 | double radius; 328 | }; 329 | 330 | class Elipse : public Shape 331 | { 332 | public: 333 | Elipse(Point const & center, double width, double height, 334 | Fill const & fill = Fill(), Stroke const & stroke = Stroke()) 335 | : Shape(fill, stroke), center(center), radius_width(width / 2), 336 | radius_height(height / 2) { } 337 | std::string toString(Layout const & layout) const 338 | { 339 | std::stringstream ss; 340 | ss << elemStart("ellipse") << attribute("cx", translateX(center.x, layout)) 341 | << attribute("cy", translateY(center.y, layout)) 342 | << attribute("rx", translateScale(radius_width, layout)) 343 | << attribute("ry", translateScale(radius_height, layout)) 344 | << fill.toString(layout) << stroke.toString(layout) << emptyElemEnd(); 345 | return ss.str(); 346 | } 347 | void offset(Point const & offset) 348 | { 349 | center.x += offset.x; 350 | center.y += offset.y; 351 | } 352 | private: 353 | Point center; 354 | double radius_width; 355 | double radius_height; 356 | }; 357 | 358 | class Rectangle : public Shape 359 | { 360 | public: 361 | Rectangle(Point const & edge, double width, double height, 362 | Fill const & fill = Fill(), Stroke const & stroke = Stroke()) 363 | : Shape(fill, stroke), edge(edge), width(width), 364 | height(height) { } 365 | std::string toString(Layout const & layout) const 366 | { 367 | std::stringstream ss; 368 | ss << elemStart("rect") << attribute("x", translateX(edge.x, layout)) 369 | << attribute("y", translateY(edge.y, layout)) 370 | << attribute("width", translateScale(width, layout)) 371 | << attribute("height", translateScale(height, layout)) 372 | << fill.toString(layout) << stroke.toString(layout) << emptyElemEnd(); 373 | return ss.str(); 374 | } 375 | void offset(Point const & offset) 376 | { 377 | edge.x += offset.x; 378 | edge.y += offset.y; 379 | } 380 | private: 381 | Point edge; 382 | double width; 383 | double height; 384 | }; 385 | 386 | class Line : public Shape 387 | { 388 | public: 389 | Line(Point const & start_point, Point const & end_point, 390 | Stroke const & stroke = Stroke()) 391 | : Shape(Fill(), stroke), start_point(start_point), 392 | end_point(end_point) { } 393 | std::string toString(Layout const & layout) const 394 | { 395 | std::stringstream ss; 396 | ss << elemStart("line") << attribute("x1", translateX(start_point.x, layout)) 397 | << attribute("y1", translateY(start_point.y, layout)) 398 | << attribute("x2", translateX(end_point.x, layout)) 399 | << attribute("y2", translateY(end_point.y, layout)) 400 | << stroke.toString(layout) << emptyElemEnd(); 401 | return ss.str(); 402 | } 403 | void offset(Point const & offset) 404 | { 405 | start_point.x += offset.x; 406 | start_point.y += offset.y; 407 | 408 | end_point.x += offset.x; 409 | end_point.y += offset.y; 410 | } 411 | private: 412 | Point start_point; 413 | Point end_point; 414 | }; 415 | 416 | class Polygon : public Shape 417 | { 418 | public: 419 | Polygon(Fill const & fill = Fill(), Stroke const & stroke = Stroke()) 420 | : Shape(fill, stroke) { } 421 | Polygon(Stroke const & stroke = Stroke()) : Shape(Color::Transparent, stroke) { } 422 | Polygon & operator<<(Point const & point) 423 | { 424 | points.push_back(point); 425 | return *this; 426 | } 427 | std::string toString(Layout const & layout) const 428 | { 429 | std::stringstream ss; 430 | ss << elemStart("polygon"); 431 | 432 | ss << "points=\""; 433 | for (unsigned i = 0; i < points.size(); ++i) 434 | ss << translateX(points[i].x, layout) << "," << translateY(points[i].y, layout) << " "; 435 | ss << "\" "; 436 | 437 | ss << fill.toString(layout) << stroke.toString(layout) << emptyElemEnd(); 438 | return ss.str(); 439 | } 440 | void offset(Point const & offset) 441 | { 442 | for (unsigned i = 0; i < points.size(); ++i) { 443 | points[i].x += offset.x; 444 | points[i].y += offset.y; 445 | } 446 | } 447 | private: 448 | std::vector points; 449 | }; 450 | 451 | class Polyline : public Shape 452 | { 453 | public: 454 | Polyline(Fill const & fill = Fill(), Stroke const & stroke = Stroke()) 455 | : Shape(fill, stroke) { } 456 | Polyline(Stroke const & stroke = Stroke()) : Shape(Color::Transparent, stroke) { } 457 | Polyline(std::vector const & points, 458 | Fill const & fill = Fill(), Stroke const & stroke = Stroke()) 459 | : Shape(fill, stroke), points(points) { } 460 | Polyline & operator<<(Point const & point) 461 | { 462 | points.push_back(point); 463 | return *this; 464 | } 465 | std::string toString(Layout const & layout) const 466 | { 467 | std::stringstream ss; 468 | ss << elemStart("polyline"); 469 | 470 | ss << "points=\""; 471 | for (unsigned i = 0; i < points.size(); ++i) 472 | ss << translateX(points[i].x, layout) << "," << translateY(points[i].y, layout) << " "; 473 | ss << "\" "; 474 | 475 | ss << fill.toString(layout) << stroke.toString(layout) << emptyElemEnd(); 476 | return ss.str(); 477 | } 478 | void offset(Point const & offset) 479 | { 480 | for (unsigned i = 0; i < points.size(); ++i) { 481 | points[i].x += offset.x; 482 | points[i].y += offset.y; 483 | } 484 | } 485 | std::vector points; 486 | }; 487 | 488 | class Text : public Shape 489 | { 490 | public: 491 | Text(Point const & origin, std::string const & content, Fill const & fill = Fill(), 492 | Font const & font = Font(), Stroke const & stroke = Stroke()) 493 | : Shape(fill, stroke), origin(origin), content(content), font(font) { } 494 | std::string toString(Layout const & layout) const 495 | { 496 | std::stringstream ss; 497 | ss << elemStart("text") << attribute("x", translateX(origin.x, layout)) 498 | << attribute("y", translateY(origin.y, layout)) 499 | << fill.toString(layout) << stroke.toString(layout) << font.toString(layout) 500 | << ">" << content << elemEnd("text"); 501 | return ss.str(); 502 | } 503 | void offset(Point const & offset) 504 | { 505 | origin.x += offset.x; 506 | origin.y += offset.y; 507 | } 508 | private: 509 | Point origin; 510 | std::string content; 511 | Font font; 512 | }; 513 | 514 | // Sample charting class. 515 | class LineChart : public Shape 516 | { 517 | public: 518 | LineChart(Dimensions margin = Dimensions(), double scale = 1, 519 | Stroke const & axis_stroke = Stroke(.5, Color::Purple)) 520 | : axis_stroke(axis_stroke), margin(margin), scale(scale) { } 521 | LineChart & operator<<(Polyline const & polyline) 522 | { 523 | if (polyline.points.empty()) 524 | return *this; 525 | 526 | polylines.push_back(polyline); 527 | return *this; 528 | } 529 | std::string toString(Layout const & layout) const 530 | { 531 | if (polylines.empty()) 532 | return ""; 533 | 534 | std::string ret; 535 | for (unsigned i = 0; i < polylines.size(); ++i) 536 | ret += polylineToString(polylines[i], layout); 537 | 538 | return ret + axisString(layout); 539 | } 540 | void offset(Point const & offset) 541 | { 542 | for (unsigned i = 0; i < polylines.size(); ++i) 543 | polylines[i].offset(offset); 544 | } 545 | private: 546 | Stroke axis_stroke; 547 | Dimensions margin; 548 | double scale; 549 | std::vector polylines; 550 | 551 | optional getDimensions() const 552 | { 553 | if (polylines.empty()) 554 | return optional(); 555 | 556 | optional min = getMinPoint(polylines[0].points); 557 | optional max = getMaxPoint(polylines[0].points); 558 | for (unsigned i = 0; i < polylines.size(); ++i) { 559 | if (getMinPoint(polylines[i].points)->x < min->x) 560 | min->x = getMinPoint(polylines[i].points)->x; 561 | if (getMinPoint(polylines[i].points)->y < min->y) 562 | min->y = getMinPoint(polylines[i].points)->y; 563 | if (getMaxPoint(polylines[i].points)->x > max->x) 564 | max->x = getMaxPoint(polylines[i].points)->x; 565 | if (getMaxPoint(polylines[i].points)->y > max->y) 566 | max->y = getMaxPoint(polylines[i].points)->y; 567 | } 568 | 569 | return optional(Dimensions(max->x - min->x, max->y - min->y)); 570 | } 571 | std::string axisString(Layout const & layout) const 572 | { 573 | optional dimensions = getDimensions(); 574 | if (!dimensions) 575 | return ""; 576 | 577 | // Make the axis 10% wider and higher than the data points. 578 | double width = dimensions->width * 1.1; 579 | double height = dimensions->height * 1.1; 580 | 581 | // Draw the axis. 582 | Polyline axis(Color::Transparent, axis_stroke); 583 | axis << Point(margin.width, margin.height + height) << Point(margin.width, margin.height) 584 | << Point(margin.width + width, margin.height); 585 | 586 | return axis.toString(layout); 587 | } 588 | std::string polylineToString(Polyline const & polyline, Layout const & layout) const 589 | { 590 | Polyline shifted_polyline = polyline; 591 | shifted_polyline.offset(Point(margin.width, margin.height)); 592 | 593 | std::vector vertices; 594 | for (unsigned i = 0; i < shifted_polyline.points.size(); ++i) 595 | vertices.push_back(Circle(shifted_polyline.points[i], getDimensions()->height / 30.0, Color::Black)); 596 | 597 | return shifted_polyline.toString(layout) + vectorToString(vertices, layout); 598 | } 599 | }; 600 | 601 | class Document 602 | { 603 | public: 604 | Document(std::string const & file_name, Layout layout = Layout()) 605 | : file_name(file_name), layout(layout) { } 606 | 607 | Document & operator<<(Shape const & shape) 608 | { 609 | body_nodes_str += shape.toString(layout); 610 | return *this; 611 | } 612 | std::string toString() const 613 | { 614 | std::stringstream ss; 615 | ss << "\n\n\n" << body_nodes_str << elemEnd("svg"); 622 | return ss.str(); 623 | } 624 | bool save() const 625 | { 626 | std::ofstream ofs(file_name.c_str()); 627 | if (!ofs.good()) 628 | return false; 629 | 630 | ofs << toString(); 631 | ofs.close(); 632 | return true; 633 | } 634 | private: 635 | std::string file_name; 636 | Layout layout; 637 | 638 | std::string body_nodes_str; 639 | }; 640 | } 641 | 642 | #endif 643 | -------------------------------------------------------------------------------- /src/split_data.cc: -------------------------------------------------------------------------------- 1 | #include "split_data.h" 2 | 3 | SplitData::SplitData() 4 | {} 5 | 6 | /* 7 | SplitData::SplitData(Arrangement_2::Halfedge_handle _e, K dis1, K dis2) { 8 | e = _e; 9 | 10 | K ratio1 = dis2 / (dis2 - dis1); 11 | K ratio2 = -dis1 / (dis2 - dis1); 12 | 13 | split_point = Point_2(e->source()->point().x() * ratio1 + e->target()->point().x() * ratio2, 14 | e->source()->point().y() * ratio1 + e->target()->point().y() * ratio2); 15 | } 16 | */ 17 | SplitData::SplitData(Arrangement_2::Halfedge_handle _e, const Point_2& p) { 18 | e = _e; 19 | split_point = p; 20 | } 21 | 22 | K ComputeDepth(const PlaneParam& param, Arrangement_2::Halfedge_handle u, int source) { 23 | auto v_src = u->source(); 24 | auto v_tar = u->target(); 25 | auto& p = (source == 1) ? v_src->point() : v_tar->point(); 26 | return param.ComputeDepth(p); 27 | } 28 | 29 | void CollectSelfIntersection(Arrangement_2::Ccb_halfedge_circulator curr, 30 | int id1, int id2, 31 | const std::unordered_set& halfedges, 32 | const std::vector& plane_params, 33 | std::vector* p_split_points) { 34 | auto& split_points = *p_split_points; 35 | int current_size = split_points.size(); 36 | bool valid = true; 37 | auto origin = curr; 38 | 39 | auto f = curr->face(); 40 | LineSegment l; 41 | 42 | plane_params[id1].ProjectiveIntersection(plane_params[id2], &l); 43 | 44 | do { 45 | Arrangement_2::Halfedge_handle he = curr; 46 | if (halfedges.count(he) == 0) { 47 | valid = false; 48 | break; 49 | } 50 | //auto pp = he->source()->point(); 51 | //vs.push_back(Eigen::Vector3d(pp.x().convert_to(), pp.y().convert_to(), 0)); 52 | Point_2 p; 53 | if (l.Intersect(he->source()->point(), he->target()->point(), &p)) { 54 | split_points.push_back(SplitData(he, p)); 55 | } 56 | } while (++curr != origin); 57 | if (!valid) { 58 | split_points.resize(current_size); 59 | return; 60 | } 61 | //if (valid && split_points.size() % 2 == 1) { 62 | //printf("HHHH %d %d\n", id1, id2); 63 | //exit(0); 64 | //} 65 | if (!valid || split_points.size() % 2 == 1) { 66 | split_points.resize(current_size); 67 | } 68 | } 69 | 70 | void SortSplitPoint(std::vector* p_split_points) { 71 | auto& split_points = *p_split_points; 72 | 73 | std::sort(split_points.begin(), split_points.end()); 74 | 75 | for (int j = 0; j < split_points.size(); j += 2) { 76 | if (split_points[j].e->face() == split_points[j+1].e->face() || 77 | split_points[j].e->face() == split_points[j+1].e->twin()->face()) { 78 | split_points[j].e = split_points[j].e->twin(); 79 | } 80 | if (split_points[j].e->twin()->face() == split_points[j+1].e->twin()->face()) { 81 | split_points[j+1].e = split_points[j+1].e->twin(); 82 | } 83 | } 84 | 85 | for (int j = 0; j < split_points.size(); j += 2) 86 | split_points[j + 1].fid = split_points[j + 1].e->face()->data(); 87 | 88 | } 89 | 90 | void SubdivideAtIntersection(std::vector > & splits, Arrangement_2* pout) { 91 | auto& out = *pout; 92 | std::unordered_map > > edge_handles; 93 | for (int i = 0; i < splits.size(); ++i) { 94 | for (int j = 0; j < splits[i].size(); ++j) { 95 | auto it = edge_handles.find(splits[i][j].e); 96 | if (it != edge_handles.end()) { 97 | it->second.push_back(std::make_pair(i, j)); 98 | } 99 | else { 100 | it = edge_handles.find(splits[i][j].e->twin()); 101 | if (it != edge_handles.end()) { 102 | it->second.push_back(std::make_pair(i, j)); 103 | } else { 104 | std::vector > v; 105 | v.push_back(std::make_pair(i, j)); 106 | edge_handles[splits[i][j].e] = v; 107 | } 108 | } 109 | } 110 | } 111 | 112 | 113 | for (auto& info : edge_handles) { 114 | auto p = info.first->source()->point(); 115 | std::vector > dis(info.second.size()); 116 | for (int i = 0; i < info.second.size(); ++i) { 117 | auto e = info.second[i]; 118 | auto diff_p = splits[e.first][e.second].split_point - p; 119 | dis[i] = std::make_pair(diff_p.x() * diff_p.x() + diff_p.y() * diff_p.y(), i); 120 | } 121 | 122 | std::sort(dis.begin(), dis.end()); 123 | 124 | std::vector dis_id; 125 | for (int i = 0; i < dis.size(); ++i) { 126 | if (i > 0) { 127 | if (dis[i].first != dis[i - 1].first) { 128 | dis_id.push_back(i); 129 | } 130 | } else { 131 | dis_id.push_back(0); 132 | } 133 | } 134 | auto e_handle = info.first; 135 | for (int ii = dis_id.size() - 1; ii >= 0; --ii) { 136 | int i = dis_id[ii]; 137 | auto e = info.second[dis[i].second]; 138 | auto& split_point = splits[e.first][e.second].split_point; 139 | 140 | if (split_point == e_handle->source()->point()) { 141 | for (int j = 0; j <= ii; ++j) { 142 | int i = dis_id[j]; 143 | auto e = info.second[dis[i].second]; 144 | splits[e.first][e.second].v = e_handle->source(); 145 | } 146 | break; 147 | } 148 | else if (split_point == e_handle->target()->point()) { 149 | splits[e.first][e.second].v = e_handle->target(); 150 | continue; 151 | } 152 | Segment_2 s1(e_handle->source()->point(), split_point); 153 | Segment_2 s2(split_point, e_handle->target()->point()); 154 | 155 | e_handle = out.split_edge(e_handle, s1, s2); 156 | splits[e.first][e.second].v = e_handle->target(); 157 | } 158 | for (int i = 1; i < dis.size(); ++i) { 159 | if ((dis[i].first == dis[i - 1].first)) { 160 | auto e1 = info.second[dis[i - 1].second]; 161 | auto e = info.second[dis[i].second]; 162 | splits[e.first][e.second].v = splits[e1.first][e1.second].v; 163 | } 164 | } 165 | } 166 | 167 | for (int i = 0; i < splits.size(); ++i) { 168 | auto& points = splits[i]; 169 | for (int j = 0; j < points.size(); j += 2) { 170 | if (points[j].v->point() != points[j + 1].v->point()) { 171 | Segment_2 s(points[j].v->point(), points[j+1].v->point()); 172 | auto new_e = out.insert_at_vertices(s, points[j].v, points[j + 1].v); 173 | new_e->face()->set_data(points[j + 1].fid); 174 | new_e->twin()->face()->set_data(points[j + 1].fid); 175 | } 176 | } 177 | } 178 | } -------------------------------------------------------------------------------- /src/split_data.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTORGRAPH_RENDER_SPLIT_DATA_H_ 2 | #define VECTORGRAPH_RENDER_SPLIT_DATA_H_ 3 | 4 | #include "plane_param.h" 5 | #include "types.h" 6 | 7 | struct SplitData 8 | { 9 | public: 10 | SplitData(); 11 | //SplitData(Arrangement_2::Halfedge_handle _e, K dis1, K dis2); 12 | SplitData(Arrangement_2::Halfedge_handle _e, const Point_2& p); 13 | 14 | Arrangement_2::Halfedge_handle e; 15 | Arrangement_2::Vertex_handle v; 16 | long long fid; 17 | Point_2 split_point; 18 | bool trivial; 19 | 20 | // make this an inline funcion for fast computation 21 | K signature() const { 22 | return K(0.3245) * split_point.x() + K(0.6842) * split_point.y(); 23 | } 24 | 25 | // make this an inline funcion for fast computation 26 | bool operator<(const SplitData& n) const { 27 | return signature() < n.signature(); 28 | } 29 | }; 30 | 31 | K ComputeDepth(const PlaneParam& plane, Arrangement_2::Halfedge_handle u, int source = 1); 32 | 33 | void CollectSelfIntersection(Arrangement_2::Ccb_halfedge_circulator curr, 34 | int id1, int id2, 35 | const std::unordered_set& halfedges, 36 | const std::vector& plane_params, 37 | std::vector* p_split_points); 38 | 39 | void SortSplitPoint(std::vector* p_split_points); 40 | 41 | void SubdivideAtIntersection(std::vector >& splits, Arrangement_2* pout); 42 | 43 | #endif -------------------------------------------------------------------------------- /src/types.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTORGRAPH_RENDERER_TYPES_H_ 2 | #define VECTORGRAPH_RENDERER_TYPES_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | struct FaceIDPair 13 | { 14 | long long operator()( const long long& lhs, const long long& rhs ) const { 15 | return lhs * ((long long)(1 << 16)) * ((long long)(1 << 16)) + rhs; 16 | } 17 | }; 18 | 19 | typedef CGAL::Exact_rational K; 20 | typedef CGAL::Cartesian Kernel; 21 | typedef CGAL::Arr_segment_traits_2 Traits_2; 22 | typedef Traits_2::Point_2 Point_2; 23 | typedef Traits_2::X_monotone_curve_2 Segment_2; 24 | typedef CGAL::Arr_face_extended_dcel Dcel; 25 | typedef CGAL::Arrangement_2 Arrangement_2; 26 | typedef CGAL::Arr_face_overlay_traits Overlay_traits; 30 | 31 | 32 | #endif --------------------------------------------------------------------------------