├── .gitignore ├── surf ├── surfconfig.h.in ├── src │ ├── tools.cpp │ ├── surf.cpp │ ├── TriangulationUtils.h │ ├── BGLGraph.h │ ├── BGLGraph.cpp │ ├── WavefrontSupportingLine.h │ ├── BasicInput.cpp │ ├── EventQueue.h │ ├── BasicTriangulation.h │ ├── CollapseSpec.cpp │ ├── cgaltools.cpp │ ├── SkeletonStructure.h │ ├── SkeletonStructure.cpp │ └── BasicInput.h ├── test │ └── TestHeap.cpp ├── CMakeLists.txt └── include │ ├── tools.h │ └── surf.h ├── .gitmodules ├── .editorconfig ├── cmake └── tests │ └── test_CGAL__Intersection_of_constraints_exception.cpp ├── .travis.yml ├── gui ├── surfgui.pro ├── CMakeLists.txt ├── OffsetsGraphicsItem.h ├── InputGraphicsItem.h ├── OffsetsGraphicsItem.cpp ├── SkeletonGraphicsItem.h ├── surfgui.h ├── InputGraphicsItem.cpp ├── mainwindow.h ├── SkeletonGraphicsItem.cpp ├── main.cpp ├── MyQtThings.h └── KineticTriangulationGraphicsItem.h ├── test-data ├── convex03.graphml ├── special-cases │ ├── degree-one-multi1-000.graphml │ ├── degree-one-on-ch.graphml │ ├── same-speed-convex-hull-000.graphml │ ├── degree-one-interior-1.graphml │ ├── degree-one-interior-2.graphml │ ├── degree-one-interior-3.graphml │ ├── degree-one-simple-000.graphml │ ├── iso-simple-1.graphml │ ├── iso-simple-3.graphml │ ├── collinear-split-event.graphml │ ├── degree-one-multi2-000.graphml │ ├── spoke-1.graphml │ ├── spoke-2.graphml │ ├── spoke-3.graphml │ ├── iso-simple-2.graphml │ ├── degree-one-simple-002.graphml │ ├── degree-one-simple-003.graphml │ ├── degree-one-bevels-causing-splits.graphml │ ├── degree-one-simple-001.graphml │ ├── degree-one-multi3-000.graphml │ ├── degree-one-multi4-000.graphml │ ├── multi-same-speed-event.graphml │ └── degree-one-on-ch.ipe ├── convex04.graphml ├── issue2 │ ├── issue2-minimal1.ipe │ └── issue2-minimal1.graphml ├── convex07.graphml ├── convex10.graphml ├── convex19.graphml ├── srpg0000025.graphml ├── srpg_octo0000035.graphml └── srpg0000028.graphml ├── config.h.in ├── cc ├── CMakeLists.txt └── test │ └── RunTestData.cpp ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /build* 2 | tags 3 | held-data 4 | -------------------------------------------------------------------------------- /surf/surfconfig.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #cmakedefine HAVE_INTERSECTION_OF_CONSTRAINTS_EXCEPTION 4 | #cmakedefine HAVE_CGAL_NO_CONSTRAINT_INTERSECTION_TAG 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "surf/easyloggingpp"] 2 | path = surf/easyloggingpp 3 | url = https://github.com/cgalab/easyloggingpp 4 | [submodule "surf/getopt-for-windows"] 5 | path = surf/getopt-for-windows 6 | url = https://github.com/Chunde/getopt-for-windows 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # this should work for all editors that support .editorconfig! 2 | # 3 | # on debian, emacs users should install elpa-editorconfig and vim 4 | # users should install vim-editorconfig. 5 | 6 | root = true 7 | 8 | [*] 9 | indent_style = space 10 | trim_trailing_whitespace = true 11 | end_of_line = lf 12 | charset = utf-8 13 | 14 | [*.{c,h,cpp}] 15 | indent_size = 2 16 | 17 | [Makefile] 18 | indent_style = tab 19 | 20 | [*.am] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /cmake/tests/test_CGAL__Intersection_of_constraints_exception.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | using Kernel = CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt; 7 | using T = CGAL::Constrained_Delaunay_triangulation_2; 8 | 9 | int main(int, char**) { 10 | try { 11 | } catch (T::Intersection_of_constraints_exception &err) { 12 | } 13 | exit(0); 14 | } 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | cache: 4 | ccache: true 5 | 6 | os: 7 | - linux 8 | dist: bionic 9 | 10 | addons: 11 | apt: 12 | update: true 13 | packages: 14 | - cmake 15 | - libboost-graph-dev 16 | - libboost-iostreams-dev 17 | - libcgal-dev 18 | - libcgal-qt5-dev 19 | - libgtest-dev 20 | - googletest 21 | - libqt5opengl5-dev 22 | - libqt5svg5-dev 23 | - qtbase5-dev 24 | 25 | env: 26 | global: 27 | - MAKEFLAGS="-j 2" 28 | 29 | matrix: 30 | include: 31 | - compiler: clang 32 | env: BUILD_TYPE="Release" 33 | - compiler: clang 34 | env: BUILD_TYPE="Debug" 35 | - compiler: gcc 36 | env: BUILD_TYPE="Release" 37 | - compiler: gcc 38 | env: BUILD_TYPE="Debug" 39 | - compiler: gcc 40 | env: BUILD_TYPE="Release" NT_USE_DOUBLE=yes 41 | - compiler: gcc 42 | env: BUILD_TYPE="Debug" NT_USE_DOUBLE=yes 43 | 44 | script: 45 | - cmake -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" -DNT_USE_DOUBLE=${NT_USE_DOUBLE:-no} . 46 | - make 47 | - make test 48 | -------------------------------------------------------------------------------- /gui/surfgui.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2018-06-27T09:56:31 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = surfgui 12 | TEMPLATE = app 13 | 14 | # The following define makes your compiler emit warnings if you use 15 | # any feature of Qt which as been marked as deprecated (the exact warnings 16 | # depend on your compiler). Please consult the documentation of the 17 | # deprecated API in order to know how to port your code away from it. 18 | DEFINES += QT_DEPRECATED_WARNINGS 19 | 20 | # You can also make your code fail to compile if you use deprecated APIs. 21 | # In order to do so, uncomment the following line. 22 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 23 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 24 | 25 | 26 | SOURCES += main.cpp\ 27 | mainwindow.cpp 28 | 29 | HEADERS += mainwindow.h 30 | 31 | FORMS += mainwindow.ui 32 | 33 | INCLUDEPATH += ../surf/include ../surf/src 34 | -------------------------------------------------------------------------------- /gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (surfgui VERSION "${SURFVERSION}" DESCRIPTION "surfgui") 2 | 3 | find_package(Qt5Widgets REQUIRED) 4 | find_package(CGAL COMPONENTS Qt5 Core REQUIRED) 5 | include(${CGAL_USE_FILE}) 6 | 7 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTOUIC ON) 10 | 11 | add_executable(surfgui 12 | main.cpp 13 | mainwindow.cpp 14 | InputGraphicsItem.cpp 15 | KineticTriangulationGraphicsItem.cpp 16 | SkeletonGraphicsItem.cpp 17 | OffsetsGraphicsItem.cpp 18 | ) 19 | if ( MSVC ) 20 | target_sources(surfgui 21 | PRIVATE ../surf/getopt-for-windows/getopt.c ) 22 | target_include_directories(surfgui PRIVATE ../surf/getopt-for-windows ) 23 | endif() 24 | 25 | target_link_libraries(surfgui ${QT_LIBRARIES} Qt5::Widgets ) 26 | target_link_libraries(surfgui ${CGAL_LIBRARIES} ${CGAL_3RD_PARTY_LIBRARIES}) 27 | TARGET_LINK_LIBRARIES( surfgui 28 | surflib ) 29 | target_include_directories(surfgui PRIVATE ../surf/include) 30 | target_include_directories(surfgui PRIVATE ../surf/src) 31 | -------------------------------------------------------------------------------- /test-data/convex03.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -0.4016393443 9 | 0.9344262295 10 | 11 | 12 | 0.5983606557 13 | 0.7827868852 14 | 15 | 16 | -0.0204918033 17 | 1.3975409836 18 | 19 | 20 | 1.0 21 | 22 | 23 | 1.0 24 | 25 | 26 | 1.0 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test-data/special-cases/degree-one-multi1-000.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1.0 6 | 7 | 8 | 0.0 9 | 10 | 11 | 12 | 224.0 13 | 752.0 14 | 15 | 16 | 224.0 17 | 704.0 18 | 19 | 20 | 160.0 21 | 688.0 22 | 23 | 24 | 288.0 25 | 656.0 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /surf/src/tools.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "tools.h" 19 | 20 | #include 21 | #include 22 | 23 | static uint32_t rnd_seed = 123456789; 24 | static int seeded = false; 25 | 26 | void 27 | my_srand(uint32_t s) { 28 | rnd_seed = s; 29 | seeded = true; 30 | } 31 | 32 | uint32_t 33 | my_rand() { 34 | if (!seeded) { 35 | int seed = time(NULL); 36 | //LOG(DEBUG) << "seeding RNG with " << seed; 37 | my_srand(seed); 38 | }; 39 | rnd_seed = (1103515245 * rnd_seed + 12345); // % m; 40 | return rnd_seed; 41 | } 42 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** comparisons, when both arguments are actually equal, are often expensive. 4 | * This enables or disables the more expensive asserts of this kind. 5 | */ 6 | #cmakedefine DEBUG_EXPENSIVE_PREDICATES @DEBUG_EXPENSIVE_PREDICATES@ 7 | 8 | 9 | /** refine triangulation to avoid flip events sooner. 10 | */ 11 | #cmakedefine REFINE_TRIANGULATION 12 | 13 | 14 | /** do or do not compile with debug output. 15 | */ 16 | #cmakedefine DEBUG_OUTPUT 17 | 18 | /** include filename and line numbers in log output. 19 | */ 20 | #cmakedefine DEBUG_OUTPUT_WITH_FILES 21 | 22 | ///** Use two means to compute collapse times and event classification, and compare. 23 | // * 24 | // * only used in debug builds 25 | // */ 26 | ////#define DEBUG_ALL_COLLAPSE_TIMES_EXPENSIVE 27 | 28 | /** Do quick checks for collapse time correctness/classification 29 | * 30 | * only used in debug builds 31 | */ 32 | #cmakedefine DEBUG_COLLAPSE_TIMES 33 | 34 | 35 | /** The cmake build type, if we have it. 36 | */ 37 | #cmakedefine CMAKE_BUILD_TYPE "@UPPERCASE_CMAKE_BUILD_TYPE@" 38 | 39 | /** Our copy of -DNDEBUG, if we have it. Set via the cmake build type. 40 | */ 41 | #cmakedefine SURF_NDEBUG 42 | 43 | /** the version as supplied by CMake 44 | */ 45 | #cmakedefine VERSIONGIT "@VERSIONGIT@" 46 | 47 | /** Use double instead of CORE Expressions 48 | */ 49 | #cmakedefine NT_USE_DOUBLE 50 | 51 | /** Collect stats on heap stuff 52 | */ 53 | #cmakedefine HEAP_STATS 54 | -------------------------------------------------------------------------------- /test-data/convex04.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -0.5357142857 9 | -0.1363636364 10 | 11 | 12 | 0.4642857143 13 | 0.0162337662 14 | 15 | 16 | 0.4058441558 17 | 0.737012987 18 | 19 | 20 | -0.3571428571 21 | 0.5714285714 22 | 23 | 24 | 1.0 25 | 26 | 27 | 1.0 28 | 29 | 30 | 1.0 31 | 32 | 33 | 1.0 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /surf/src/surf.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "surf.h" 19 | 20 | INITIALIZE_EASYLOGGINGPP 21 | 22 | unsigned DBG_INDENT_CTR = 0; 23 | 24 | void 25 | setup_logging(int argc, char* argv[], bool debugLogs) { 26 | START_EASYLOGGINGPP(argc, argv); 27 | 28 | el::Configurations defaultConf; 29 | defaultConf.setGlobally(el::ConfigurationType::Format, "%datetime{%H%m%s.%g} %levshort %msg"); 30 | if (! debugLogs) { 31 | defaultConf.set(el::Level::Debug, el::ConfigurationType::Enabled, "false"); 32 | } 33 | el::Loggers::reconfigureAllLoggers(defaultConf); 34 | el::Loggers::addFlag( el::LoggingFlag::ColoredTerminalOutput ); 35 | } 36 | 37 | #ifdef HEAP_STATS 38 | unsigned heap_eq_ctr = 0; 39 | #endif 40 | -------------------------------------------------------------------------------- /test-data/special-cases/degree-one-on-ch.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 288.0 14 | 800.0 15 | 16 | 17 | 224.0 18 | 736.0 19 | 20 | 21 | 272.0 22 | 688.0 23 | 24 | 25 | 320.0 26 | 736.0 27 | 28 | 29 | 416.0 30 | 672.0 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /test-data/special-cases/same-speed-convex-hull-000.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1.0 6 | 7 | 8 | 0.0 9 | 10 | 11 | 12 | 128.0 13 | 768.0 14 | 15 | 16 | 128.0 17 | 704.0 18 | 19 | 20 | 176.0 21 | 704.0 22 | 23 | 24 | 192.0 25 | 640.0 26 | 27 | 28 | 192.0 29 | 576.0 30 | 31 | 32 | 320.0 33 | 576.0 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /test-data/issue2/issue2-minimal1.ipe: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 0.6 0 0 0.6 0 0 e 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 1268 202 m 27 | 1268 199 l 28 | 29 | 30 | 1268 199 m 31 | 1269 198 l 32 | 33 | 34 | 213 454 m 35 | 213 448 l 36 | 37 | 38 | 213 448 m 39 | 223 448 l 40 | 41 | 42 | 1279 192 m 43 | 1280 193 l 44 | 45 | 46 | 224 449 m 47 | 233 449 l 48 | 49 | 50 | 233 449 m 51 | 233 469 l 52 | 53 | 54 | 233 469 m 55 | 213 469 l 56 | 57 | 58 | 213 469 m 59 | 213 463 l 60 | 61 | 62 | 213 463 m 63 | 198 463 l 64 | 65 | 66 | 198 463 m 67 | 198 458 l 68 | 69 | 70 | 198 458 m 71 | 212 458 l 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /gui/OffsetsGraphicsItem.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | #include "surfgui.h" 21 | #include "SkeletonDCEL.h" 22 | 23 | class OffsetsGraphicsItem : 24 | public CGAL::Qt::GraphicsItem 25 | { 26 | private: 27 | using Base = CGAL::Qt::GraphicsItem; 28 | 29 | private: 30 | std::vector offsets; 31 | QPen segments_pen; 32 | 33 | protected: 34 | QRectF bounding_rect; 35 | void updateBoundingBox(); 36 | 37 | public: 38 | OffsetsGraphicsItem(); 39 | 40 | QRectF boundingRect() const { return bounding_rect; }; 41 | 42 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); 43 | void set_offsets(std::vector&& o) { 44 | offsets.swap(o); 45 | modelChanged(); 46 | } 47 | 48 | void setSegmentsPen(const QPen& pen) { segments_pen = pen; }; 49 | const QPen& segmentsPen() const { return segments_pen; } 50 | 51 | void modelChanged(); 52 | }; 53 | -------------------------------------------------------------------------------- /test-data/special-cases/degree-one-interior-1.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 336.0 14 | 752.0 15 | 16 | 17 | 272.0 18 | 752.0 19 | 20 | 21 | 288.0 22 | 800.0 23 | 24 | 25 | 224.0 26 | 736.0 27 | 28 | 29 | 272.0 30 | 688.0 31 | 32 | 33 | 320.0 34 | 736.0 35 | 36 | 37 | 416.0 38 | 800.0 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /surf/src/TriangulationUtils.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | #include "surf.h" 21 | 22 | #include "cgaltools.h" 23 | 24 | template 25 | class TriangulationUtilsStatic { 26 | //protected: 27 | public: 28 | static const unsigned _cw[3]; 29 | static const unsigned _ccw[3]; 30 | static const unsigned _mod3[5]; 31 | }; 32 | 33 | template 34 | const unsigned TriangulationUtilsStatic::_cw[3] = {2, 0, 1}; 35 | 36 | template 37 | const unsigned TriangulationUtilsStatic::_ccw[3] = {1, 2, 0}; 38 | 39 | template 40 | const unsigned TriangulationUtilsStatic::_mod3[5] = {0, 1, 2, 0, 1}; 41 | 42 | class TriangulationUtils : public TriangulationUtilsStatic<> { 43 | public: 44 | static inline unsigned cw(int i) { 45 | SRF_precondition(0<=i && i<3); 46 | return _cw[i]; 47 | } 48 | static inline unsigned ccw(int i) { 49 | SRF_precondition(0<=i && i<3); 50 | return _ccw[i]; 51 | } 52 | static inline unsigned mod3(int i) { 53 | SRF_precondition(0<=i && i<6); 54 | return _mod3[i]; 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /test-data/special-cases/degree-one-interior-2.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 336.0 14 | 752.0 15 | 16 | 17 | 272.0 18 | 752.0 19 | 20 | 21 | 272.0 22 | 800.0 23 | 24 | 25 | 224.0 26 | 736.0 27 | 28 | 29 | 288.0 30 | 704.0 31 | 32 | 33 | 320.0 34 | 736.0 35 | 36 | 37 | 416.0 38 | 800.0 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /test-data/special-cases/degree-one-interior-3.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 336.0 14 | 752.0 15 | 16 | 17 | 272.0 18 | 752.0 19 | 20 | 21 | 272.0 22 | 800.0 23 | 24 | 25 | 224.0 26 | 736.0 27 | 28 | 29 | 272.0 30 | 704.0 31 | 32 | 33 | 320.0 34 | 736.0 35 | 36 | 37 | 416.0 38 | 800.0 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /cc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (surfer VERSION "${SURFVERSION}" DESCRIPTION "surfer") 2 | 3 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 4 | 5 | find_package( Boost REQUIRED iostreams ) 6 | if ( NOT Boost_FOUND ) 7 | message(STATUS "Boost not found.") 8 | return() 9 | endif() 10 | include_directories(${Boost_INCLUDE_DIRS}) 11 | 12 | add_executable(surfer 13 | main.cpp ) 14 | 15 | 16 | if ( MSVC ) 17 | target_sources(surfer 18 | PRIVATE ../surf/getopt-for-windows/getopt.c ) 19 | target_include_directories(surfer PRIVATE ../surf/getopt-for-windows ) 20 | endif() 21 | 22 | target_link_libraries(surfer surflib) 23 | target_link_libraries(surfer ${Boost_LIBRARIES} ) 24 | 25 | target_include_directories(surfer PRIVATE ../surf/include) 26 | target_include_directories(surfer PRIVATE ../surf/src) 27 | 28 | #### TESTING ## 29 | if(TEST_SUITE) 30 | set( TEST_BINARY_NAME runtests_cc ) 31 | include(GoogleTest) 32 | add_executable( ${TEST_BINARY_NAME} 33 | test/RunTestData.cpp 34 | main.cpp 35 | ) 36 | gtest_add_tests(TARGET ${TEST_BINARY_NAME}) 37 | set_target_properties(${TEST_BINARY_NAME} PROPERTIES PRIVATE "-Wnofloat-equal") 38 | target_compile_definitions(${TEST_BINARY_NAME} PRIVATE -DCMAKE_SOURCE_DIR="${CMAKE_SOURCE_DIR}" ) 39 | target_compile_definitions(${TEST_BINARY_NAME} PRIVATE -DSURF_TEST_SUITE=1 ) 40 | 41 | target_include_directories(${TEST_BINARY_NAME} PRIVATE ../surf/include) 42 | target_include_directories(${TEST_BINARY_NAME} PRIVATE ../surf/src) 43 | target_include_directories(${TEST_BINARY_NAME} PRIVATE test) 44 | target_link_libraries(${TEST_BINARY_NAME} gtest gtest_main pthread ) 45 | target_link_libraries(${TEST_BINARY_NAME} surflib ) 46 | target_link_libraries(${TEST_BINARY_NAME} stdc++fs ) 47 | target_link_libraries(${TEST_BINARY_NAME} ${Boost_LIBRARIES} ) 48 | 49 | target_compile_options(${TEST_BINARY_NAME} PRIVATE "-Wno-unused-function") 50 | ENDIF() 51 | -------------------------------------------------------------------------------- /surf/src/BGLGraph.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | #include "surf.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | struct BGLVertexPropertyType { 27 | std::string x, y; 28 | }; 29 | 30 | struct BGLEdgePropertyType { 31 | std::string weight; 32 | std::string weight_additive; 33 | }; 34 | 35 | class BGLGraph : public boost::adjacency_list { 36 | private: 37 | using Base = boost::adjacency_list; 38 | 39 | boost::dynamic_properties dp; 40 | 41 | public: 42 | typedef typename Base::vertex_property_type vertex_property_type; 43 | typedef typename Base::vertex_descriptor vertex_descriptor; 44 | 45 | public: 46 | BGLGraph() 47 | : Base() 48 | , dp(boost::ignore_other_properties) 49 | {}; 50 | 51 | static BGLGraph create_from_graphml(std::istream &istream); 52 | 53 | friend std::ostream& operator<<(std::ostream& os, const BGLGraph& e); 54 | }; 55 | 56 | -------------------------------------------------------------------------------- /test-data/special-cases/degree-one-simple-000.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1.0 6 | 7 | 8 | 0.0 9 | 10 | 11 | 12 | 126.974 13 | 811.424 14 | 15 | 16 | 93.0688 17 | 790.815 18 | 19 | 20 | 79.7725 21 | 734.97 22 | 23 | 24 | 132.958 25 | 697.74 26 | 27 | 28 | 166.863 29 | 734.97 30 | 31 | 32 | 180.825 33 | 761.563 34 | 35 | 36 | 161.545 37 | 798.128 38 | 39 | 40 | 283.071 41 | 758.831 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /test-data/special-cases/iso-simple-1.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 192.0 14 | 768.0 15 | 16 | 17 | 192.0 18 | 704.0 19 | 20 | 21 | 160.0 22 | 704.0 23 | 24 | 25 | 160.0 26 | 672.0 27 | 28 | 29 | 192.0 30 | 672.0 31 | 32 | 33 | 192.0 34 | 608.0 35 | 36 | 37 | 320.0 38 | 608.0 39 | 40 | 41 | 320.0 42 | 768.0 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /test-data/special-cases/iso-simple-3.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 240.0 14 | 768.0 15 | 16 | 17 | 240.0 18 | 704.0 19 | 20 | 21 | 192.0 22 | 704.0 23 | 24 | 25 | 192.0 26 | 640.0 27 | 28 | 29 | 160.0 30 | 640.0 31 | 32 | 33 | 160.0 34 | 608.0 35 | 36 | 37 | 320.0 38 | 608.0 39 | 40 | 41 | 320.0 42 | 768.0 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /surf/src/BGLGraph.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "BGLGraph.h" 19 | 20 | BGLGraph 21 | BGLGraph:: 22 | create_from_graphml(std::istream &istream) { 23 | typedef BGLGraph::vertex_property_type VertexPropertyType; 24 | typedef BGLGraph::edge_property_type EdgePropertyType; 25 | 26 | BGLGraph graph; 27 | graph.dp.property("vertex-coordinate-x", boost::get(&VertexPropertyType::x, graph)); 28 | graph.dp.property("vertex-coordinate-y", boost::get(&VertexPropertyType::y, graph)); 29 | graph.dp.property("edge-weight", boost::get(&EdgePropertyType::weight, graph)); 30 | graph.dp.property("edge-weight-additive", boost::get(&EdgePropertyType::weight_additive, graph)); 31 | 32 | boost::read_graphml(istream, graph, graph.dp); 33 | return graph; 34 | } 35 | 36 | 37 | std::ostream& 38 | operator<<(std::ostream& os, const BGLGraph& g) { 39 | typedef BGLGraph::vertex_descriptor VertexType; 40 | auto index_map = boost::get(boost::vertex_index, g); 41 | 42 | os << "vertices(g):" << std::endl; 43 | for (auto vp = boost::vertices(g); vp.first != vp.second; ++vp.first) { 44 | const VertexType v = *vp.first; 45 | os << index_map[v] << " " << g[v].x << " " << g[v].y << std::endl; 46 | } 47 | return os; 48 | } 49 | -------------------------------------------------------------------------------- /test-data/special-cases/collinear-split-event.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 128.0 14 | 704.0 15 | 16 | 17 | 128.0 18 | 576.0 19 | 20 | 21 | 448.0 22 | 576.0 23 | 24 | 25 | 448.0 26 | 752.0 27 | 28 | 29 | 384.0 30 | 752.0 31 | 32 | 33 | 384.0 34 | 592.0 35 | 36 | 37 | 240.0 38 | 592.0 39 | 40 | 41 | 240.0 42 | 704.0 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /test-data/convex07.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -0.5386533666 9 | 0.1970074813 10 | 11 | 12 | -0.6658354115 13 | -0.1346633416 14 | 15 | 16 | -0.536159601 17 | -0.5985037406 18 | 19 | 20 | -0.0498753117 21 | -0.6982543641 22 | 23 | 24 | 0.2294264339 25 | -0.2369077307 26 | 27 | 28 | 0.0897755611 29 | 0.1546134663 30 | 31 | 32 | -0.2967581047 33 | 0.3017456359 34 | 35 | 36 | 1.0 37 | 38 | 39 | 1.0 40 | 41 | 42 | 1.0 43 | 44 | 45 | 1.0 46 | 47 | 48 | 1.0 49 | 50 | 51 | 1.0 52 | 53 | 54 | 1.0 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /gui/InputGraphicsItem.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | #include "surfgui.h" 21 | #include "BasicInput.h" 22 | 23 | class InputGraphicsItem : 24 | public CGAL::Qt::GraphicsItem 25 | { 26 | private: 27 | using Base = CGAL::Qt::GraphicsItem; 28 | 29 | private: 30 | const BasicInput * const input; 31 | QPen vertices_pen; 32 | QPen segments_pen; 33 | QPen labels_pen; 34 | bool visible_labels = false; 35 | 36 | 37 | protected: 38 | QRectF bounding_rect; 39 | void updateBoundingBox(); 40 | 41 | public: 42 | InputGraphicsItem(const BasicInput * const input); 43 | 44 | QRectF boundingRect() const { return bounding_rect; }; 45 | 46 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); 47 | 48 | void setVerticesPen(const QPen& pen) { vertices_pen = pen; }; 49 | void setSegmentsPen(const QPen& pen) { segments_pen = pen; }; 50 | void setLabelsPen(const QPen& pen) { labels_pen = pen; }; 51 | const QPen& verticesPen() const { return vertices_pen; } 52 | const QPen& segmentsPen() const { return segments_pen; } 53 | const QPen& labelsPen() const { return labels_pen; } 54 | void setVisibleLabels(bool visible) { if (visible_labels != visible) { prepareGeometryChange(); }; visible_labels = visible; } 55 | 56 | void modelChanged(); 57 | }; 58 | -------------------------------------------------------------------------------- /gui/OffsetsGraphicsItem.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2015 -- 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "OffsetsGraphicsItem.h" 24 | 25 | OffsetsGraphicsItem:: 26 | OffsetsGraphicsItem() 27 | : Base() 28 | , segments_pen(QPen(::Qt::gray, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)) 29 | { 30 | modelChanged(); 31 | setZValue(3); 32 | } 33 | 34 | void 35 | OffsetsGraphicsItem:: 36 | paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { 37 | PainterOstream painterostream(painter); 38 | 39 | painter->setPen(segmentsPen()); 40 | for (const auto& family : offsets) { 41 | for (const auto& segment : family) { 42 | painterostream << segment; 43 | } 44 | } 45 | } 46 | 47 | void 48 | OffsetsGraphicsItem:: 49 | updateBoundingBox() { 50 | Converter convert; 51 | prepareGeometryChange(); 52 | 53 | if (offsets.size() == 0) { 54 | return; 55 | } 56 | 57 | if (offsets[0].size() == 0) { 58 | return; 59 | } 60 | 61 | auto bb = GuiSegment(offsets[0][0]).bbox(); 62 | for (const auto& family : offsets) { 63 | for (const auto& segments : family) { 64 | bb += GuiSegment(segments).bbox(); 65 | } 66 | } 67 | 68 | bounding_rect = convert(bb); 69 | } 70 | 71 | void 72 | OffsetsGraphicsItem:: 73 | modelChanged() { 74 | prepareGeometryChange(); 75 | updateBoundingBox(); 76 | } 77 | -------------------------------------------------------------------------------- /test-data/special-cases/degree-one-multi2-000.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1.0 6 | 7 | 8 | 0.0 9 | 10 | 11 | 12 | 224.0 13 | 672.0 14 | 15 | 16 | 192.0 17 | 640.0 18 | 19 | 20 | 208.0 21 | 608.0 22 | 23 | 24 | 272.0 25 | 592.0 26 | 27 | 28 | 272.0 29 | 640.0 30 | 31 | 32 | 240.0 33 | 720.0 34 | 35 | 36 | 224.0 37 | 736.0 38 | 39 | 40 | 336.0 41 | 656.0 42 | 43 | 44 | 320.0 45 | 544.0 46 | 47 | 48 | 128.0 49 | 688.0 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /test-data/special-cases/spoke-1.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 128.0 14 | 752.0 15 | 16 | 17 | 128.0 18 | 640.0 19 | 20 | 21 | 192.0 22 | 640.0 23 | 24 | 25 | 128.0 26 | 592.0 27 | 28 | 29 | 128.0 30 | 544.0 31 | 32 | 33 | 288.0 34 | 544.0 35 | 36 | 37 | 288.0 38 | 592.0 39 | 40 | 41 | 224.0 42 | 640.0 43 | 44 | 45 | 288.0 46 | 640.0 47 | 48 | 49 | 288.0 50 | 752.0 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /test-data/special-cases/spoke-2.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 128.0 14 | 752.0 15 | 16 | 17 | 128.0 18 | 688.0 19 | 20 | 21 | 192.0 22 | 640.0 23 | 24 | 25 | 128.0 26 | 592.0 27 | 28 | 29 | 128.0 30 | 544.0 31 | 32 | 33 | 288.0 34 | 544.0 35 | 36 | 37 | 288.0 38 | 592.0 39 | 40 | 41 | 224.0 42 | 640.0 43 | 44 | 45 | 288.0 46 | 688.0 47 | 48 | 49 | 288.0 50 | 752.0 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /test-data/special-cases/spoke-3.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 128.0 14 | 752.0 15 | 16 | 17 | 128.0 18 | 608.0 19 | 20 | 21 | 160.0 22 | 576.0 23 | 24 | 25 | 192.0 26 | 576.0 27 | 28 | 29 | 192.0 30 | 544.0 31 | 32 | 33 | 288.0 34 | 544.0 35 | 36 | 37 | 288.0 38 | 608.0 39 | 40 | 41 | 224.0 42 | 608.0 43 | 44 | 45 | 224.0 46 | 656.0 47 | 48 | 49 | 288.0 50 | 752.0 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /test-data/special-cases/iso-simple-2.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 288.0 14 | 608.0 15 | 16 | 17 | 288.0 18 | 672.0 19 | 20 | 21 | 320.0 22 | 672.0 23 | 24 | 25 | 320.0 26 | 704.0 27 | 28 | 29 | 288.0 30 | 704.0 31 | 32 | 33 | 288.0 34 | 768.0 35 | 36 | 37 | 176.0 38 | 768.0 39 | 40 | 41 | 176.0 42 | 624.0 43 | 44 | 45 | 192.0 46 | 624.0 47 | 48 | 49 | 192.0 50 | 608.0 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /test-data/special-cases/degree-one-simple-002.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1.0 6 | 7 | 8 | 0.0 9 | 10 | 11 | 12 | 126.974 13 | 811.424 14 | 15 | 16 | 93.0688 17 | 790.815 18 | 19 | 20 | 79.7725 21 | 734.97 22 | 23 | 24 | 132.958 25 | 697.74 26 | 27 | 28 | 166.863 29 | 734.97 30 | 31 | 32 | 180.825 33 | 761.563 34 | 35 | 36 | 161.545 37 | 798.128 38 | 39 | 40 | 283.071 41 | 758.831 42 | 43 | 44 | 266.1771 45 | 834.849122 46 | 47 | 48 | 247.17310000000003 49 | 807.398122 50 | 51 | 52 | 333.75010000000003 53 | 797.544122 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /surf/src/WavefrontSupportingLine.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | #include "surf.h" 21 | 22 | #include "cgaltools.h" 23 | #include "tools.h" 24 | 25 | #include 26 | 27 | class WavefrontSupportingLine { 28 | private: 29 | using Transformation = CGAL::Aff_transformation_2; 30 | public: 31 | const Line_2 l; 32 | const NT weight; 33 | const Vector_2 normal_direction; /* arbitrary length */ 34 | const Vector_2 normal_unit; /* unit length */ 35 | const Vector_2 normal; /* weighted */ 36 | public: 37 | WavefrontSupportingLine(const Point_2 &u, const Point_2 &v, const NT &p_weight) 38 | : l(u, v) 39 | , weight(p_weight) 40 | , normal_direction(l.to_vector().perpendicular(CGAL::COUNTERCLOCKWISE)) 41 | , normal_unit(normal_direction / CGAL::sqrt(normal_direction.squared_length())) 42 | , normal(normal_unit * weight) 43 | { }; 44 | 45 | WavefrontSupportingLine(const Line_2 &p_l, const NT &p_weight) 46 | : l(p_l) 47 | , weight(p_weight) 48 | , normal_direction(l.to_vector().perpendicular(CGAL::COUNTERCLOCKWISE)) 49 | , normal_unit(normal_direction / CGAL::sqrt(normal_direction.squared_length())) 50 | , normal(normal_unit * weight) 51 | { }; 52 | 53 | Line_2 line_at_one() const { 54 | Transformation translate(CGAL::TRANSLATION, normal); 55 | return Line_2(translate(l)); 56 | } 57 | 58 | Line_2 line_at(const NT& now) const { 59 | Transformation translate(CGAL::TRANSLATION, now * normal); 60 | return Line_2(translate(l)); 61 | } 62 | }; 63 | 64 | using WavefrontSupportingLineList = FixedVector; 65 | -------------------------------------------------------------------------------- /gui/SkeletonGraphicsItem.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | #include "surfgui.h" 21 | #include "SkeletonDCEL.h" 22 | 23 | class SkeletonGraphicsItem : 24 | public CGAL::Qt::GraphicsItem 25 | { 26 | private: 27 | using Base = CGAL::Qt::GraphicsItem; 28 | 29 | private: 30 | const SkeletonDCEL * const skeleton; 31 | QPen vertices_pen; 32 | QPen input_segments_pen; 33 | QPen segments_pen; 34 | QPen rays_pen; 35 | QPen labels_pen; 36 | bool visible_labels = false; 37 | 38 | 39 | protected: 40 | QRectF bounding_rect; 41 | void updateBoundingBox(); 42 | 43 | public: 44 | SkeletonGraphicsItem(const SkeletonDCEL * const skeleton); 45 | 46 | QRectF boundingRect() const { return bounding_rect; }; 47 | 48 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); 49 | 50 | void setVerticesPen(const QPen& pen) { vertices_pen = pen; }; 51 | void setInputSegmentsPen(const QPen& pen) { input_segments_pen = pen; }; 52 | void setSegmentsPen(const QPen& pen) { segments_pen = pen; }; 53 | void setRaysPen(const QPen& pen) { rays_pen = pen; }; 54 | void setLabelsPen(const QPen& pen) { labels_pen = pen; }; 55 | const QPen& verticesPen() const { return vertices_pen; } 56 | const QPen& inputSegmentsPen() const { return input_segments_pen; } 57 | const QPen& segmentsPen() const { return segments_pen; } 58 | const QPen& raysPen() const { return rays_pen; } 59 | const QPen& labelsPen() const { return labels_pen; } 60 | void setVisibleLabels(bool visible) { if (visible_labels != visible) { prepareGeometryChange(); }; visible_labels = visible; } 61 | 62 | void modelChanged(); 63 | }; 64 | -------------------------------------------------------------------------------- /test-data/special-cases/degree-one-simple-003.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1.0 6 | 7 | 8 | 0.0 9 | 10 | 11 | 12 | 126.974 13 | 811.424 14 | 15 | 16 | 93.0688 17 | 790.815 18 | 19 | 20 | 79.7725 21 | 734.97 22 | 23 | 24 | 132.958 25 | 697.74 26 | 27 | 28 | 166.863 29 | 734.97 30 | 31 | 32 | 180.825 33 | 761.563 34 | 35 | 36 | 161.545 37 | 798.128 38 | 39 | 40 | 283.071 41 | 758.831 42 | 43 | 44 | 312.633 45 | 723.637 46 | 47 | 48 | 277.439 49 | 706.04 50 | 51 | 52 | 310.522 53 | 682.812 54 | 55 | 56 | 359.089 57 | 702.521 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /test-data/issue2/issue2-minimal1.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 212.0 14 | 458.0 15 | 16 | 17 | 212.0 18 | 455.0 19 | 20 | 21 | 213.0 22 | 454.0 23 | 24 | 25 | 213.0 26 | 448.0 27 | 28 | 29 | 223.0 30 | 448.0 31 | 32 | 33 | 224.0 34 | 449.0 35 | 36 | 37 | 233.0 38 | 449.0 39 | 40 | 41 | 233.0 42 | 469.0 43 | 44 | 45 | 213.0 46 | 469.0 47 | 48 | 49 | 213.0 50 | 463.0 51 | 52 | 53 | 198.0 54 | 463.0 55 | 56 | 57 | 198.0 58 | 458.0 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /gui/surfgui.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2020 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include "cgaltools.h" 28 | using SurfKernel = Kernel; 29 | 30 | #include 31 | using GuiKernel = CGAL::Simple_cartesian; 32 | 33 | /* We might want to look into guarding this with 34 | * SurfKernel != GuiKernel, so we only overload this 35 | * when/if the Kernel is actually a Core::Expr kernel or something like that. 36 | * 37 | * If the kernel already is a double cartesian, then we could simply do 38 | * using PainterOstream = CGAL::Qt::PainterOstream; 39 | * here. 40 | */ 41 | class GuiPoint : public GuiKernel::Point_2 { 42 | private: 43 | using Base = GuiKernel::Point_2; 44 | public: 45 | GuiPoint(const SurfKernel::Point_2& p) 46 | : Base( 47 | CGAL::to_double(p.x()), 48 | CGAL::to_double(p.y()) 49 | ) 50 | {}; 51 | }; 52 | 53 | class GuiSegment : public GuiKernel::Segment_2 { 54 | private: 55 | using Base = GuiKernel::Segment_2; 56 | public: 57 | GuiSegment(const SurfKernel::Segment_2& s) 58 | : Base( GuiPoint(s.source()), GuiPoint(s.target())) 59 | {}; 60 | }; 61 | 62 | class PainterOstream : public CGAL::Qt::PainterOstream { 63 | private: 64 | using Base = CGAL::Qt::PainterOstream; 65 | public: 66 | PainterOstream(QPainter* p) : Base(p) {}; 67 | 68 | inline PainterOstream& operator<<(const GuiKernel::Segment_2& s) { 69 | Base::operator<<(s); 70 | return *this; 71 | } 72 | inline PainterOstream& operator<<(const SurfKernel::Segment_2& s) { 73 | GuiSegment g(s); 74 | Base::operator<<(g); 75 | return *this; 76 | } 77 | }; 78 | 79 | 80 | using Converter = CGAL::Qt::Converter; 81 | using GConverter = CGAL::Qt::Converter; 82 | -------------------------------------------------------------------------------- /cc/test/RunTestData.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2015 -- 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "gtest/gtest.h" 19 | 20 | #include 21 | #include 22 | 23 | /* defined in main.cpp when SURF_TEST_SUITE is defined */ 24 | int run_surfer(int argc, char *argv[]); 25 | 26 | std::vector input_files; 27 | 28 | class SurferRunTest : 29 | public ::testing::TestWithParam {}; 30 | 31 | 32 | static int 33 | call_surfer(std::vector args) { 34 | std::vector cmdv; 35 | 36 | std::transform(args.begin(), args.end(), std::back_inserter(cmdv), 37 | [](std::string &s) { return s.c_str(); }); 38 | char **cmd = const_cast(cmdv.data()); 39 | 40 | std::cout << "Running "; 41 | for (auto a : cmdv) { 42 | std::cout << a << " "; 43 | }; 44 | std::cout << std::endl; 45 | 46 | 47 | int ret = run_surfer(cmdv.size(), &cmd[0]); 48 | exit(ret); 49 | } 50 | 51 | TEST_P(SurferRunTest, Run) { 52 | std::vector args; 53 | 54 | args.push_back( /* */ "surfer"); 55 | args.push_back(GetParam()); 56 | args.push_back("/dev/null"); 57 | 58 | EXPECT_EXIT(call_surfer(args), ::testing::ExitedWithCode(0), "All done."); 59 | } 60 | 61 | INSTANTIATE_TEST_CASE_P(SurferRunTests, 62 | SurferRunTest, 63 | ::testing::ValuesIn(input_files), 64 | ); 65 | 66 | static bool 67 | has_suffix(const std::string &path, const std::string &suffix) { 68 | if (suffix.size() > path.size()) return false; 69 | return path.compare(path.size() - suffix.size(), suffix.size(), suffix) == 0; 70 | } 71 | 72 | int main(int argc, char **argv) { 73 | 74 | for(auto& p : std::filesystem::recursive_directory_iterator(CMAKE_SOURCE_DIR "/test-data")) { 75 | if (has_suffix(p.path(), ".graphml")) { 76 | input_files.push_back(p.path()); 77 | } 78 | } 79 | testing::InitGoogleTest(&argc, argv); 80 | return RUN_ALL_TESTS(); 81 | } 82 | -------------------------------------------------------------------------------- /test-data/convex10.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -0.5954915028 9 | -0.6571638901 10 | 11 | 12 | -0.8454915028 13 | -0.4755282581 14 | 15 | 16 | -1.1545084972 17 | -0.4755282581 18 | 19 | 20 | -1.4045084972 21 | -0.6571638901 22 | 23 | 24 | -1.5 25 | -0.9510565163 26 | 27 | 28 | -1.4045084972 29 | -1.2449491424 30 | 31 | 32 | -1.1545084972 33 | -1.4265847744 34 | 35 | 36 | -0.8454915028 37 | -1.4265847744 38 | 39 | 40 | -0.5954915028 41 | -1.2449491424 42 | 43 | 44 | -0.5 45 | -0.9510565163 46 | 47 | 48 | 1.0 49 | 50 | 51 | 1.0 52 | 53 | 54 | 1.0 55 | 56 | 57 | 1.0 58 | 59 | 60 | 1.0 61 | 62 | 63 | 1.0 64 | 65 | 66 | 1.0 67 | 68 | 69 | 1.0 70 | 71 | 72 | 1.0 73 | 74 | 75 | 1.0 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /test-data/special-cases/degree-one-bevels-causing-splits.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0 7 | 8 | 9 | 0.0 10 | 11 | 12 | 13 | 288.0 14 | 800.0 15 | 16 | 17 | 224.0 18 | 736.0 19 | 20 | 21 | 272.0 22 | 688.0 23 | 24 | 25 | 320.0 26 | 736.0 27 | 28 | 29 | 416.0 30 | 672.0 31 | 32 | 33 | 400.0 34 | 800.0 35 | 36 | 37 | 496.0 38 | 720.0 39 | 40 | 41 | 336.0 42 | 752.0 43 | 44 | 45 | 272.0 46 | 752.0 47 | 48 | 49 | 192.0 50 | 736.0 51 | 52 | 53 | 80.0 54 | 736.0 55 | 56 | 57 | 96.0 58 | 640.0 59 | 60 | 61 | 64.0 62 | 608.0 63 | 64 | 65 | 240.0 66 | 544.0 67 | 68 | 69 | 128.0 70 | 688.0 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /surf/src/BasicInput.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "BasicInput.h" 19 | 20 | BasicInputFromBGL:: 21 | BasicInputFromBGL(const BGLGraph& graph) { 22 | typedef BGLGraph::vertex_descriptor VertexType; 23 | typedef BGLGraph::edge_descriptor EdgeType; 24 | DEBUG_STMT(auto index_map = boost::get(boost::vertex_index, graph)); 25 | 26 | for (auto vp = boost::vertices(graph); vp.first != vp.second; ++vp.first) { 27 | const VertexType v = *vp.first; 28 | DBG(DBG_INPUT) << "x: " << graph[v].x << "; y: " << graph[v].y; 29 | Point_2 p(string_to_maybe_NT(graph[v].x), string_to_maybe_NT(graph[v].y)); 30 | DBG(DBG_INPUT) << std::setprecision(16) << "x: " << CGAL::to_double(p.x()) << "; y: " << CGAL::to_double(p.y()); 31 | unsigned degree = boost::degree(v, graph); 32 | add_vertex(Vertex(p, degree, num_vertices_())); 33 | assert(index_map[v] == num_vertices_()-1); 34 | } 35 | for (auto ep = boost::edges(graph); ep.first != ep.second; ++ep.first) { 36 | const EdgeType e = *ep.first; 37 | const NT weight((graph[e].weight == "") ? CORE_ONE : string_to_maybe_NT(graph[e].weight)); 38 | unsigned s = source(e, graph); 39 | unsigned t = target(e, graph); 40 | if (s == t) { 41 | LOG(ERROR) << "Invalid input: source and target of edge " << num_edges_() << " are the same vertex (v" << t << ")."; 42 | exit(EXIT_INVALID_INPUT); 43 | } 44 | add_edge(s, t, weight); 45 | } 46 | 47 | finalize(); 48 | } 49 | 50 | #ifndef SURF_NDEBUG 51 | void 52 | BasicInput::assert_valid() const { 53 | unsigned* d = new unsigned[vertices_.size()](); 54 | unsigned deg1 = 0; 55 | 56 | assert(edge_map.size() == edges_.size()); 57 | for (size_t i=0; isecond == i); 67 | } 68 | for (size_t i=0; i 2 | 3 | 4 | 5 | 1.0 6 | 7 | 8 | 0.0 9 | 10 | 11 | 12 | 126.974 13 | 811.424 14 | 15 | 16 | 93.0688 17 | 790.815 18 | 19 | 20 | 79.7725 21 | 734.97 22 | 23 | 24 | 132.958 25 | 697.74 26 | 27 | 28 | 166.863 29 | 734.97 30 | 31 | 32 | 180.825 33 | 761.563 34 | 35 | 36 | 161.545 37 | 798.128 38 | 39 | 40 | 283.071 41 | 758.831 42 | 43 | 44 | 312.633 45 | 723.637 46 | 47 | 48 | 277.439 49 | 706.04 50 | 51 | 52 | 310.522 53 | 682.812 54 | 55 | 56 | 359.089 57 | 702.521 58 | 59 | 60 | 266.1771 61 | 834.849122 62 | 63 | 64 | 247.17310000000003 65 | 807.398122 66 | 67 | 68 | 333.75010000000003 69 | 797.544122 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /surf/src/EventQueue.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | #include "surf.h" 21 | 22 | #include "CollapseSpec.h" 23 | #include "Heap.h" 24 | 25 | class Event : public CollapseSpec { 26 | public: 27 | const KineticTriangle * const t; 28 | Event(const KineticTriangle* t, const NT& now); 29 | 30 | void update_collapse(const NT& now); 31 | 32 | friend std::ostream& operator<<(std::ostream& os, const Event& e); 33 | }; 34 | 35 | class EventQueueItem : public HeapItemBase { 36 | private: 37 | public: 38 | EventQueueItem(const KineticTriangle* t, const NT& now) 39 | : HeapItemBase(Event(t, now)) 40 | {}; 41 | 42 | void update_priority(const NT& now) { 43 | priority.update_collapse(now); 44 | } 45 | }; 46 | 47 | 48 | class EventQueue : private HeapBase { 49 | private: 50 | using Base = HeapBase ; 51 | 52 | private: 53 | std::vector need_update; 54 | std::vector need_dropping; 55 | FixedVector tidx_in_need_dropping; 56 | FixedVector tidx_in_need_update; 57 | 58 | FixedVector tidx_to_qitem_map; 59 | 60 | void tidx_to_qitem_map_add(const KineticTriangle * t, ElementType qi); 61 | void drop_by_tidx(unsigned tidx); 62 | void update_by_tidx(unsigned tidx, const NT& now); 63 | void assert_no_pending() const; 64 | public: 65 | EventQueue(const KineticTriangulation& kt); 66 | 67 | /* we /could/ make this const, and our heap a mutable 68 | * object attribute and the vectors also mutable. 69 | * At which point pretty much everything in here is 70 | * declared mutable, so let's just not. 71 | */ 72 | const ElementType& peak() const { 73 | assert_no_pending(); 74 | return Base::peak(); 75 | } 76 | const ElementType& peak(int idx) const { 77 | assert_no_pending(); 78 | return Base::peak(idx); 79 | } 80 | using Base::size; 81 | using Base::empty; 82 | 83 | void process_pending_updates(const NT& now); 84 | 85 | void needs_update(const KineticTriangle * t, bool may_have_valid_collapse_spec = false); 86 | void needs_dropping(KineticTriangle * t); 87 | 88 | bool in_needs_update(const KineticTriangle * t) const; 89 | bool in_needs_dropping(const KineticTriangle * t) const; 90 | 91 | bool is_valid_heap() const; 92 | }; 93 | -------------------------------------------------------------------------------- /surf/src/BasicTriangulation.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | #include "surf.h" 21 | #include "surf/surfconfig.h" 22 | 23 | #include "BasicInput.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | struct BasicVertexInfo { 31 | int original_vertex_idx = -1; 32 | }; 33 | struct BasicFaceInfo { 34 | int component = -1; 35 | bool queued_potential_components = false; /** true if we have visited a neighbor across a constraint. */ 36 | bool queued_this_component = false; /** true if we have ever visited a neighbor across a triangulation edge. 37 | So it's either queued for the current of a previous component. */ 38 | bool matches_component(int q) { 39 | return (q < 0 || component == q); 40 | } 41 | }; 42 | 43 | class BasicTriangulation : public 44 | CGAL::Constrained_Delaunay_triangulation_2< 45 | Kernel, 46 | CGAL::Triangulation_data_structure_2< 47 | CGAL::Triangulation_vertex_base_with_info_2, 48 | CGAL::Triangulation_face_base_with_info_2 > 49 | >, 50 | #ifdef HAVE_CGAL_NO_CONSTRAINT_INTERSECTION_TAG 51 | CGAL::No_constraint_intersection_tag 52 | #else 53 | CGAL::No_intersection_tag 54 | #endif 55 | > { 56 | private: 57 | using Base = Constrained_Delaunay_triangulation_2; 58 | bool consistent_sidedness; 59 | int max_component_ = -1; 60 | 61 | /** establish components. 62 | * 63 | * we provide the verex handles of the first edge (v0, v1), and 64 | * we start by tagging the component left of that as 0, and the 65 | * component right of it (if it is a different one) as 1. 66 | * 67 | * All other components are then tagged with increasing numbers. 68 | * 69 | * If there are only two components, and every edge has a different 70 | * component on each of its sides, we set consistent_sidedness to true. 71 | * Else we set it to false. 72 | * 73 | * Currently nothing cares about consistent_sidedness, however. 74 | */ 75 | void tag_components(const Vertex_handle& v0, const Vertex_handle& v1); 76 | public: 77 | /* Initialize the constrained triangulation. 78 | */ 79 | void initialize(const BasicInput& input); 80 | int max_component() const { return max_component_; }; 81 | }; 82 | -------------------------------------------------------------------------------- /surf/test/TestHeap.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2015 -- 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #define TESTING_HEAP_IS_ALL_PUBLIC 1 19 | 20 | #include "Heap.h" 21 | #include "gtest/gtest.h" 22 | 23 | #include 24 | 25 | static void test_heap_basic(void *); 26 | static void test_heap_heapify(void *); 27 | static void test_heap_remove(void *); 28 | static void test_heap_pop(void *); 29 | 30 | class HeapItem : 31 | public HeapItemBase { 32 | friend void test_heap_pop(void *bla); 33 | 34 | template friend class HeapBase; 35 | public: 36 | int data; 37 | HeapItem(int p_data, double p) : 38 | HeapItemBase(p), 39 | data(p_data) {}; 40 | }; 41 | 42 | class Heap : public HeapBase { 43 | friend void test_heap_basic(void *bla); 44 | friend void test_heap_heapify(void *bla); 45 | friend void test_heap_remove(void *bla); 46 | friend void test_heap_pop(void *bla); 47 | 48 | public: 49 | typedef HeapBase Parent; 50 | typedef typename Parent::ArrayType ArrayType; 51 | 52 | public: 53 | Heap(const ArrayType& v) : Parent(v) {}; 54 | }; 55 | 56 | typedef typename Heap::ArrayType HeapArrayType; 57 | 58 | TEST(HeapTest, Basic) { 59 | HeapArrayType a; 60 | a.push_back(std::make_shared(1, 9.4)); 61 | a.push_back(std::make_shared(2, 3.3)); 62 | a.push_back(std::make_shared(3, 5.1)); 63 | 64 | 65 | Heap h(a); 66 | 67 | EXPECT_EQ(a.size(), 3); 68 | ASSERT_TRUE(h.is_heap()); 69 | EXPECT_EQ(h.size(), 3); 70 | EXPECT_EQ(h.v_[0]->data, 2); 71 | EXPECT_EQ(h.v_[1]->data, 1); 72 | EXPECT_EQ(h.v_[2]->data, 3); 73 | 74 | h.swap_idx(0,1); 75 | ASSERT_FALSE(h.is_heap()); 76 | } 77 | 78 | static Heap 79 | heap_test_generate(int size) { 80 | HeapArrayType a; 81 | for (int i=0; i(i, (1.0*size) / (rand() % (100 * size)) )); 83 | } 84 | return Heap(a); 85 | } 86 | 87 | TEST(HeapTest, Heapify) { 88 | int size=200000; 89 | Heap h = heap_test_generate(size); 90 | EXPECT_EQ(h.size(), size); 91 | ASSERT_TRUE(h.is_heap()); 92 | } 93 | 94 | TEST(HeapTest, Remove) { 95 | int size=2000; 96 | Heap h = heap_test_generate(size); 97 | EXPECT_EQ(h.size(), size); 98 | ASSERT_TRUE(h.is_heap()); 99 | for (;size > 0; --size) { 100 | EXPECT_EQ(size, h.size()); 101 | 102 | int remove = rand() % size; 103 | h.remove(remove); 104 | 105 | ASSERT_TRUE(h.is_heap()); 106 | } 107 | } 108 | 109 | TEST(HeapTest, Pop) { 110 | int size=2000; 111 | Heap h = heap_test_generate(size); 112 | double v = 0; 113 | EXPECT_EQ(h.size(), size); 114 | ASSERT_TRUE(h.is_heap()); 115 | for (;size > 0; --size) { 116 | EXPECT_EQ(size, h.size()); 117 | auto e = h.pop(); 118 | 119 | ASSERT_LE(v, e->priority); 120 | ASSERT_TRUE(h.is_heap()); 121 | 122 | v = e->priority; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /test-data/special-cases/degree-one-multi3-000.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1.0 6 | 7 | 8 | 0.0 9 | 10 | 11 | 12 | 224.0 13 | 672.0 14 | 15 | 16 | 192.0 17 | 640.0 18 | 19 | 20 | 208.0 21 | 608.0 22 | 23 | 24 | 272.0 25 | 592.0 26 | 27 | 28 | 272.0 29 | 640.0 30 | 31 | 32 | 240.0 33 | 720.0 34 | 35 | 36 | 224.0 37 | 736.0 38 | 39 | 40 | 336.0 41 | 656.0 42 | 43 | 44 | 320.0 45 | 544.0 46 | 47 | 48 | 144.0 49 | 592.0 50 | 51 | 52 | 144.0 53 | 560.0 54 | 55 | 56 | 224.0 57 | 560.0 58 | 59 | 60 | 192.0 61 | 576.0 62 | 63 | 64 | 128.0 65 | 688.0 66 | 67 | 68 | 80.0 69 | 752.0 70 | 71 | 72 | 80.0 73 | 720.0 74 | 75 | 76 | 128.0 77 | 720.0 78 | 79 | 80 | 144.0 81 | 752.0 82 | 83 | 84 | 112.0 85 | 784.0 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /test-data/special-cases/degree-one-multi4-000.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1.0 6 | 7 | 8 | 0.0 9 | 10 | 11 | 12 | 224.0 13 | 672.0 14 | 15 | 16 | 192.0 17 | 640.0 18 | 19 | 20 | 208.0 21 | 608.0 22 | 23 | 24 | 272.0 25 | 592.0 26 | 27 | 28 | 272.0 29 | 640.0 30 | 31 | 32 | 240.0 33 | 720.0 34 | 35 | 36 | 224.0 37 | 736.0 38 | 39 | 40 | 336.0 41 | 656.0 42 | 43 | 44 | 320.0 45 | 544.0 46 | 47 | 48 | 144.0 49 | 592.0 50 | 51 | 52 | 144.0 53 | 560.0 54 | 55 | 56 | 208.0 57 | 544.0 58 | 59 | 60 | 192.0 61 | 576.0 62 | 63 | 64 | 128.0 65 | 688.0 66 | 67 | 68 | 80.0 69 | 752.0 70 | 71 | 72 | 80.0 73 | 720.0 74 | 75 | 76 | 128.0 77 | 720.0 78 | 79 | 80 | 144.0 81 | 752.0 82 | 83 | 84 | 112.0 85 | 784.0 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /gui/InputGraphicsItem.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2015 -- 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "InputGraphicsItem.h" 24 | 25 | InputGraphicsItem:: 26 | InputGraphicsItem(const BasicInput * const p_input) 27 | : Base() 28 | , input(p_input) 29 | , vertices_pen(QPen(::Qt::black, 3)) 30 | , segments_pen(QPen(::Qt::black, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)) 31 | , labels_pen(QPen(Qt::black, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)) 32 | { 33 | modelChanged(); 34 | setZValue(3); 35 | } 36 | 37 | void 38 | InputGraphicsItem:: 39 | paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { 40 | Converter convert; 41 | 42 | PainterOstream painterostream(painter); 43 | 44 | painter->setPen(segmentsPen()); 45 | for (const auto& e : input->edges()) { 46 | painterostream << input->get_segment(e); 47 | } 48 | 49 | painter->setPen(verticesPen()); 50 | auto transform = painter->worldTransform(); 51 | painter->resetTransform(); 52 | for (const auto& i : input->vertices()) { 53 | /* Using this results in "points" that are wide rectangles when one zooms too far in, 54 | * so we draw out own points after manually transforming. 55 | * //painterostream << i; 56 | */ 57 | 58 | QPointF point = transform.map(convert(i.p)); 59 | painter->drawPoint(point); 60 | } 61 | if (visible_labels) { 62 | painter->setPen(labelsPen()); 63 | QFont font(painter->font()); 64 | /* 65 | //font.setPixelSize(10); 66 | font.setPointSize(10); 67 | painter->setFont(font); 68 | for (const auto& e : input->edges()) { 69 | const QPointF p(transform.map(convert( CGAL::midpoint(input->get_segment(e).source(), input->get_segment(e).target()) ))); 70 | painter->drawText(p, "s"); 71 | } 72 | */ 73 | font.setPointSize(8); 74 | painter->setFont(font); 75 | for (const auto& v : input->vertices()) { 76 | const QPointF p(transform.map(convert(v.p))); 77 | #ifndef SURF_NDEBUG 78 | std::string t = "v#"+std::to_string(v.id); 79 | #else 80 | std::string t = "v"; 81 | #endif 82 | painter->drawText(p.x()+4, p.y(), QString::fromStdString(t)); 83 | } 84 | } 85 | painter->setWorldTransform(transform); 86 | } 87 | 88 | void 89 | InputGraphicsItem:: 90 | updateBoundingBox() { 91 | GConverter convert; 92 | prepareGeometryChange(); 93 | 94 | if (input->vertices().size() == 0) { 95 | return; 96 | } 97 | 98 | auto bb = GuiPoint((*input->vertices().begin()).p).bbox(); 99 | for (auto& v : input->vertices()) { 100 | bb += GuiPoint(v.p).bbox(); 101 | } 102 | 103 | //std::cout << "bb " << bb << std::endl; 104 | bounding_rect = convert(bb); 105 | } 106 | 107 | void 108 | InputGraphicsItem:: 109 | modelChanged() { 110 | updateBoundingBox(); 111 | } 112 | -------------------------------------------------------------------------------- /surf/src/CollapseSpec.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "CollapseSpec.h" 19 | 20 | unsigned CollapseSpec::COUNTER_NT_cmp = 0; 21 | 22 | std::ostream& 23 | operator<<(std::ostream& os, const CollapseType a) { 24 | switch (a) { 25 | case CollapseType::UNDEFINED: 26 | os << "UNDEFINED"; 27 | break; 28 | case CollapseType::FACE_HAS_INFINITELY_FAST_VERTEX_OPPOSING: 29 | os << "FACE_HAS_INFINITELY_FAST_VERTEX_OPPOSING"; 30 | break; 31 | case CollapseType::FACE_HAS_INFINITELY_FAST_VERTEX_WEIGHTED: 32 | os << "FACE_HAS_INFINITELY_FAST_VERTEX_WEIGHTED"; 33 | break; 34 | case CollapseType::TRIANGLE_COLLAPSE: 35 | os << "TRIANGLE_COLLAPSE"; 36 | break; 37 | case CollapseType::CONSTRAINT_COLLAPSE: 38 | os << "CONSTRAINT_COLLAPSE"; 39 | break; 40 | case CollapseType::SPOKE_COLLAPSE: 41 | os << "SPOKE_COLLAPSE"; 42 | break; 43 | case CollapseType::SPLIT_OR_FLIP_REFINE: 44 | os << "SPLIT_OR_FLIP_REFINE"; 45 | break; 46 | case CollapseType::VERTEX_MOVES_OVER_SPOKE: 47 | os << "VERTEX_MOVES_OVER_SPOKE"; 48 | break; 49 | case CollapseType::CCW_VERTEX_LEAVES_CH: 50 | os << "CCW_VERTEX_LEAVES_CH"; 51 | break; 52 | case CollapseType::INVALID_EVENT: 53 | os << "INVALID_EVENT"; 54 | break; 55 | /* 56 | case CollapseType::GENERIC_FLIP_EVENT: 57 | os << "GENERIC_FLIP_EVENT"; 58 | break; 59 | */ 60 | case CollapseType::NEVER: 61 | os << "NEVER"; 62 | break; 63 | } 64 | return os; 65 | } 66 | 67 | std::ostream& 68 | operator<<(std::ostream& os, const CollapseSpec& s) { 69 | os << "type " << s.type(); 70 | if (s.requires_relevant_edge()) { 71 | os << "(" << s.relevant_edge() << ")"; 72 | } 73 | if (s.type() != CollapseType::NEVER) { 74 | os << " at time " << s.get_printable_time(); 75 | if (s.requires_relevant_edge_plus_secondary_key()) { 76 | os << " (" << s.get_printable_secondary_key() << ")"; 77 | } 78 | } 79 | os << " (component " << s.component << ")"; 80 | return os; 81 | } 82 | 83 | std::ostream& 84 | operator<<(std::ostream& os, const EdgeCollapseType a) { 85 | switch (a) { 86 | case EdgeCollapseType::UNDEFINED: 87 | os << "UNDEFINED"; 88 | break; 89 | case EdgeCollapseType::PAST: 90 | os << "PAST"; 91 | break; 92 | case EdgeCollapseType::FUTURE: 93 | os << "FUTURE"; 94 | break; 95 | case EdgeCollapseType::ALWAYS: 96 | os << "ALWAYS"; 97 | break; 98 | case EdgeCollapseType::NEVER: 99 | os << "NEVER"; 100 | break; 101 | } 102 | return os; 103 | } 104 | 105 | std::ostream& 106 | operator<<(std::ostream& os, const EdgeCollapseSpec& s) { 107 | os << "type " << s.type(); 108 | if (s.type() == EdgeCollapseType::FUTURE) { 109 | os << " at time " << s.get_printable_time();// << " (" << CGAL::to_double(s.longest_spoke()) << ")"; 110 | } 111 | return os; 112 | } 113 | -------------------------------------------------------------------------------- /surf/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (surf VERSION "${SURFVERSION}" DESCRIPTION "surflib") 2 | 3 | INCLUDE (CheckTypeSize) 4 | 5 | 6 | if ( NOT MSVC ) 7 | set_source_files_properties(easyloggingpp/src/easylogging++.cc PROPERTIES COMPILE_FLAGS "-Wno-float-equal -Wno-missing-noreturn") 8 | endif() 9 | 10 | find_package( CGAL COMPONENTS Core ) 11 | if ( NOT CGAL_FOUND ) 12 | message(STATUS "CGAL not found.") 13 | return() 14 | endif() 15 | message("CMAKE_BUILD_TYPE is ${CMAKE_BUILD_TYPE}. CGAL_USE_FILE is ${CGAL_USE_FILE}" ) 16 | message("CMAKE_BUILD_TYPE is ${CMAKE_BUILD_TYPE}." ) 17 | set ( CGAL_BUILD_TYPE_INIT ${CMAKE_BUILD_TYPE} ) 18 | include( ${CGAL_USE_FILE} ) 19 | 20 | find_package( Boost REQUIRED graph iostreams) 21 | if ( NOT Boost_FOUND ) 22 | message(STATUS "Boost not found.") 23 | return() 24 | endif() 25 | 26 | #find_package(MPFI) 27 | #if ( NOT MPFR_FOUND ) 28 | # message(STATUS "MPFI not found.") 29 | # return() 30 | #endif() 31 | #include(${MPFI_USE_FILE}) 32 | 33 | #include( CGAL_CreateSingleSourceCGALProgram ) 34 | 35 | include_directories(${Boost_INCLUDE_DIRS}) 36 | 37 | # COMPILER SETTINGS 38 | 39 | if ( NOT MSVC ) 40 | # set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror" ) 41 | endif() 42 | 43 | # BINARIES AND SOURCES 44 | 45 | # cause easylogging to pretty please don't print to stdout. 46 | 47 | add_library(surflib 48 | src/cgaltools.cpp 49 | src/BGLGraph.cpp 50 | src/SkeletonDCEL.cpp 51 | src/SkeletonStructure.cpp 52 | src/BasicInput.cpp 53 | src/BasicTriangulation.cpp 54 | src/EventQueue.cpp 55 | src/KineticTriangulation.cpp 56 | src/KineticTriangle.cpp 57 | src/WavefrontEdge.cpp 58 | src/WavefrontVertex.cpp 59 | src/CollapseSpec.cpp 60 | src/tools.cpp 61 | src/surf.cpp 62 | easyloggingpp/src/easylogging++.cc 63 | ) 64 | set_target_properties(surflib PROPERTIES VERSION ${PROJECT_VERSION}) 65 | 66 | target_link_libraries(surflib ${Boost_LIBRARIES}) 67 | target_link_libraries(surflib ${CGAL_LIBRARIES} ${CGAL_3RD_PARTY_LIBRARIES}) 68 | 69 | set_target_properties(surflib PROPERTIES PUBLIC_HEADER include/tools.h) 70 | 71 | target_compile_definitions(surflib PRIVATE ELPP_CUSTOM_COUT=std::cerr) 72 | 73 | target_include_directories(surflib PRIVATE include) 74 | target_include_directories(surflib PRIVATE src) 75 | 76 | try_compile(HAVE_INTERSECTION_OF_CONSTRAINTS_EXCEPTION 77 | "${CMAKE_BINARY_DIR}/temp" 78 | SOURCES "${PROJECT_SOURCE_DIR}/../cmake/tests/test_CGAL__Intersection_of_constraints_exception.cpp" 79 | CMAKE_FLAGS "-DCMAKE_CXX_LINK_EXECUTABLE='echo not linking now...'" ) 80 | 81 | # also checks for existance and puts it into HAVE_... 82 | set(CMAKE_EXTRA_INCLUDE_FILES "CGAL/Constrained_triangulation_2.h") 83 | check_type_size("CGAL::No_constraint_intersection_tag" CGAL_NO_CONSTRAINT_INTERSECTION_TAG 84 | LANGUAGE CXX) 85 | set(CMAKE_EXTRA_INCLUDE_FILES) 86 | 87 | 88 | configure_file ( 89 | "${PROJECT_SOURCE_DIR}/surfconfig.h.in" 90 | "${PROJECT_BINARY_DIR}/surfconfig.h" 91 | @ONLY 92 | ) 93 | 94 | include(GNUInstallDirs) 95 | 96 | #### TESTING ## 97 | if(TEST_SUITE) 98 | set( TEST_BINARY_NAME runtests ) 99 | include(GoogleTest) 100 | add_executable( ${TEST_BINARY_NAME} 101 | test/TestBGLGraph.cpp 102 | test/TestHeap.cpp 103 | src/BGLGraph.cpp 104 | src/surf.cpp 105 | easyloggingpp/src/easylogging++.cc 106 | ) 107 | gtest_add_tests(TARGET ${TEST_BINARY_NAME}) 108 | 109 | if ( NOT MSVC ) 110 | set_target_properties(${TEST_BINARY_NAME} PROPERTIES PRIVATE "-Wnofloat-equal") 111 | endif() 112 | target_include_directories(${TEST_BINARY_NAME} PRIVATE include) 113 | target_include_directories(${TEST_BINARY_NAME} PRIVATE src) 114 | target_include_directories(${TEST_BINARY_NAME} PRIVATE test) 115 | target_link_libraries(${TEST_BINARY_NAME} gtest gtest_main pthread ) 116 | target_link_libraries(${TEST_BINARY_NAME} ${Boost_LIBRARIES} ) 117 | target_link_libraries(${TEST_BINARY_NAME} ${CGAL_LIBRARIES} ${CGAL_3RD_PARTY_LIBRARIES}) 118 | 119 | if ( NOT MSVC ) 120 | target_compile_options(${TEST_BINARY_NAME} PRIVATE "-Wno-unused-function") 121 | endif() 122 | ENDIF() 123 | -------------------------------------------------------------------------------- /gui/mainwindow.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef MAINWINDOW_H 19 | #define MAINWINDOW_H 20 | 21 | #include "SkeletonStructure.h" 22 | #include "InputGraphicsItem.h" 23 | #include "KineticTriangulationGraphicsItem.h" 24 | #include "OffsetsGraphicsItem.h" 25 | #include "SkeletonGraphicsItem.h" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace Ui { 33 | class MainWindow; 34 | } 35 | 36 | class MainWindow : public CGAL::Qt::DemosMainWindow 37 | { 38 | Q_OBJECT 39 | 40 | public: 41 | explicit MainWindow( 42 | std::string title, 43 | std::istream& is, 44 | unsigned skip_to=0, 45 | bool skip_all=false, 46 | std::string skip_until_time="", 47 | std::string skoffset="", 48 | int restrict_component = -1, 49 | NT step_increment = CORE_ZERO); 50 | ~MainWindow(); 51 | 52 | private: 53 | bool first_show_event = true; 54 | bool did_finish = false; 55 | private slots: 56 | void showEvent(QShowEvent *); 57 | void mousePressEvent(QMouseEvent *event); 58 | 59 | void on_actionQuit_triggered() { close(); }; 60 | void on_actionVisToggleInput_triggered() { updateVisibilities(); }; 61 | void on_actionVisToggleInputLabels_triggered() { updateVisibilities(); }; 62 | void on_actionVisToggleWavefront_triggered() { updateVisibilities(); }; 63 | void on_actionVisToggleKineticTriangulation_triggered() { updateVisibilities(); }; 64 | void on_actionVisToggleKineticTriangulationLabels_triggered() { updateVisibilities(); }; 65 | void on_actionVisToggleOffsets_triggered() { updateVisibilities(); }; 66 | void on_actionVisToggleArcs_triggered() { updateVisibilities(); }; 67 | void on_actionVisToggleHighlightCircle_triggered() { updateVisibilities(); }; 68 | void on_actionVisToggleSkeleton_triggered() { updateVisibilities(); }; 69 | void on_actionVisToggleSkeletonLabels_triggered() { updateVisibilities(); }; 70 | void on_actionResize_triggered(); 71 | void on_actionUpdateOffsets_triggered(); 72 | 73 | void on_actionTimeBackward_triggered(); 74 | void on_actionTimeForward_triggered(); 75 | void on_actionTimeForwardThrough_triggered(); 76 | void on_actionTimeForwardNext_triggered(); 77 | void on_actionTimeReset_triggered(); 78 | void on_actionEventStep_triggered(); 79 | void on_actionEventStepEnd_triggered(); 80 | 81 | void on_actionTimeOffsetMinus_triggered(); 82 | void on_actionTimeOffsetPlus_triggered(); 83 | void on_actionTimeOffsetReset_triggered(); 84 | 85 | private: 86 | std::string title; 87 | std::string skoffset; 88 | std::unique_ptr ui; 89 | QGraphicsScene scene; 90 | QLabel* time_label; 91 | NT drawing_time_offset_increment; 92 | 93 | std::unique_ptr s; 94 | 95 | std::shared_ptr input_gi; 96 | std::shared_ptr kinetic_triangulation_gi; 97 | std::shared_ptr offsets_gi; 98 | std::shared_ptr skeleton_gi; 99 | 100 | void update_offsets(); 101 | void updateVisibilities(); 102 | void updateEnabled(); 103 | void update_time_label(); 104 | void time_changed(); 105 | void simulation_has_finished(); 106 | }; 107 | 108 | #endif // MAINWINDOW_H 109 | -------------------------------------------------------------------------------- /surf/include/tools.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | /* Tools is for stand-alone things that need nothing else of surf or cgal. */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | 27 | void my_srand(uint32_t s); 28 | uint32_t my_rand(); 29 | 30 | /** Compute floor(log2()) of an unsigned integer. 31 | * 32 | * Returns -1 for 0. 33 | * 34 | * We do this the naive way, requiring log(n) time. 35 | * 36 | * If this function ever is part of the critical path consider replacing 37 | * with something from 38 | * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup . 39 | */ 40 | static inline int 41 | log2i(unsigned v) { 42 | int r = -1; 43 | 44 | while (v > 0) { 45 | r++; 46 | v >>= 1; 47 | }; 48 | 49 | return r; 50 | } 51 | 52 | template 53 | void sort_tuple(T& a, T& b) { 54 | if (a>b) std::swap(a,b); 55 | } 56 | 57 | template 58 | std::pair sorted_tuple(T a, T b) { 59 | if (a>b) std::swap(a,b); 60 | return std::make_pair(a, b); 61 | } 62 | 63 | /** returns a triple of indices i0,i1,i2 such that t[i0] <= t[i1] <= t[i2]. 64 | */ 65 | template 66 | std::tuple indirect_sort_3(const T t[3]) { 67 | unsigned i0 = 0; 68 | unsigned i1 = 1; 69 | unsigned i2 = 2; 70 | if (t[i0] > t[i1]) std::swap(i0, i1); 71 | if (t[i1] > t[i2]) std::swap(i1, i2); 72 | if (t[i0] > t[i1]) std::swap(i0, i1); 73 | return std::make_tuple(i0, i1, i2); 74 | } 75 | 76 | 77 | template 78 | static T compute_determinant(const T& x0, const T& y0, 79 | const T& x1, const T& y1, 80 | const T& x2, const T& y2) { 81 | return T( 82 | ( x0 * y1 83 | + x1 * y2 84 | + x2 * y0 85 | ) 86 | - 87 | ( y0 * x1 88 | + y1 * x2 89 | + y2 * x0 90 | ) 91 | ); 92 | } 93 | 94 | template 95 | struct FixedVector 96 | : private std::vector { 97 | private: 98 | using Base = std::vector; 99 | using size_type = typename Base::size_type; 100 | using value_type = typename Base::value_type; 101 | 102 | using Base::capacity; 103 | public: 104 | using const_iterator = typename Base::const_iterator; 105 | 106 | void reserve(size_type new_size) { 107 | assert(size() == 0); 108 | Base::reserve(new_size); 109 | } 110 | void resize(size_type new_size, const value_type& val) { 111 | assert(size() == 0); 112 | Base::resize(new_size, val); 113 | } 114 | //using Base::vector; 115 | //using Base::operator=; 116 | //using Base::get_allocator; 117 | using Base::at; 118 | //using Base::front; 119 | using Base::back; 120 | //using Base::clear; 121 | //using Base::data; 122 | using Base::begin; 123 | //using Base::cbegin 124 | using Base::end; 125 | //using Base::cend; 126 | //using Base::empty; 127 | using Base::size; 128 | using Base::operator[]; 129 | 130 | void emplace_back (value_type&& val) { 131 | assert(size() < capacity()); 132 | Base::emplace_back(std::forward(val)); 133 | } 134 | void push_back (const value_type& val) { 135 | assert(size() < capacity()); 136 | Base::push_back(val); 137 | } 138 | void push_back (value_type&& val) { 139 | assert(size() < capacity()); 140 | Base::push_back(std::forward(val)); 141 | } 142 | }; 143 | -------------------------------------------------------------------------------- /surf/src/cgaltools.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "cgaltools.h" 19 | 20 | /** Solve the quadratic polynomial f. 21 | * 22 | * returns a pair of boolean values, . 23 | * has_real_roots is true iff the polynomial has real (and not just complex) roots. 24 | * is_square is true iff the found (real) root has multiplicity 2. 25 | * 26 | * the function puts the result in arguments x0 and x1, 27 | * where after the function returns with has_real_roots set, x0 <= x1 28 | * (with x0 == x1 iff is_square). 29 | * 30 | * If has_real_roots is false, x0 and x1 remain unmodified 31 | * and we always return false for is_square. 32 | */ 33 | std::pair 34 | solve_quadratic(const Polynomial_1& f, NT& x0, NT& x1) { 35 | std::pair res; 36 | assert(f.degree() == 2); 37 | 38 | NT a(f[2]); 39 | NT b(f[1]); 40 | NT c(f[0]); 41 | //LOG(DEBUG) << " d: " << CGAL::to_double(a) << "t^2 + " 42 | // << CGAL::to_double(b) << "t + " 43 | // << CGAL::to_double(c); 44 | 45 | /* CGAL can factorize polynomials, but only square-free ones, 46 | * which doesn't help us much, alas. 47 | */ 48 | NT radicand(b*b - 4. * a * c); 49 | if (radicand == 0) { 50 | x0 = x1 = (-b) / (2. * a); 51 | res = {true, true}; 52 | } else if (radicand > 0) { 53 | NT root(CGAL::sqrt(radicand)); 54 | NT divisor(2.*a); 55 | auto sign = f.sign(); 56 | if (sign == CGAL::POSITIVE) { 57 | x0 = (-b - root) / divisor; 58 | x1 = (-b + root) / divisor; 59 | } else { 60 | assert(sign == CGAL::NEGATIVE); 61 | x1 = (-b - root) / divisor; 62 | x0 = (-b + root) / divisor; 63 | } 64 | //LOG(DEBUG) << " x0: " << CGAL::to_double(x0); 65 | //LOG(DEBUG) << " x1: " << CGAL::to_double(x1); 66 | //LOG(DEBUG) << " x0: " << x0; 67 | //LOG(DEBUG) << " x1: " << x1; 68 | res = {true, false}; 69 | } else { 70 | res = {false, false}; 71 | } 72 | 73 | return res; 74 | } 75 | 76 | #if 0 77 | /* This code requires access to some internal CGAL data 78 | * structures, so it might require defining protected 79 | * as public and re-delcaring a few private things 80 | * as public in CGAL's source. 81 | */ 82 | static 83 | void 84 | debug_core_NTs_walk_tree(CORE::ExprRep const * a) { 85 | if (auto c = dynamic_cast(a)) { 86 | std::cout << "E{" << a << "}"; 87 | if (auto r = dynamic_cast(a)) { 88 | const CORE::RealRep * rep = &(r->value.getRep()); 89 | if (rep->ID() == CORE::REAL_BIGRAT) { 90 | CORE::BigRat rat(rep->BigRatValue()); 91 | std::cout << "cr[" << rat.get_str() << "]"; 92 | } else { 93 | std::cout << "c?[" << rep->ID() << "]"; 94 | } 95 | } else { 96 | std::cout << "c[" << c->dump(CORE::ExprRep::VALUE_ONLY) << "]"; 97 | } 98 | } else if (auto u = dynamic_cast(a)) { 99 | debug_core_NTs_walk_tree(u->child); 100 | std::cout << "E{" << a << "}"; 101 | std::cout << "1" << u->op() << 102 | "[" << 103 | "E{" << u->child << "}" << 104 | "]"; 105 | } else if (auto b = dynamic_cast(a)) { 106 | debug_core_NTs_walk_tree(b->first); 107 | debug_core_NTs_walk_tree(b->second); 108 | std::cout << "E{" << a << "}"; 109 | std::cout << "2" << b->op() << 110 | "[" << 111 | "E{" << b->first << "}" << 112 | "," << 113 | "E{" << b->second << "}" << 114 | "]"; 115 | } 116 | std::cout << " # " << a->dump(CORE::ExprRep::VALUE_ONLY) << std::endl; 117 | } 118 | #endif 119 | -------------------------------------------------------------------------------- /surf/src/SkeletonStructure.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | #include "surf.h" 21 | 22 | #include "WavefrontVertex.h" 23 | #include "KineticTriangulation.h" 24 | #include "EventQueue.h" 25 | 26 | class SkeletonStructure; 27 | 28 | class WavefrontPropagator { 29 | private: 30 | SkeletonStructure * const sk; 31 | std::shared_ptr eq; 32 | unsigned event_ctr_ = 0; 33 | bool finalized = false; 34 | 35 | NT time = CORE_ZERO; 36 | NT last_event_time = CORE_ZERO; 37 | NT increment = 0.0005; 38 | int current_component = -1; 39 | int last_event_component = -1; 40 | 41 | bool no_more_events() const { 42 | assert(eq); 43 | return eq->empty() || peak()->get_priority().type() == CollapseType::NEVER; 44 | } 45 | 46 | public: 47 | WavefrontPropagator(SkeletonStructure * const p_sk) 48 | : sk(p_sk) 49 | {}; 50 | void setup_queue(KineticTriangulation& kt); 51 | 52 | bool propagation_complete() const { 53 | return finalized; 54 | } 55 | 56 | const NT& get_time() const { return time; }; 57 | /** Get the current component we are working on. 58 | * 59 | * This is for display purposes only. 60 | * -1 if we should show all. if >=0 we are currently working on this component. */ 61 | int get_current_component() const { return current_component; }; 62 | //void set_time(const NT& t) { time = t; }; 63 | void set_increment(const NT& i) { increment = i; }; 64 | 65 | /** Move backwards in time */ 66 | void reverse_time() { time -= increment; }; 67 | 68 | /** Move forward in time, but ignore any event that may have happened */ 69 | void advance_time_ignore_event() { time += increment; }; 70 | void advance_time_ignore_event(const NT& t) { time = t; }; 71 | 72 | /** Move forward in time by the increment, or until the next event and handle it */ 73 | void advance_time(); 74 | 75 | /** Move forward in time to the next event but do not handle it yet. */ 76 | void advance_time_next(); 77 | 78 | /** Move forward in time to the next event and handle it. */ 79 | void advance_step(); 80 | 81 | /** Process events until done. */ 82 | void advance_to_end(); 83 | 84 | void reset_time_to_last_event() { time = last_event_time; current_component = last_event_component; }; 85 | 86 | const std::shared_ptr peak() const { 87 | assert(eq); 88 | assert(!eq->empty()); 89 | return eq->peak(); 90 | } 91 | 92 | unsigned event_ctr() const { return event_ctr_; } 93 | 94 | void do_initial_skips(bool skip_all, unsigned skip_to, const NT& skip_until_time); 95 | 96 | /** finish up and create the dcel and whatever else is necessary once the propagation is done. */ 97 | void finalize(); 98 | }; 99 | 100 | 101 | class SkeletonStructure { 102 | friend class WavefrontPropagator; 103 | 104 | private: 105 | const BasicInput input; 106 | WavefrontEdgeList wavefront_edges; 107 | KineticTriangulation kt; 108 | 109 | public: 110 | WavefrontPropagator wp; 111 | 112 | SkeletonStructure(BasicInput&& input_) 113 | : input(std::forward(input_)) 114 | , wp(this) 115 | { }; 116 | 117 | /** copy vertices and edges from a Boost Graph. */ 118 | void initialize(int restrict_component = -1); 119 | 120 | const BasicInput& get_input() const { 121 | return input; 122 | } 123 | const KineticTriangulation& get_kt() const { 124 | return kt; 125 | } 126 | const SkeletonDCEL& get_skeleton() const { 127 | return kt.get_skeleton(); 128 | } 129 | 130 | //void print_next_info() const; 131 | }; 132 | -------------------------------------------------------------------------------- /test-data/convex19.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -0.0296610169 9 | 1.2669491525 10 | 11 | 12 | -0.1525423729 13 | 1.2161016949 14 | 15 | 16 | -0.2415254237 17 | 1.1355932203 18 | 19 | 20 | -0.3220338983 21 | 1.0254237288 22 | 23 | 24 | -0.3559322034 25 | 0.9025423729 26 | 27 | 28 | -0.3644067797 29 | 0.7796610169 30 | 31 | 32 | -0.3644067797 33 | 0.6355932203 34 | 35 | 36 | -0.2966101695 37 | 0.5084745763 38 | 39 | 40 | -0.1822033898 41 | 0.3940677966 42 | 43 | 44 | -0.0042372881 45 | 0.3008474576 46 | 47 | 48 | 0.1652542373 49 | 0.2838983051 50 | 51 | 52 | 0.3559322034 53 | 0.3093220339 54 | 55 | 56 | 0.4703389831 57 | 0.4237288136 58 | 59 | 60 | 0.5508474576 61 | 0.5720338983 62 | 63 | 64 | 0.5889830508 65 | 0.7669491525 66 | 67 | 68 | 0.5381355932 69 | 0.9533898305 70 | 71 | 72 | 0.4237288136 73 | 1.1355932203 74 | 75 | 76 | 0.2796610169 77 | 1.2457627119 78 | 79 | 80 | 0.1101694915 81 | 1.2838983051 82 | 83 | 84 | 1.0 85 | 86 | 87 | 1.0 88 | 89 | 90 | 1.0 91 | 92 | 93 | 1.0 94 | 95 | 96 | 1.0 97 | 98 | 99 | 1.0 100 | 101 | 102 | 1.0 103 | 104 | 105 | 1.0 106 | 107 | 108 | 1.0 109 | 110 | 111 | 1.0 112 | 113 | 114 | 1.0 115 | 116 | 117 | 1.0 118 | 119 | 120 | 1.0 121 | 122 | 123 | 1.0 124 | 125 | 126 | 1.0 127 | 128 | 129 | 1.0 130 | 131 | 132 | 1.0 133 | 134 | 135 | 1.0 136 | 137 | 138 | 1.0 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /surf/src/SkeletonStructure.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "SkeletonStructure.h" 19 | #include "BasicTriangulation.h" 20 | 21 | void 22 | WavefrontPropagator:: 23 | setup_queue(KineticTriangulation& kt) { 24 | eq = std::make_shared(kt); 25 | kt.set_queue(eq); 26 | } 27 | 28 | void 29 | WavefrontPropagator:: 30 | advance_time() { 31 | time += increment; 32 | 33 | if (!propagation_complete()) { 34 | if (no_more_events()) { 35 | advance_step(); 36 | } else { 37 | NT want_time = time; 38 | while (!propagation_complete() && want_time > eq->peak()->get_priority().time()) { 39 | advance_step(); 40 | } 41 | time = want_time; 42 | } 43 | } 44 | } 45 | 46 | void 47 | WavefrontPropagator:: 48 | advance_time_next() { 49 | if (! no_more_events() ) { 50 | const std::shared_ptr next = eq->peak(); 51 | time = next->get_priority().time(); 52 | if (sk->get_kt().restrict_component()) { 53 | current_component = peak()->get_priority().t->component; 54 | } 55 | } 56 | } 57 | 58 | void 59 | WavefrontPropagator:: 60 | advance_step() { 61 | DBG_INDENT_LEVEL_STORE; 62 | DBG_FUNC_BEGIN(DBG_PROP); 63 | 64 | if (!no_more_events()) { 65 | const std::shared_ptr next = eq->peak(); 66 | time = next->get_priority().time(); 67 | if (sk->get_kt().restrict_component()) { 68 | current_component = peak()->get_priority().t->component; 69 | } 70 | ++event_ctr_; 71 | VLOG(2) << " event#" << event_ctr_ << " @ " << CGAL::to_double(time); 72 | sk->kt.handle_event(next->get_priority()); 73 | DBG(DBG_PROP) << " event#" << event_ctr_ << " handling done. Processing pending PQ updates."; 74 | eq->process_pending_updates(time); 75 | DBG(DBG_PROP) << " event#" << event_ctr_ << " PQ updates done. Time is now " << CGAL::to_double(time); 76 | LOG(INFO) << " event#" << event_ctr_ << " done. Time is now " << CGAL::to_double(time); 77 | 78 | last_event_time = time; 79 | last_event_component = current_component; 80 | } 81 | 82 | if (no_more_events()) { 83 | LOG(INFO) << "All done."; 84 | finalize(); 85 | } else { 86 | const Event& next_event = eq->peak()->get_priority(); 87 | DBG(DBG_PROP) << " event#" << (event_ctr_+1) << " will be " << next_event; 88 | assert((next_event.t->component == current_component && next_event.time() >= last_event_time) || 89 | next_event.t->component > current_component); 90 | if (eq->size() >= 2) { 91 | DBG(DBG_PROP) << " event child in heap: " << eq->peak(1)->get_priority(); 92 | if (eq->size() >= 3) { 93 | DBG(DBG_PROP) << " event child in heap: " << eq->peak(2)->get_priority(); 94 | } 95 | } 96 | } 97 | DBG_FUNC_END(DBG_PROP); 98 | DBG_INDENT_LEVEL_CHECK; 99 | } 100 | 101 | void 102 | WavefrontPropagator:: 103 | advance_to_end() { 104 | while (!propagation_complete()) { 105 | advance_step(); 106 | } 107 | } 108 | 109 | void 110 | WavefrontPropagator:: 111 | do_initial_skips(bool skip_all, unsigned skip_to, const NT& skip_until_time) { 112 | if (skip_all) { 113 | advance_to_end(); 114 | } else { 115 | while (!propagation_complete() && 116 | skip_to > event_ctr()+1) { 117 | advance_step(); 118 | }; 119 | } 120 | if (skip_until_time > CORE_ZERO) { 121 | while (!propagation_complete() && 122 | (no_more_events() || skip_until_time > peak()->get_priority().time())) { 123 | advance_step(); 124 | } 125 | if (skip_until_time > get_time()) { 126 | advance_time_ignore_event(skip_until_time); 127 | } 128 | } 129 | } 130 | 131 | void 132 | WavefrontPropagator:: 133 | finalize() { 134 | DBG_FUNC_BEGIN(DBG_PROP); 135 | assert(no_more_events()); 136 | if (!finalized) { 137 | DBG(DBG_PROP) << "Calling create_remaining_skeleton_dcel()"; 138 | sk->kt.create_remaining_skeleton_dcel(); 139 | finalized = true; 140 | current_component = -1; 141 | DBG(DBG_PROP) << "Finalized."; 142 | 143 | sk->kt.update_event_timing_stats(-1); 144 | } 145 | DBG_FUNC_END(DBG_PROP); 146 | } 147 | 148 | 149 | void 150 | SkeletonStructure:: 151 | initialize(int restrict_component) { 152 | kt.initialize(input, &wavefront_edges, restrict_component); 153 | assert(kt.triangles_size() > 0); 154 | wp.setup_queue(kt); 155 | } 156 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.10) 2 | project (ORD53) 3 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 4 | if(POLICY CMP0077) 5 | cmake_policy(SET CMP0077 NEW) 6 | endif() 7 | 8 | if ( NOT EXISTS "${PROJECT_SOURCE_DIR}/surf/easyloggingpp/src/easylogging++.h") 9 | message( FATAL_ERROR "We are missing easylogging in surf/easyloggingpp/. See the README.md file for details." ) 10 | endif() 11 | 12 | execute_process(COMMAND git describe --abbrev=8 --dirty --always --tags 13 | OUTPUT_VARIABLE GIT_REV 14 | OUTPUT_STRIP_TRAILING_WHITESPACE 15 | ERROR_QUIET) 16 | if ("${GIT_REV}" STREQUAL "") 17 | SET(GIT_REV "git:N/A") 18 | endif() 19 | SET(SURFVERSION "1.99") 20 | SET(VERSIONGIT "${SURFVERSION}-${GIT_REV}") 21 | 22 | set (CMAKE_CXX_STANDARD_REQUIRED ON) 23 | set (CMAKE_CXX_STANDARD 17) 24 | 25 | if ( NOT MSVC ) 26 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall" ) 27 | 28 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wfloat-equal -Wundef -Wpointer-arith" ) 29 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wwrite-strings" ) 30 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wredundant-decls -Wchar-subscripts -Wcomment -Wformat=2" ) 31 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wwrite-strings -Wmissing-declarations -Wredundant-decls" ) 32 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wswitch-enum" ) 33 | 34 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Winit-self -Wmissing-field-initializers" ) 35 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Waddress -Wmissing-noreturn -Wstrict-overflow=1" ) 36 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Warray-bounds" ) 37 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshadow" ) 38 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic" ) 39 | 40 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=switch-enum" ) 41 | # set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=shadow" ) 42 | # set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=float-equal" ) 43 | 44 | ### XXX REMOVE EVENTUALLY 45 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter" ) 46 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable" ) 47 | endif() 48 | 49 | if( NOT CMAKE_BUILD_TYPE ) 50 | set( CMAKE_BUILD_TYPE Debug CACHE STRING 51 | "Choose the type of build, options are: Debug Release." 52 | FORCE ) 53 | endif() 54 | set (CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE 1) 55 | 56 | string(TOUPPER "${CMAKE_BUILD_TYPE}" UPPERCASE_CMAKE_BUILD_TYPE) 57 | 58 | if (NOT UPPERCASE_CMAKE_BUILD_TYPE MATCHES "^(DEBUG|RELEASE)$") 59 | message(FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") 60 | endif() 61 | 62 | # so we can run in valgrind 63 | add_definitions(-DCGAL_DISABLE_ROUNDING_MATH_CHECK=ON) 64 | 65 | add_definitions(-DELPP_NO_DEFAULT_LOG_FILE) 66 | 67 | 68 | if(UPPERCASE_CMAKE_BUILD_TYPE MATCHES DEBUG) 69 | SET (DEBUG_OUTPUT_DEFAULT ON) 70 | SET (DEBUG_COLLAPSE_TIMES ON) 71 | SET (SURF_NDEBUG OFF) 72 | else() 73 | SET (DEBUG_OUTPUT_DEFAULT OFF) 74 | SET (DEBUG_COLLAPSE_TIMES OFF) 75 | SET (SURF_NDEBUG ON) 76 | endif() 77 | 78 | ## Set default option values unless values given on the command line. 79 | if (NOT DEFINED DEBUG_COLLAPSE_TIMES) 80 | SET (DEBUG_COLLAPSE_TIMES ${DEBUG_COLLAPSE_TIMES}) 81 | endif() 82 | if (NOT DEFINED DEBUG_OUTPUT) 83 | SET (DEBUG_OUTPUT ${DEBUG_OUTPUT_DEFAULT}) 84 | endif() 85 | 86 | # declare the options 87 | option(REFINE_TRIANGULATION "Refine triangulation (to avoid flip events)" ON) 88 | 89 | # option(DEBUG_EXPENSIVE_PREDICATES "Check assertions that we expect to be expensive" OFF) 90 | set (DEBUG_EXPENSIVE_PREDICATES SOME CACHE STRING "Check assertions that we expect to be expensive") 91 | set_property(CACHE DEBUG_EXPENSIVE_PREDICATES PROPERTY STRINGS SOME ON OFF) 92 | 93 | if(UPPERCASE_CMAKE_BUILD_TYPE MATCHES DEBUG) 94 | if(DEBUG_EXPENSIVE_PREDICATES STREQUAL "SOME") 95 | set (DEBUG_EXPENSIVE_PREDICATES 1) 96 | elseif(DEBUG_EXPENSIVE_PREDICATES) 97 | set (DEBUG_EXPENSIVE_PREDICATES 2) 98 | else() 99 | set (DEBUG_EXPENSIVE_PREDICATES OFF) 100 | endif() 101 | else() 102 | set (DEBUG_EXPENSIVE_PREDICATES OFF) 103 | endif() 104 | 105 | option(TEST_SUITE "Build test suite" OFF) 106 | 107 | option(DEBUG_COLLAPSE_TIMES "Double check collapse times and add extra safeties" ${DEBUG_COLLAPSE_TIMES}) 108 | 109 | option(DEBUG_OUTPUT "Include logging at debug level" ${DEBUG_OUTPUT}) 110 | option(DEBUG_OUTPUT_WITH_FILES "Include filenames and line numbersin debug output" OFF) 111 | 112 | option(NT_USE_DOUBLE "Use double as the number type" OFF) 113 | option(HEAP_STATS "Gather stats on heap properties" OFF) 114 | 115 | option(BUILD_SHARED_LIBS "Build surflib as a shared library" OFF) 116 | option(LIB_ONLY "Build surflib only, no gui or cli" OFF) 117 | 118 | configure_file ( 119 | "${PROJECT_SOURCE_DIR}/config.h.in" 120 | "${PROJECT_BINARY_DIR}/config.h" 121 | @ONLY 122 | ) 123 | include_directories("${PROJECT_BINARY_DIR}") 124 | 125 | if(LIB_ONLY) 126 | subdirs (surf) 127 | else() 128 | subdirs (surf gui cc) 129 | endif() 130 | 131 | enable_testing() 132 | -------------------------------------------------------------------------------- /gui/SkeletonGraphicsItem.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2015 -- 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "SkeletonGraphicsItem.h" 24 | 25 | SkeletonGraphicsItem:: 26 | SkeletonGraphicsItem(const SkeletonDCEL * const skeleton_) 27 | : Base() 28 | , skeleton(skeleton_) 29 | , vertices_pen(QPen(::Qt::blue, 3)) 30 | , input_segments_pen(QPen(::Qt::black, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)) 31 | , segments_pen(QPen(::Qt::blue, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)) 32 | , rays_pen(QPen(QColor("#5599ff"), 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)) 33 | , labels_pen(QPen(Qt::black, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)) 34 | { 35 | modelChanged(); 36 | setZValue(2); 37 | } 38 | 39 | void 40 | SkeletonGraphicsItem:: 41 | paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { 42 | Converter convert; 43 | 44 | PainterOstream painterostream(painter); 45 | int ray_length = 10; 46 | 47 | for (auto hit = skeleton->halfedges_begin(); hit != skeleton->halfedges_end(); ++hit) { 48 | if (hit > hit->opposite()) continue; /* Only paint one of every halfedge pair */ 49 | 50 | const auto& arc = hit->curve(); 51 | if (arc.type() == typeid(Segment_3)) { 52 | const Segment_3& s = boost::get(arc); 53 | painter->setPen( 54 | hit->is_input() ? 55 | inputSegmentsPen() : 56 | segmentsPen() ); 57 | painterostream << project_plane(s); 58 | } else { 59 | assert(arc.type() == typeid(Ray_3)); 60 | const Ray_3& r = boost::get(arc); 61 | const Ray_2 r2 = project_plane(r); 62 | painter->setPen(raysPen()); 63 | painterostream << Segment_2(r2.source(), r2.point(ray_length)); 64 | } 65 | } 66 | 67 | painter->setPen(verticesPen()); 68 | auto transform = painter->worldTransform(); 69 | painter->resetTransform(); 70 | for (auto vit = skeleton->vertices_begin(); vit != skeleton->vertices_end(); ++vit) { 71 | if (vit->has_null_point()) continue; 72 | QPointF point = transform.map(convert(project_plane(vit->point()))); 73 | painter->drawPoint(point); 74 | } 75 | 76 | #ifndef SURF_NDEBUG 77 | if (visible_labels) { 78 | painter->setPen(labelsPen()); 79 | QFont font(painter->font()); 80 | 81 | font.setPointSize(8); 82 | painter->setFont(font); 83 | 84 | for (auto vit = skeleton->vertices_begin(); vit != skeleton->vertices_end(); ++vit) { 85 | if (vit->has_null_point()) continue; 86 | const QPointF p(transform.map(convert( project_plane(vit->point()) ))); 87 | std::string t = "dv#"+std::to_string(vit->id); 88 | painter->drawText(p.x()+4, p.y(), QString::fromStdString(t)); 89 | } 90 | 91 | for (auto hit = skeleton->halfedges_begin(); hit != skeleton->halfedges_end(); ++hit) { 92 | if (hit > hit->opposite()) continue; 93 | 94 | std::string t = "dh#(" + std::to_string(hit->id)+ "," + std::to_string(hit->opposite()->id) + ")"; 95 | 96 | Point_2 pos; 97 | const auto& arc = hit->curve(); 98 | if (arc.type() == typeid(Segment_3)) { 99 | const Segment_3& s = boost::get(arc); 100 | const Segment_2 s2 = project_plane(s); 101 | pos = CGAL::midpoint(s2.source(), s2.target()); 102 | } else { 103 | assert(arc.type() == typeid(Ray_3)); 104 | const Ray_3& r = boost::get(arc); 105 | const Ray_2 r2 = project_plane(r); 106 | pos = CGAL::midpoint(r2.source(), r2.point(ray_length)); 107 | } 108 | const QPointF p(transform.map(convert( pos ))); 109 | painter->drawText(p.x()+4, p.y(), QString::fromStdString(t)); 110 | } 111 | } 112 | #endif 113 | painter->setWorldTransform(transform); 114 | } 115 | 116 | void 117 | SkeletonGraphicsItem:: 118 | updateBoundingBox() { 119 | GConverter convert; 120 | prepareGeometryChange(); 121 | 122 | auto vit = skeleton->vertices_begin(); 123 | if (vit->has_null_point()) { ++vit; }; 124 | assert(! vit->has_null_point()); 125 | 126 | auto bb = GuiPoint(project_plane(vit->point())).bbox(); 127 | ++vit; 128 | for (; vit != skeleton->vertices_end(); ++vit) { 129 | if (vit->has_null_point()) continue; 130 | bb += GuiPoint(project_plane(vit->point())).bbox(); 131 | } 132 | 133 | bounding_rect = convert(bb); 134 | } 135 | 136 | void 137 | SkeletonGraphicsItem:: 138 | modelChanged() { 139 | updateBoundingBox(); 140 | } 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Surfer2 2 | 3 | Surfer2 is an implementation of Aichholzer and Aurenhammer's triangulation 4 | based straight skeleton algorithm using CGAL. It has been developed at 5 | the University of Salzburg's Computational Geometry and Applications Lab. 6 | 7 | # Obtaining the source code 8 | 9 | Clone the git repository: 10 | The logging class is included via a git submodule, so either clone the source 11 | using ` 12 | 13 | git clone --recurse-submodules https://github.com/cgalab/surfer2 14 | 15 | or, if you already have a checkout without the submodule, go to the working copy and 16 | 17 | git submodule update --init --recursive 18 | 19 | If all works well, you should see files both at the top level of your working copy as well as in `surf/easyloggingpp`. 20 | 21 | If you did not clone the source with git, you may have to get easyloggingpp yourself. You can find it at 22 | https://github.com/cgalab/easyloggingpp 23 | 24 | # Build requirements 25 | 26 | To build surfer2, you need a c++ toolchain, cmake, and several libraries including CGAL. 27 | On Debian 10 (buster), installing the following packages is sufficient to build surfer2: 28 | 29 | * build-essential 30 | * cmake 31 | * libboost-graph-dev 32 | * libboost-iostreams-dev 33 | * libcgal-dev 34 | * libcgal-qt5-dev 35 | * libqt5opengl5-dev 36 | * libqt5svg5-dev 37 | * qtbase5-dev 38 | * clang 39 | 40 | 41 | To also build the test suite, you will additionally need `libgtest-dev`. If 42 | you have an older libgtest-dev (<= 1.7), such as on Ubuntu 18.04, you may need 43 | to explicitly install googletest also. 44 | 45 | # Building 46 | 47 | To build surfer, run cmake and make: 48 | 49 | mkdir build && 50 | cd build && 51 | CXX=clang++ cmake -DCMAKE_BUILD_TYPE=Release .. && 52 | make 53 | 54 | This will create a release build, without expensive assertions. To build the debug build, 55 | pass `-DCMAKE_BUILD_TYPE=Debug` to cmake, or nothing at all. If you prefer gcc to clang, don't set 56 | the CXX environment variable when calling cmake. 57 | 58 | Other build options are `TEST_SUITE`, which defaults to off (to enable, pass `-DTEST_SUITE=on` to cmake), 59 | and `BUILD_SHARED_LIBS` and `LIB_ONLY` (also off by default). 60 | 61 | # Running the command line client or gui 62 | 63 | Both the command line client, `surfer`, as well as the gui, `surfgui` take 64 | [GraphML][graphml] files with coordinates as specified in 65 | [Graph-Attributes][graph-attributes] as input. 66 | 67 | ./cc/surfer ../test-data/srpg0000025.graphml 68 | 69 | ./gui/surfgui ../test-data/srpg0000028.graphml 70 | 71 | 72 | They accept several options, one of them is `--help` which prints a list of all 73 | options. The `--component=` option restricts computation to 74 | component number ``, an integer number starting at 0. For polygonal 75 | input, `l` and `r` are accepted for (left and right). The default is `-1`, which 76 | computes all components' straight skeleton, one after the other. A value of `-2` 77 | propagates the wavefront in all components at the same time. 78 | 79 | To create a .graphml file from a .line or .ipe file use 80 | 81 | ord-format 82 | 83 | from the [format-converter repository][format-converter]. 84 | 85 | [graphml]: http://graphml.graphdrawing.org/ 86 | [format-converter]: https://github.com/cgalab/format-converter 87 | [graph-attributes]: https://github.com/cgalab/format-converter/blob/master/GRAPH-ATTRIBUTES.md 88 | 89 | ## GUI 90 | 91 | The gui has a set of buttons near the top. They all have mouse-over tool tips. 92 | The block starting with `i` controls layer visibility: 93 | 94 | * `i` toggles visibility of the input PSLG, 95 | * `I` toggles visibility of the input PSLG labels, 96 | * `w` toggles visibility of the wavefront, 97 | * `t` toggles visibility of the triangulation, 98 | * `T` toggles visibility of the triangulation labels, 99 | * `s` toggles visibility of the straight skeleton (once finalized), 100 | * `S` toggles visibility of the straight skeleton labels (once finalized, and only in the debug build). 101 | 102 | All of those options can be controlled with the keyboard using Alt+<letter>. Use Alt+Shift for capitals. 103 | 104 | The next two blocks deal with time and even handling. The functionality behind 105 | these buttons can also be accessed by simply pressing the corresponding keyboard key. 106 | 107 | * `,` moves the time forward to the time of the next event, 108 | * <backspace> moves the time back to the time of the last event, 109 | * `b` moves the drawing backwards in time. Elements are not necessarily consistent in the past. 110 | * `N` moves the time forwards, including event processing. 111 | * `M` moves the time forwards, not including event processing. As such, the future is not necessarily shown correctly. 112 | * `n` **Moves to the next event time and processes it**. 113 | * <enter> **Processes all remaining events**. 114 | 115 | Lastly, there are a few options that modify the drawing only. These sometimes help in debugging or investigating how things looked just prior to an event. 116 | 117 | * `-` decrease drawing offset (draw things at a time earlier than now), 118 | * `+` increase drawing offset (draw things at a time later than now), 119 | * `=` reset drawing offset. 120 | 121 | # License 122 | 123 | Surfer is free software. You may redistribute it and/or modify 124 | it under the terms of the GNU General Public License (v3). 125 | -------------------------------------------------------------------------------- /surf/src/BasicInput.h: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | #include "surf.h" 21 | 22 | #include "BGLGraph.h" 23 | #include "tools.h" 24 | #include "cgaltools.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | class BasicVertex { 31 | public: 32 | DEBUG_DECL( 33 | const unsigned id; 34 | ) 35 | const Point_2 p; 36 | const unsigned degree; 37 | const unsigned reflex_beveling_add; 38 | 39 | BasicVertex(const Point_2& p_p, unsigned p_degree, unsigned p_id) 40 | : 41 | #ifndef SURF_NDEBUG 42 | id(p_id), 43 | #endif 44 | p(p_p) 45 | , degree(p_degree) 46 | , reflex_beveling_add(0) // number of kinetic vertices to create additionally at reflex vertices. 47 | // At degree one vertices, we always add at least one. 48 | {} 49 | }; 50 | 51 | class BasicEdge { 52 | public: 53 | const unsigned u, v; 54 | const NT weight; 55 | 56 | BasicEdge(unsigned p_u, unsigned p_v, const NT &p_weight=1.0) 57 | : u(p_u) 58 | , v(p_v) 59 | , weight(p_weight) 60 | { 61 | assert(u < v); 62 | } 63 | }; 64 | 65 | class BasicInput { 66 | protected: 67 | using VertexIdxPair = std::pair; 68 | 69 | public: 70 | using Vertex = BasicVertex; 71 | using Edge = BasicEdge; 72 | using VertexList = std::vector; 73 | using EdgeList = std::vector; 74 | 75 | private: 76 | bool finalized = false; 77 | 78 | unsigned num_of_deg1_vertices = 0; 79 | VertexList vertices_; 80 | EdgeList edges_; 81 | std::map edge_map; 82 | /* keep a list of instances of our number type for the different weights. */ 83 | std::set weight_set; 84 | 85 | protected: 86 | /** Add an input vertex to the vertexlist */ 87 | void add_vertex(Vertex&& p) { 88 | assert(!finalized); 89 | if (p.degree == 1) num_of_deg1_vertices++; 90 | vertices_.emplace_back(std::forward(p)); 91 | } 92 | 93 | unsigned num_vertices_() const { return vertices_.size(); }; 94 | unsigned num_edges_() const { return edges_.size(); }; 95 | 96 | /** Add an input edge between vertices to the edgelist */ 97 | void add_edge(unsigned u, unsigned v, const NT& weight=1.0) { 98 | assert(!finalized); 99 | sort_tuple(u,v); 100 | assert(u < vertices_.size()); 101 | assert(v < vertices_.size()); 102 | assert(u!=v); 103 | 104 | auto wsinsert_res = weight_set.insert(weight); 105 | edges_.emplace_back(Edge(u,v, *wsinsert_res.first)); 106 | [[maybe_unused]] auto res = edge_map.emplace(std::pair(VertexIdxPair(u,v), edges_.size()-1)); 107 | assert(res.second); 108 | } 109 | 110 | /* Call once all input has been loaded */ 111 | void finalize() { 112 | assert(!finalized); 113 | finalized = true; 114 | assert_valid(); 115 | } 116 | 117 | bool is_finalized() const { return finalized; }; 118 | 119 | #ifndef SURF_NDEBUG 120 | void assert_valid() const; 121 | #else 122 | void assert_valid() const {}; 123 | #endif 124 | 125 | public: 126 | /* All these methods only work on finalized graphs */ 127 | const VertexList& vertices() const { assert(finalized); return vertices_; }; 128 | const EdgeList& edges() const { assert(finalized); return edges_; }; 129 | 130 | unsigned get_num_of_deg1_vertices() const { 131 | assert(finalized); 132 | return num_of_deg1_vertices; 133 | } 134 | unsigned get_total_degree() const { 135 | assert(finalized); 136 | return edges().size() * 2; 137 | } 138 | unsigned get_num_extra_beveling_vertices() const { 139 | assert(finalized); 140 | /* Includes the extra one vertex we'll need at a minimum for degree-1 vertices. */ 141 | return num_of_deg1_vertices; 142 | } 143 | bool has_edge(unsigned u, unsigned v) const { 144 | assert(finalized); 145 | sort_tuple(u,v); 146 | auto findres = edge_map.find(VertexIdxPair(u, v)); 147 | return findres != edge_map.end(); 148 | } 149 | const Edge& get_edge(unsigned u, unsigned v) const { 150 | assert(finalized); 151 | sort_tuple(u,v); 152 | assert(has_edge(u,v)); 153 | auto findres = edge_map.find(VertexIdxPair(u, v)); 154 | assert(findres != edge_map.end()); 155 | return edges_[findres->second]; 156 | } 157 | 158 | Segment_2 get_segment(const Edge& e) const { 159 | assert(finalized); 160 | return Segment_2(vertices_[e.u].p, vertices_[e.v].p); 161 | } 162 | }; 163 | 164 | class BasicInputFromBGL : public BasicInput { 165 | public: 166 | BasicInputFromBGL(const BGLGraph& graph); 167 | }; 168 | -------------------------------------------------------------------------------- /gui/main.cpp: -------------------------------------------------------------------------------- 1 | /** surfer2 -- a straight skeleton implementation 2 | * 3 | * Copyright 2018, 2019 Peter Palfrader 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include "mainwindow.h" 19 | 20 | #include "SkeletonStructure.h" 21 | #include "tools.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | static struct option long_options[] = { 29 | { "help" , no_argument , 0, 'h'}, 30 | { "skip-to" , required_argument, 0, 's'}, 31 | { "skip-all" , no_argument , 0, 'S'}, 32 | { "skip-until", required_argument, 0, 'T'}, 33 | { "component", required_argument, 0, 'c'}, 34 | { "sk-offset" , required_argument, 0, 'O'}, 35 | { "step-increment", required_argument, 0, 'I'}, 36 | //{ "random-seeed", required_argument, 0, 'R'}, 37 | { 0, 0, 0, 0} 38 | }; 39 | 40 | [[noreturn]] 41 | static void 42 | usage(const char *progname, int err) { 43 | FILE *f = err ? stderr : stdout; 44 | 45 | fprintf(f,"Usage: %s [options] \n", progname); 46 | fprintf(f," Options: --skip-to= Skip to event n at start.\n"); 47 | fprintf(f," --skip-all Skip until end.\n"); 48 | fprintf(f," --skip-until=