├── CMakeLists.txt
├── LICENSE
├── README.md
├── cam-icon.png
├── cam.desktop
├── color_pallete.xml
├── dxf
├── contourtypes.dxf
└── contourtypes.dxf~
├── gcode.ngc
├── icons.qrc
├── icons
├── 3d.svg
├── angle_2p.svg
├── angle_3p.svg
├── applications-system.png
├── arc.svg
├── arrow-down.png
├── back.svg
├── bottom.svg
├── cad.svg
├── cam.svg
├── camcam.svg
├── chamfer.svg
├── circle.svg
├── circle_3p.svg
├── circle_4p.svg
├── circle_4p_off.svg
├── circle_4p_on.svg
├── circlecenter.svg
├── circlecenter_off.svg
├── circlecenter_on.svg
├── copy.svg
├── cubicbezcurve.svg
├── debug.svg
├── delete.svg
├── diameter.svg
├── dimension_edit.svg
├── document-edit.png
├── document-new.png
├── document-open.png
├── document-save.png
├── draw-arrow-back.png
├── draw-arrow-down.png
├── draw-arrow-forward.png
├── draw-arrow-up.png
├── edit-clear.png
├── ellipse.svg
├── emergency-stop
├── empty.svg
├── endpoint.svg
├── endpoint_off.svg
├── endpoint_on.svg
├── extend.svg
├── filled.svg
├── front.svg
├── grid.svg
├── grid_off.svg
├── grid_on.svg
├── help-browser.png
├── horizontal.svg
├── intersection.svg
├── intersection_off.svg
├── intersection_on.svg
├── layer.svg
├── left.svg
├── line.svg
├── linestrip.svg
├── linetype.svg
├── linewidth.svg
├── match_properties.svg
├── midpoint.svg
├── midpoint_off.svg
├── midpoint_on.svg
├── mirror.svg
├── move.svg
├── move_down.svg
├── move_left.svg
├── move_left_down.svg
├── move_left_up.svg
├── move_right.svg
├── move_right_down.svg
├── move_right_up.svg
├── move_up.svg
├── nearest.svg
├── nearest_off.svg
├── nearest_on.svg
├── new.svg
├── non_horizontal.svg
├── offset.svg
├── open.svg
├── opinion-no.png
├── opinion-okay.png
├── ortho.svg
├── ortho_off.svg
├── ortho_on.svg
├── perpendicular.svg
├── perpendicular_off.svg
├── perpendicular_on.svg
├── point.svg
├── polygon.svg
├── preferences-other.png
├── radius.svg
├── rectangle.svg
├── refresh.png
├── right.svg
├── rotate.svg
├── save.svg
├── save_as.svg
├── scale.svg
├── spline.svg
├── stretch.svg
└── test
├── libcavalier
├── CMakeLists.txt
├── LICENSE
├── README.md
├── c_api_include
│ └── cavaliercontours.h
├── examples
│ ├── CMakeLists.txt
│ ├── basicpolylinefunctions.cpp
│ ├── polylinecombine.cpp
│ ├── polylineoffset.cpp
│ └── windingnumber.cpp
├── include
│ └── cavc
│ │ ├── internal
│ │ ├── common.hpp
│ │ └── diagnostics.hpp
│ │ ├── intrcircle2circle2.hpp
│ │ ├── intrlineseg2circle2.hpp
│ │ ├── intrlineseg2lineseg2.hpp
│ │ ├── mathutils.hpp
│ │ ├── plinesegment.hpp
│ │ ├── polyline.hpp
│ │ ├── polylinecombine.hpp
│ │ ├── polylineintersects.hpp
│ │ ├── polylineoffset.hpp
│ │ ├── polylineoffsetislands.hpp
│ │ ├── staticspatialindex.hpp
│ │ ├── vector.hpp
│ │ └── vector2.hpp
├── offsets.cpp
├── offsets.h
├── src
│ └── cavaliercontours.cpp
└── tests
│ ├── CMakeLists.txt
│ ├── benchmarks
│ ├── CMakeLists.txt
│ ├── areabenchmarks.cpp
│ ├── benchmarkprofiles.h
│ ├── clipper.cmake
│ ├── clipperbenchmarks.cpp
│ ├── combinebenchmarks.cpp
│ ├── extentsbenchmarks.cpp
│ ├── googlebenchmark.cmake
│ ├── offsetbenchmarks.cpp
│ ├── pathlengthbenchmarks.cpp
│ ├── spatialindexbenchmarks.cpp
│ └── windingnumberbenchmarks.cpp
│ ├── polylinefactory
│ ├── CMakeLists.txt
│ ├── include
│ │ └── polylinefactory.hpp
│ └── src
│ │ └── polylinefactory.cpp
│ └── tests
│ ├── CMakeLists.txt
│ ├── cavc_combine_plines_tests.cpp
│ ├── cavc_parallel_offset_tests.cpp
│ ├── cavc_pline_function_tests.cpp
│ ├── cavc_pline_tests.cpp
│ ├── googletest.cmake
│ ├── include
│ ├── c_api_test_helpers.hpp
│ └── testhelpers.hpp
│ └── staticspatialindex_tests.cpp
├── libcontour
├── contours.cpp
└── contours.h
├── libdata
├── variable.cpp
└── variable.h
├── libdialog
├── CMakeLists.txt
├── COPYING
├── README.md
├── doc
│ ├── message.md
│ ├── notify.md
│ ├── open_file.md
│ ├── pfd.md
│ ├── save_file.md
│ └── select_folder.md
├── examples
│ ├── Makefile
│ ├── example.cpp
│ ├── example.vcxproj
│ ├── kill.cpp
│ └── kill.vcxproj
└── portable-file-dialogs.h
├── libdxfrw
├── Makefile.am
├── Makefile.in
├── drw_base.h
├── drw_classes.cpp
├── drw_classes.h
├── drw_entities.cpp
├── drw_entities.h
├── drw_header.cpp
├── drw_header.h
├── drw_interface.h
├── drw_objects.cpp
├── drw_objects.h
├── dx_data.h
├── dx_iface.cpp
├── dx_iface.h
├── intern
│ ├── drw_cptable932.h
│ ├── drw_cptable936.h
│ ├── drw_cptable949.h
│ ├── drw_cptable950.h
│ ├── drw_cptables.h
│ ├── drw_dbg.cpp
│ ├── drw_dbg.h
│ ├── drw_textcodec.cpp
│ ├── drw_textcodec.h
│ ├── dwgbuffer.cpp
│ ├── dwgbuffer.h
│ ├── dwgreader.cpp
│ ├── dwgreader.h
│ ├── dwgreader15.cpp
│ ├── dwgreader15.h
│ ├── dwgreader18.cpp
│ ├── dwgreader18.h
│ ├── dwgreader21.cpp
│ ├── dwgreader21.h
│ ├── dwgreader24.cpp
│ ├── dwgreader24.h
│ ├── dwgreader27.cpp
│ ├── dwgreader27.h
│ ├── dwgutil.cpp
│ ├── dwgutil.h
│ ├── dxfreader.cpp
│ ├── dxfreader.h
│ ├── dxfwriter.cpp
│ ├── dxfwriter.h
│ ├── rscodec.cpp
│ └── rscodec.h
├── libdwgr.cpp
├── libdwgr.h
├── libdxfrw.cpp
├── libdxfrw.h
├── main.txt
└── main_doc.h
├── libgcode
├── gcode.cpp
└── gcode.h
├── libocc
├── draw_primitives.cpp
├── draw_primitives.h
├── opencascade.cpp
└── opencascade.h
├── libspline
├── bezier_spline.cpp
├── bezier_spline.h
├── cubic_spline.cpp
├── cubic_spline.h
├── spline.cpp
└── spline.h
├── main.cpp
├── mainwindow.cpp
├── mainwindow.cpp.autosave
├── mainwindow.h
├── mainwindow.ui
└── qt-dxf.pro
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 | set(PROJECT "cam") # The name of the file excluding .c or .h
3 | project(${PROJECT} C CXX)
4 |
5 | set(CMAKE_INCLUDE_CURRENT_DIR ON)
6 |
7 | set(CMAKE_AUTOUIC ON)
8 | set(CMAKE_AUTOMOC ON)
9 | set(CMAKE_AUTORCC ON)
10 |
11 | set(CMAKE_CXX_STANDARD 20)
12 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
13 |
14 | # Set variables.
15 | set(SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
16 | set(BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}")
17 |
18 | # Set includepaths for header files etc.
19 | include_directories(${SRC_DIR})
20 | include_directories(${SRC_DIR}/libspline)
21 | include_directories(${SRC_DIR}/libocc)
22 | include_directories(${SRC_DIR}/libgcode)
23 | include_directories(${SRC_DIR}/libdxfrw)
24 | include_directories(${SRC_DIR}/libdialog)
25 | include_directories(${SRC_DIR}/libdata)
26 | include_directories(${SRC_DIR}/libcontour)
27 | include_directories(${SRC_DIR}/libcavalier)
28 | include_directories(${SRC_DIR}/libcavalier/src)
29 | include_directories(${SRC_DIR}/libcavalier/include/cavc)
30 | include_directories(${SRC_DIR}/libcavalier/include/cavc/internal)
31 | include_directories(${SRC_DIR}/icons)
32 |
33 | # Set path to your opencascade lib.
34 | # https://github.com/tpaviot/oce/releases/download/official-upstream-packages/opencascade-7.4.0.tgz
35 | #
36 | # Do the build process, and make install. Then set filepath.
37 |
38 | include_directories(/home/user/linuxcnc/cmake/vendor/oce-upstream-V7_5_0beta/inc)
39 | include_directories(/home/user/linuxcnc/cmake/vendor/oce-upstream-V7_5_0beta/oce-upstream-V7_5_0beta/src)
40 |
41 |
42 | find_package(OpenGL REQUIRED)
43 | find_package(GLUT REQUIRED)
44 |
45 | include_directories("/usr/include/eigen3")
46 | find_package (Eigen3 3.3 REQUIRED)
47 |
48 | # Opencascade
49 | find_package (OpenCASCADE REQUIRED)
50 |
51 | SET(OpenCASCADE_LIBS
52 | TKGeomAlgo TKMesh TKHLR TKBO TKShHealing
53 | TKPrim
54 | TKernel TKMath TKTopAlgo TKService
55 | TKG2d TKG3d TKV3d TKOpenGl
56 | TKBRep TKXSBase TKGeomBase TKGeomAlgo
57 | TKXSDRAW
58 | TKLCAF TKXCAF TKCAF TKVCAF
59 | TKCDF TKBin TKBinL TKBinXCAF TKXml TKXmlL TKXmlXCAF
60 | # -- IGES support
61 | TKIGES
62 | # -- STEP support
63 | TKSTEP TKXDESTEP TKXDEIGES TKSTEPAttr TKSTEPBase TKSTEP209
64 | # -- STL support
65 | TKSTL
66 | # -- OBJ/glTF support
67 | TKRWMesh TKMeshVS
68 | # -- VRML support
69 | TKVRML
70 | # -- ViewerTest
71 | TKViewerTest
72 | )
73 |
74 | # Qt
75 | set(CMAKE_INCLUDE_CURRENT_DIR ON)
76 | set(CMAKE_AUTOUIC ON)
77 | set(CMAKE_AUTOMOC ON)
78 | set(CMAKE_AUTORCC ON)
79 | set(CMAKE_CXX_STANDARD 17)
80 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
81 | set(CMAKE_PREFIX_PATH "/opt/qt-creator/5.15.1/gcc_64/lib/cmake")
82 | include_directories("/opt/qt-creator/5.15.1/gcc_64/include/QtWidgets")
83 | include_directories("/opt/qt-creator/5.15.1/gcc_64/include/QtGui")
84 | include_directories("/opt/qt-creator/5.15.1/gcc_64/include")
85 | include_directories("/opt/qt-creator/5.15.1/gcc_64/include/QtCore")
86 |
87 | find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets OpenGL REQUIRED)
88 | find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets OpenGL REQUIRED)
89 |
90 | set(PROJECT_SOURCES
91 | ${SRC_DIR}/main.cpp
92 | ${SRC_DIR}/mainwindow.cpp
93 | ${SRC_DIR}/mainwindow.h
94 | ${SRC_DIR}/mainwindow.ui
95 | ${SRC_DIR}/libcavalier/offsets.cpp
96 | ${SRC_DIR}/libcontour/contours.cpp
97 | ${SRC_DIR}/libdata/variable.cpp
98 | ${SRC_DIR}/libdxfrw/drw_classes.cpp
99 | ${SRC_DIR}/libdxfrw/drw_entities.cpp
100 | ${SRC_DIR}/libdxfrw/drw_header.cpp
101 | ${SRC_DIR}/libdxfrw/drw_objects.cpp
102 | ${SRC_DIR}/libdxfrw/dx_iface.cpp
103 | ${SRC_DIR}/libdxfrw/intern/drw_dbg.cpp
104 | ${SRC_DIR}/libdxfrw/intern/drw_textcodec.cpp
105 | ${SRC_DIR}/libdxfrw/intern/dwgbuffer.cpp
106 | ${SRC_DIR}/libdxfrw/intern/dwgreader.cpp
107 | ${SRC_DIR}/libdxfrw/intern/dwgreader15.cpp
108 | ${SRC_DIR}/libdxfrw/intern/dwgreader18.cpp
109 | ${SRC_DIR}/libdxfrw/intern/dwgreader21.cpp
110 | ${SRC_DIR}/libdxfrw/intern/dwgreader24.cpp
111 | ${SRC_DIR}/libdxfrw/intern/dwgreader27.cpp
112 | ${SRC_DIR}/libdxfrw/intern/dwgutil.cpp
113 | ${SRC_DIR}/libdxfrw/intern/dxfreader.cpp
114 | ${SRC_DIR}/libdxfrw/intern/dxfwriter.cpp
115 | ${SRC_DIR}/libdxfrw/intern/rscodec.cpp
116 | ${SRC_DIR}/libdxfrw/libdwgr.cpp
117 | ${SRC_DIR}/libdxfrw/libdxfrw.cpp
118 | ${SRC_DIR}/libgcode/gcode.cpp
119 | ${SRC_DIR}/libocc/draw_primitives.cpp
120 | ${SRC_DIR}/libspline/bezier_spline.cpp
121 | ${SRC_DIR}/libspline/cubic_spline.cpp
122 | ${SRC_DIR}/libspline/spline.cpp
123 | ${SRC_DIR}/libocc/opencascade.cpp
124 | )
125 |
126 | if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
127 | qt_add_executable(${PROJECT}
128 | ${PROJECT_SOURCES}
129 | )
130 | else()
131 | if(ANDROID)
132 | add_library(${PROJECT} SHARED
133 | ${PROJECT_SOURCES}
134 | )
135 | else()
136 | add_executable(${PROJECT}
137 | ${PROJECT_SOURCES}
138 | )
139 | endif()
140 | endif()
141 |
142 |
143 | # Link.
144 | target_link_libraries(${PROJECT_NAME} PRIVATE pthread
145 | ${OpenCASCADE_LIBS} Qt5::Widgets Qt5::OpenGL ${OPENGL_LIBRARIES} ${GLUT_LIBRARY}
146 | )
147 |
148 | # Make install
149 | # install(TARGETS ${PROJECT} DESTINATION ${BUILD_DIR}/)
150 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | []()
2 |
3 | 
4 |
5 | Now available as .deb package for linux, including desktop launcher.
6 | https://github.com/grotius-cnc/simple_cam/releases/tag/1.0.0
7 |
8 | Currently implemented:
9 |
10 | - Dxf read, write and save example.
11 | - Opencascade cad view, selecting base contours by mouse click.
12 | - Supports offset by cad-layer.
13 | - Supports contour offset values: -, + or 0 (no offset).
14 | - Depth sequence, "keep parts together" kpt output algorimte.
15 | - Lead-in, lead-out for offsets.
16 | - Supports single-open, multiple-open, single-closed and multiple-closed primitive offsets.
17 | - Generates a gcode.ngc output file located in build directory.
18 | - Can add line-numbers to the gcode.ngc file.
19 | - Supports pockets with or without islands. Base offset + Internal pocket offset.
20 | - Supports multiple operation output.
21 | - Supports second head + xyz tool offsets.
22 | - Shows rapids.
23 |
24 |
25 |
--------------------------------------------------------------------------------
/cam-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/cam-icon.png
--------------------------------------------------------------------------------
/cam.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=Cam
3 | Exec=./cam
4 | Type=Application
5 | Categories=Development;
6 | Comment="You can clean up hal with : Halrun -U"
7 | Icon=/opt/cam/cam-icon.png
8 | Path=/opt/cam/build-qt-dxf-Desktop-Debug
9 |
--------------------------------------------------------------------------------
/icons.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | icons/3d.svg
4 | icons/angle_2p.svg
5 | icons/angle_3p.svg
6 | icons/applications-system.png
7 | icons/arc.svg
8 | icons/arrow-down.png
9 | icons/back.svg
10 | icons/bottom.svg
11 | icons/cad.svg
12 | icons/cam.svg
13 | icons/camcam.svg
14 | icons/chamfer.svg
15 | icons/circle_3p.svg
16 | icons/circle_4p_off.svg
17 | icons/circle_4p_on.svg
18 | icons/circle_4p.svg
19 | icons/circle.svg
20 | icons/circlecenter_off.svg
21 | icons/circlecenter_on.svg
22 | icons/circlecenter.svg
23 | icons/copy.svg
24 | icons/cubicbezcurve.svg
25 | icons/debug.svg
26 | icons/delete.svg
27 | icons/diameter.svg
28 | icons/dimension_edit.svg
29 | icons/document-edit.png
30 | icons/document-new.png
31 | icons/document-open.png
32 | icons/document-save.png
33 | icons/draw-arrow-back.png
34 | icons/draw-arrow-down.png
35 | icons/draw-arrow-forward.png
36 | icons/draw-arrow-up.png
37 | icons/edit-clear.png
38 | icons/ellipse.svg
39 | icons/emergency-stop
40 | icons/empty.svg
41 | icons/endpoint_off.svg
42 | icons/endpoint_on.svg
43 | icons/endpoint.svg
44 | icons/extend.svg
45 | icons/filled.svg
46 | icons/front.svg
47 | icons/grid_off.svg
48 | icons/grid_on.svg
49 | icons/grid.svg
50 | icons/help-browser.png
51 | icons/horizontal.svg
52 | icons/intersection_off.svg
53 | icons/intersection_on.svg
54 | icons/intersection.svg
55 | icons/layer.svg
56 | icons/left.svg
57 | icons/line.svg
58 | icons/linestrip.svg
59 | icons/linetype.svg
60 | icons/linewidth.svg
61 | icons/match_properties.svg
62 | icons/midpoint_off.svg
63 | icons/midpoint_on.svg
64 | icons/midpoint.svg
65 | icons/mirror.svg
66 | icons/move_down.svg
67 | icons/move_left_down.svg
68 | icons/move_left_up.svg
69 | icons/move_left.svg
70 | icons/move_right_down.svg
71 | icons/move_right_up.svg
72 | icons/move_right.svg
73 | icons/move_up.svg
74 | icons/move.svg
75 | icons/nearest_off.svg
76 | icons/nearest_on.svg
77 | icons/nearest.svg
78 | icons/new.svg
79 | icons/non_horizontal.svg
80 | icons/offset.svg
81 | icons/open.svg
82 | icons/opinion-no.png
83 | icons/opinion-okay.png
84 | icons/ortho_off.svg
85 | icons/ortho_on.svg
86 | icons/ortho.svg
87 | icons/perpendicular_off.svg
88 | icons/perpendicular_on.svg
89 | icons/perpendicular.svg
90 | icons/point.svg
91 | icons/polygon.svg
92 | icons/preferences-other.png
93 | icons/radius.svg
94 | icons/rectangle.svg
95 | icons/refresh.png
96 | icons/right.svg
97 | icons/rotate.svg
98 | icons/save_as.svg
99 | icons/save.svg
100 | icons/scale.svg
101 | icons/spline.svg
102 | icons/stretch.svg
103 | icons/test
104 |
105 |
106 |
--------------------------------------------------------------------------------
/icons/applications-system.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/applications-system.png
--------------------------------------------------------------------------------
/icons/arrow-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/arrow-down.png
--------------------------------------------------------------------------------
/icons/document-edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/document-edit.png
--------------------------------------------------------------------------------
/icons/document-new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/document-new.png
--------------------------------------------------------------------------------
/icons/document-open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/document-open.png
--------------------------------------------------------------------------------
/icons/document-save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/document-save.png
--------------------------------------------------------------------------------
/icons/draw-arrow-back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/draw-arrow-back.png
--------------------------------------------------------------------------------
/icons/draw-arrow-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/draw-arrow-down.png
--------------------------------------------------------------------------------
/icons/draw-arrow-forward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/draw-arrow-forward.png
--------------------------------------------------------------------------------
/icons/draw-arrow-up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/draw-arrow-up.png
--------------------------------------------------------------------------------
/icons/edit-clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/edit-clear.png
--------------------------------------------------------------------------------
/icons/emergency-stop:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/emergency-stop
--------------------------------------------------------------------------------
/icons/help-browser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/help-browser.png
--------------------------------------------------------------------------------
/icons/opinion-no.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/opinion-no.png
--------------------------------------------------------------------------------
/icons/opinion-okay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/opinion-okay.png
--------------------------------------------------------------------------------
/icons/preferences-other.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/preferences-other.png
--------------------------------------------------------------------------------
/icons/refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grotius-cnc/cam/27d1270f6d2936b33bc34ff7299f6a6180c9cf87/icons/refresh.png
--------------------------------------------------------------------------------
/icons/test:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/libcavalier/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | # detect if CavalierContours is a subproject or not
4 | # do not include examples, tests, and benchmarks in that case
5 | if (NOT DEFINED PROJECT_NAME)
6 | set(NOT_SUBPROJECT ON)
7 | endif()
8 |
9 |
10 | project(CavalierContours VERSION 0.1)
11 | set(CMAKE_CXX_STANDARD 14)
12 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
13 |
14 | option(CAVC_HEADER_ONLY "C++ header only library of CavalierContours, if ON then no library is built" OFF)
15 | option(CAVC_BUILD_SHARED_LIB "Build the C API CavalierContours dynamic shared library (SET OFF for static library)" ON)
16 |
17 | if(CAVC_HEADER_ONLY)
18 | set(CAVC_CPP_HEADER_ONLY_LIB ${PROJECT_NAME})
19 | else()
20 | set(CAVC_CPP_HEADER_ONLY_LIB ${PROJECT_NAME}_header_only)
21 | set(CAVC_C_API_LIB ${PROJECT_NAME})
22 | endif()
23 |
24 |
25 |
26 | add_library(${CAVC_CPP_HEADER_ONLY_LIB} INTERFACE)
27 | target_include_directories(${CAVC_CPP_HEADER_ONLY_LIB}
28 | INTERFACE include/)
29 |
30 | if(NOT CAVC_HEADER_ONLY)
31 | if(CAVC_BUILD_SHARED_LIB)
32 | add_library(${CAVC_C_API_LIB}
33 | SHARED src/cavaliercontours.cpp)
34 | else()
35 | add_library(${CAVC_C_API_LIB}
36 | STATIC src/cavaliercontours.cpp)
37 | target_compile_definitions(${CAVC_C_API_LIB}
38 | PUBLIC CAVC_STATIC_LIB)
39 | endif()
40 | # compiler warnings, note: not using /W4 on MSVC since it emits warning due to default of /W3
41 | # from cmake
42 | if(MSVC)
43 | if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
44 | target_compile_options(${CAVC_C_API_LIB} PRIVATE
45 | /clang:-Werror /clang:-Wall /clang:-Wextra /clang:-pedantic-errors /clang:-Wsign-conversion /clang:-Wconversion)
46 | endif()
47 | target_compile_options(${CAVC_C_API_LIB} PRIVATE
48 | /WX)
49 | else()
50 | target_compile_options(${CAVC_C_API_LIB} PRIVATE
51 | -Werror -Wall -Wextra -pedantic-errors -Wsign-conversion -Wconversion)
52 | endif()
53 | target_compile_definitions(${CAVC_C_API_LIB}
54 | PRIVATE CAVC_EXPORTS)
55 | target_include_directories(${CAVC_C_API_LIB}
56 | PUBLIC c_api_include/)
57 | target_link_libraries(${CAVC_C_API_LIB}
58 | PRIVATE ${CAVC_CPP_HEADER_ONLY_LIB})
59 | endif()
60 |
61 | if (NOT_SUBPROJECT AND NOT CAVC_HEADER_ONLY)
62 | # output all libraries and executables to the root build dir for simplicity in running tests with dynamic libs
63 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
64 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
65 | add_subdirectory(examples)
66 | include(CTest)
67 | enable_testing()
68 | add_subdirectory(tests)
69 | endif()
70 |
--------------------------------------------------------------------------------
/libcavalier/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Jedidiah Buck McCready
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/libcavalier/examples/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 | macro(add_example name)
3 | add_executable(${name} ${name}.cpp)
4 | target_link_libraries(${name}
5 | PRIVATE ${CAVC_CPP_HEADER_ONLY_LIB})
6 | endmacro()
7 |
8 | add_example(polylinecombine)
9 | add_example(polylineoffset)
10 | add_example(windingnumber)
11 | add_example(basicpolylinefunctions)
12 |
--------------------------------------------------------------------------------
/libcavalier/examples/basicpolylinefunctions.cpp:
--------------------------------------------------------------------------------
1 | #include "cavc/polyline.hpp"
2 |
3 | using namespace cavc;
4 |
5 | int main(int argc, char *argv[]) {
6 | (void)argc;
7 | (void)argv;
8 |
9 | // closed polyline representing a circle going counter clockwise
10 | double radius = 10.0;
11 | Polyline ccwCircle;
12 | ccwCircle.addVertex(0, 0, 1);
13 | ccwCircle.addVertex(2.0 * radius, 0, 1);
14 | ccwCircle.isClosed() = true;
15 |
16 | // closed polyline representing a circle going clockwise
17 | Polyline cwCircle;
18 | cwCircle.addVertex(0, 0, -1);
19 | cwCircle.addVertex(2.0 * radius, 0, -1);
20 | cwCircle.isClosed() = true;
21 |
22 | // path length of polyline
23 | double length = getPathLength(ccwCircle);
24 | assert(utils::fuzzyEqual(length, 2.0 * utils::pi() * radius));
25 |
26 | // signed area of closed polyline (area is positive if counter clockwise, negative if clockwise)
27 | double area = getArea(ccwCircle);
28 | assert(utils::fuzzyEqual(area, utils::pi() * radius * radius));
29 | assert(utils::fuzzyEqual(getArea(cwCircle), -area));
30 |
31 | // polyline extents
32 | AABB extents = getExtents(ccwCircle);
33 | assert(utils::fuzzyEqual(extents.xMin, 0.0));
34 | assert(utils::fuzzyEqual(extents.yMin, -radius));
35 | assert(utils::fuzzyEqual(extents.xMax, 2.0 * radius));
36 | assert(utils::fuzzyEqual(extents.yMax, radius));
37 |
38 | // Closest point on polyline to a point given
39 | ClosestPoint closestPoint(ccwCircle, Vector2(radius, 10.0 * radius));
40 | // index is the starting vertex index of the closest segment (going clockwise so arc starting at
41 | // the second vertex is closest)
42 | assert(closestPoint.index() == 1);
43 | assert(fuzzyEqual(closestPoint.point(), Vector2(radius, radius)));
44 | assert(utils::fuzzyEqual(closestPoint.distance(), 9.0 * radius));
45 | }
46 |
--------------------------------------------------------------------------------
/libcavalier/examples/polylinecombine.cpp:
--------------------------------------------------------------------------------
1 | #include "cavc/polylinecombine.hpp"
2 |
3 | using namespace cavc;
4 |
5 | int main(int argc, char *argv[]) {
6 | (void)argc;
7 | (void)argv;
8 |
9 | // closed polyline representing a circle
10 | Polyline circle;
11 | circle.addVertex(0, 1, 1);
12 | circle.addVertex(10, 1, 1);
13 | circle.isClosed() = true;
14 |
15 | // closed polyline representing a rectangle (overlaps with the circle)
16 | Polyline rectangle;
17 | rectangle.addVertex(3, -10, 0);
18 | rectangle.addVertex(6, -10, 0);
19 | rectangle.addVertex(6, 10, 0);
20 | rectangle.addVertex(3, 10, 0);
21 | rectangle.isClosed() = true;
22 |
23 | CombineResult unionResult = combinePolylines(circle, rectangle, PlineCombineMode::Union);
24 | CombineResult excludeResult =
25 | combinePolylines(circle, rectangle, PlineCombineMode::Exclude);
26 | CombineResult intersectResult =
27 | combinePolylines(circle, rectangle, PlineCombineMode::Intersect);
28 | CombineResult xorResult = combinePolylines(circle, rectangle, PlineCombineMode::XOR);
29 | }
30 |
--------------------------------------------------------------------------------
/libcavalier/examples/polylineoffset.cpp:
--------------------------------------------------------------------------------
1 | #include "cavc/polylineoffset.hpp"
2 |
3 | int main(int argc, char *argv[]) {
4 | (void)argc;
5 | (void)argv;
6 | // input polyline
7 | cavc::Polyline input;
8 | // add vertexes as (x, y, bulge)
9 | input.addVertex(0, 25, 1);
10 | input.addVertex(0, 0, 0);
11 | input.addVertex(2, 0, 1);
12 | input.addVertex(10, 0, -0.5);
13 | input.addVertex(8, 9, 0.374794619217547);
14 | input.addVertex(21, 0, 0);
15 | input.addVertex(23, 0, 1);
16 | input.addVertex(32, 0, -0.5);
17 | input.addVertex(28, 0, 0.5);
18 | input.addVertex(39, 21, 0);
19 | input.addVertex(28, 12, 0);
20 | input.isClosed() = true;
21 |
22 | // compute the resulting offset polylines, offset = 3
23 | std::vector> results = cavc::parallelOffset(input, 3.0);
24 | }
25 |
--------------------------------------------------------------------------------
/libcavalier/examples/windingnumber.cpp:
--------------------------------------------------------------------------------
1 | #include "cavc/polyline.hpp"
2 |
3 | using namespace cavc;
4 |
5 | int main(int argc, char *argv[]) {
6 | (void)argc;
7 | (void)argv;
8 |
9 | // rectangle going counter clockwise
10 | Polyline ccwRectangle;
11 | ccwRectangle.addVertex(-10, -10, 0);
12 | ccwRectangle.addVertex(10, -10, 0);
13 | ccwRectangle.addVertex(10, 10, 0);
14 | ccwRectangle.addVertex(-10, 10, 0);
15 | ccwRectangle.isClosed() = true;
16 |
17 | int wn = getWindingNumber(ccwRectangle, Vector2(1, 1));
18 | assert(wn == 1);
19 | wn = getWindingNumber(ccwRectangle, Vector2(12, 12));
20 | assert(wn == 0);
21 |
22 | // make rectangle go clockwise
23 | Polyline cwRectangle = ccwRectangle;
24 | invertDirection(cwRectangle);
25 | wn = getWindingNumber(cwRectangle, Vector2(1, 1));
26 | assert(wn == -1);
27 |
28 | // wrap around the point twice (self intersecting polyline)
29 | Polyline ccwTwiceWrapping = ccwRectangle;
30 | ccwTwiceWrapping.addVertex(-12, -12, 1);
31 | ccwTwiceWrapping.addVertex(12, -12, 0);
32 | ccwTwiceWrapping.addVertex(12, 12, 0);
33 | ccwTwiceWrapping.addVertex(-12, 12, 0);
34 | wn = getWindingNumber(ccwTwiceWrapping, Vector2(1, 1));
35 | assert(wn == 2);
36 |
37 | Polyline cwTwiceWrapping = ccwTwiceWrapping;
38 | invertDirection(cwTwiceWrapping);
39 | wn = getWindingNumber(cwTwiceWrapping, Vector2(1, 1));
40 | assert(wn == -2);
41 |
42 | // winding number function always returns 0 for open polylines whose start and end points do not
43 | // touch (even if geometrically it does wrap)
44 | ccwTwiceWrapping.isClosed() = false;
45 | wn = getWindingNumber(ccwTwiceWrapping, Vector2(1, 1));
46 | assert(wn == 0);
47 | }
48 |
--------------------------------------------------------------------------------
/libcavalier/include/cavc/internal/common.hpp:
--------------------------------------------------------------------------------
1 | #ifndef CAVC_INTERNAL_COMMON_HPP
2 | #define CAVC_INTERNAL_COMMON_HPP
3 | #include
4 | #include
5 | #include
6 |
7 | namespace cavc {
8 | namespace internal {
9 | #define CAVC_ASSERT(cond, msg) assert(cond &&msg)
10 |
11 | template inline void hashCombine(std::size_t &seed, const T &val) {
12 | // copied from boost hash_combine, it's not the best hash combine but it's very simple
13 | // https://stackoverflow.com/questions/35985960/c-why-is-boosthash-combine-the-best-way-to-combine-hash-values
14 | seed ^= std::hash()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
15 | }
16 |
17 | struct IndexPairHash {
18 | inline std::size_t operator()(std::pair const &pair) const {
19 | std::size_t seed = 0;
20 | hashCombine(seed, pair.first);
21 | hashCombine(seed, pair.second);
22 | return seed;
23 | }
24 | };
25 | } // namespace internal
26 | } // namespace cavc
27 |
28 | #endif // CAVC_INTERNAL_COMMON_HPP
29 |
--------------------------------------------------------------------------------
/libcavalier/include/cavc/internal/diagnostics.hpp:
--------------------------------------------------------------------------------
1 | #ifndef CAVC_INTERNAL_DIAGNOSTICS_HPP
2 | #define CAVC_INTERNAL_DIAGNOSTICS_HPP
3 | #include "../polyline.hpp"
4 | #include
5 | #include
6 | #include
7 | namespace cavc {
8 | namespace internal {
9 | template
10 | std::string printVertexesToInitializerList(::cavc::Polyline const &pline) {
11 | std::stringstream ss;
12 | ss << std::setprecision(14) << "{ ";
13 | for (auto const &v : pline.vertexes()) {
14 | ss << "{ " << v.x() << ", " << v.y() << ", " << v.bulge() << " },\n";
15 | }
16 | std::string result = ss.str();
17 | result.pop_back();
18 | result.pop_back();
19 | result.push_back(' ');
20 | result.push_back('}');
21 | return result;
22 | }
23 | } // namespace internal
24 | } // namespace cavc
25 | #endif // CAVC_INTERNAL_DIAGNOSTICS_HPP
26 |
--------------------------------------------------------------------------------
/libcavalier/include/cavc/intrcircle2circle2.hpp:
--------------------------------------------------------------------------------
1 | #ifndef CAVC_INTRCIRCLE2CIRCLE2_HPP
2 | #define CAVC_INTRCIRCLE2CIRCLE2_HPP
3 | #include "vector2.hpp"
4 | namespace cavc {
5 | enum class Circle2Circle2IntrType {
6 | // no intersect between circles
7 | NoIntersect,
8 | // one intersect between circles (tangent)
9 | OneIntersect,
10 | // two intersects between circles
11 | TwoIntersects,
12 | // circles are coincident
13 | Coincident
14 | };
15 |
16 | template struct IntrCircle2Circle2Result {
17 | // type of intersect
18 | Circle2Circle2IntrType intrType;
19 | // first intersect point if intrType is OneIntersect or TwoIntersects, undefined otherwise
20 | Vector2 point1;
21 | // second intersect point if intrType is TwoIntersects, undefined otherwise
22 | Vector2 point2;
23 | };
24 |
25 | // Find intersect between two circles in 2D.
26 | template
27 | IntrCircle2Circle2Result intrCircle2Circle2(Real radius1, Vector2 const ¢er1,
28 | Real radius2, Vector2 const ¢er2) {
29 | // Reference algorithm: http://paulbourke.net/geometry/circlesphere/
30 |
31 | IntrCircle2Circle2Result result;
32 | Vector2 cv = center2 - center1;
33 | Real d2 = dot(cv, cv);
34 | Real d = std::sqrt(d2);
35 | if (d < utils::realThreshold()) {
36 | // same center position
37 | if (utils::fuzzyEqual(radius1, radius2)) {
38 | result.intrType = Circle2Circle2IntrType::Coincident;
39 | } else {
40 | result.intrType = Circle2Circle2IntrType::NoIntersect;
41 | }
42 | } else {
43 | // different center position
44 | if (d > radius1 + radius2 + utils::realThreshold() ||
45 | d + utils::realThreshold() < std::abs(radius1 - radius2)) {
46 | result.intrType = Circle2Circle2IntrType::NoIntersect;
47 | } else {
48 | Real rad1Sq = radius1 * radius1;
49 | Real a = (rad1Sq - radius2 * radius2 + d2) / (Real(2) * d);
50 | Vector2 midPoint = center1 + a * cv / d;
51 | Real diff = rad1Sq - a * a;
52 | if (diff < Real(0)) {
53 | result.intrType = Circle2Circle2IntrType::OneIntersect;
54 | result.point1 = midPoint;
55 | } else {
56 | Real h = std::sqrt(diff);
57 | Real hOverD = h / d;
58 | Real xTerm = hOverD * cv.y();
59 | Real yTerm = hOverD * cv.x();
60 | Real x1 = midPoint.x() + xTerm;
61 | Real y1 = midPoint.y() - yTerm;
62 | Real x2 = midPoint.x() - xTerm;
63 | Real y2 = midPoint.y() + yTerm;
64 | result.point1 = Vector2(x1, y1);
65 | result.point2 = Vector2(x2, y2);
66 | if (fuzzyEqual(result.point1, result.point2)) {
67 | result.intrType = Circle2Circle2IntrType::OneIntersect;
68 | } else {
69 | result.intrType = Circle2Circle2IntrType::TwoIntersects;
70 | }
71 | }
72 | }
73 | }
74 |
75 | return result;
76 | }
77 | } // namespace cavc
78 |
79 | #endif // CAVC_INTRCIRCLE2CIRCLE2_HPP
80 |
--------------------------------------------------------------------------------
/libcavalier/include/cavc/intrlineseg2circle2.hpp:
--------------------------------------------------------------------------------
1 | #ifndef CAVC_INTRLINESEG2CIRCLE2_HPP
2 | #define CAVC_INTRLINESEG2CIRCLE2_HPP
3 | #include "vector2.hpp"
4 | namespace cavc {
5 | template struct IntrLineSeg2Circle2Result {
6 | // number of interescts found (0, 1, or 2)
7 | int numIntersects;
8 | // parametric value for first intersect (if numIntersects > 0) otherwise undefined
9 | Real t0;
10 | // parametric value for second intersect (if numintersects > 1) otherwise undefined
11 | Real t1;
12 | };
13 |
14 | // Gets the intersect between a segment and a circle, returning the parametric solution t to the
15 | // segment equation P(t) = v1 + t * (v2 - v1) for t = 0 to t = 1, if t < 0 or t > 1 then intersect
16 | // occurs only when extending the segment out past the points given (if t < 0 intersect nearest v1,
17 | // if t > 0 then intersect nearest v2), intersects are "sticky" and "snap" to tangent points, e.g. a
18 | // segment very close to being a tangent will be returned as a single intersect point
19 | template
20 | IntrLineSeg2Circle2Result intrLineSeg2Circle2(Vector2 const &p0,
21 | Vector2 const &p1, Real radius,
22 | Vector2 const &circleCenter) {
23 | // This function solves for t by substituting the parametric equations for the segment x = v1.X +
24 | // t * (v2.X - v1.X) and y = v1.Y + t * (v2.Y - v1.Y) for t = 0 to t = 1 into the circle equation
25 | // (x-h)^2 + (y-k)^2 = r^2 and then solving the resulting equation in the form a*t^2 + b*t + c = 0
26 | // using the quadratic formula
27 | IntrLineSeg2Circle2Result result;
28 | Real dx = p1.x() - p0.x();
29 | Real dy = p1.y() - p0.y();
30 | Real h = circleCenter.x();
31 | Real k = circleCenter.y();
32 |
33 | Real a = dx * dx + dy * dy;
34 | if (std::abs(a) < utils::realThreshold()) {
35 | // v1 = v2, test if point is on the circle
36 | Real xh = p0.x() - h;
37 | Real yk = p0.y() - k;
38 | if (utils::fuzzyEqual(xh * xh + yk * yk, radius * radius)) {
39 | result.numIntersects = 1;
40 | result.t0 = Real(0);
41 | } else {
42 | result.numIntersects = 0;
43 | }
44 | } else {
45 | Real b = Real(2) * (dx * (p0.x() - h) + dy * (p0.y() - k));
46 | Real c = (p0.x() * p0.x() - 2.0 * h * p0.x() + h * h) +
47 | (p0.y() * p0.y() - 2.0 * k * p0.y() + k * k) - radius * radius;
48 | Real discr = b * b - 4.0 * a * c;
49 |
50 | if (std::abs(discr) < utils::realThreshold()) {
51 | // 1 solution (tangent line)
52 | result.numIntersects = 1;
53 | result.t0 = -b / (Real(2) * a);
54 | } else if (discr < Real(0)) {
55 | result.numIntersects = 0;
56 | } else {
57 | result.numIntersects = 2;
58 | std::pair sols = utils::quadraticSolutions(a, b, c, discr);
59 | result.t0 = sols.first;
60 | result.t1 = sols.second;
61 | }
62 | }
63 |
64 | CAVC_ASSERT(result.numIntersects >= 0 && result.numIntersects <= 2, "invalid intersect count");
65 | return result;
66 | }
67 | } // namespace cavc
68 |
69 | #endif // CAVC_INTRLINESEG2CIRCLE2_HPP
70 |
--------------------------------------------------------------------------------
/libcavalier/include/cavc/mathutils.hpp:
--------------------------------------------------------------------------------
1 | #ifndef CAVC_MATHUTILS_HPP
2 | #define CAVC_MATHUTILS_HPP
3 |
4 | #include "internal/common.hpp"
5 | #include
6 | #include
7 | namespace cavc {
8 | namespace utils {
9 | // absolute threshold to be used for comparing reals generally
10 | template constexpr Real realThreshold() { return Real(1e-8); }
11 |
12 | // absolute threshold to be used for reals in common geometric computation (e.g. to check for
13 | // singularities)
14 | template constexpr Real realPrecision() { return Real(1e-5); }
15 |
16 | // absolute threshold to be used for joining slices together at end points
17 | template constexpr Real sliceJoinThreshold() { return Real(1e-4); }
18 |
19 | // absolute threshold to be used for pruning invalid slices for offset
20 | template constexpr Real offsetThreshold() { return Real(1e-4); }
21 |
22 | template constexpr Real pi() { return Real(3.14159265358979323846264338327950288); }
23 |
24 | template constexpr Real tau() { return Real(2) * pi(); }
25 |
26 | template bool fuzzyEqual(Real x, Real y, Real epsilon = realThreshold()) {
27 | return std::abs(x - y) < epsilon;
28 | }
29 |
30 | template
31 | bool fuzzyInRange(Real minValue, Real value, Real maxValue, Real epsilon = realThreshold()) {
32 | return (value + epsilon > minValue) && (value < maxValue + epsilon);
33 | }
34 |
35 | /// Normalize radius to be between 0 and 2PI, e.g. -PI/4 becomes 7PI/8 and 5PI becomes PI.
36 | template Real normalizeRadians(Real angle) {
37 | if (angle >= Real(0) && angle <= tau()) {
38 | return angle;
39 | }
40 |
41 | return angle - std::floor(angle / tau()) * tau();
42 | }
43 |
44 | /// Returns the smaller difference between two angles, result is negative if angle2 < angle1.
45 | template Real deltaAngle(Real angle1, Real angle2) {
46 | Real diff = normalizeRadians(angle2 - angle1);
47 | if (diff > pi()) {
48 | diff -= tau();
49 | }
50 |
51 | return diff;
52 | }
53 |
54 | /// Tests if angle is between a start and end angle (counter clockwise start to end, inclusive).
55 | template
56 | bool angleIsBetween(Real startAngle, Real endAngle, Real testAngle,
57 | Real epsilon = realThreshold()) {
58 | Real endSweep = normalizeRadians(endAngle - startAngle);
59 | Real midSweep = normalizeRadians(testAngle - startAngle);
60 |
61 | return midSweep < endSweep + epsilon;
62 | }
63 |
64 | template
65 | bool angleIsWithinSweep(Real startAngle, Real sweepAngle, Real testAngle,
66 | Real epsilon = realThreshold()) {
67 | Real endAngle = startAngle + sweepAngle;
68 | if (sweepAngle < Real(0)) {
69 | return angleIsBetween(endAngle, startAngle, testAngle, epsilon);
70 | }
71 |
72 | return angleIsBetween(startAngle, endAngle, testAngle, epsilon);
73 | }
74 |
75 | /// Returns the solutions to for the quadratic equation -b +/- sqrt (b * b - 4 * a * c) / (2 * a).
76 | template
77 | std::pair quadraticSolutions(Real a, Real b, Real c, Real discr) {
78 | // Function avoids loss in precision due to taking the difference of two floating point values
79 | // that are very near each other in value.
80 | // See:
81 | // https://math.stackexchange.com/questions/311382/solving-a-quadratic-equation-with-precision-when-using-floating-point-variables
82 | CAVC_ASSERT(fuzzyEqual(b * b - Real(4) * a * c, discr), "discriminate is not correct");
83 | Real sqrtDiscr = std::sqrt(discr);
84 | Real denom = Real(2) * a;
85 | Real sol1;
86 | if (b < Real(0)) {
87 | sol1 = (-b + sqrtDiscr) / denom;
88 | } else {
89 | sol1 = (-b - sqrtDiscr) / denom;
90 | }
91 |
92 | Real sol2 = (c / a) / sol1;
93 |
94 | return std::make_pair(sol1, sol2);
95 | }
96 |
97 | template std::size_t nextWrappingIndex(std::size_t index, const T &container) {
98 | if (index == container.size() - 1) {
99 | return 0;
100 | }
101 |
102 | return index + 1;
103 | }
104 |
105 | template std::size_t prevWrappingIndex(std::size_t index, const T &container) {
106 | if (index == 0) {
107 | return container.size() - 1;
108 | }
109 |
110 | return index - 1;
111 | }
112 | } // namespace utils
113 | } // namespace cavc
114 |
115 | #endif // CAVC_MATHUTILS_HPP
116 |
--------------------------------------------------------------------------------
/libcavalier/include/cavc/vector2.hpp:
--------------------------------------------------------------------------------
1 | #ifndef CAVC_VECTOR2_HPP
2 | #define CAVC_VECTOR2_HPP
3 | #include "mathutils.hpp"
4 | #include "vector.hpp"
5 | #include
6 | namespace cavc {
7 | template using Vector2 = Vector;
8 |
9 | /// Perpendicular vector to v (rotating counter clockwise).
10 | template Vector2 perp(Vector2 const &v) {
11 | return Vector2{-v.y(), v.x()};
12 | }
13 |
14 | /// Normalized perpendicular vector to v (rotating counter clockwise).
15 | template Vector2 unitPerp(Vector2 const &v) {
16 | Vector2 result{-v.y(), v.x()};
17 | normalize(result);
18 | return result;
19 | }
20 |
21 | /// Perpendicular dot product. Equivalent to dot(v0, perp(v1)).
22 | template Real perpDot(Vector2 const &v0, Vector2 const &v1) {
23 | return v0.x() * v1.y() - v0.y() * v1.x();
24 | }
25 |
26 | /// Returns the distance squared between p0 and p1. Equivalent to dot(p1 - p0, p1 - p0).
27 | template Real distSquared(Vector2 const &p0, Vector2 const &p1) {
28 | Vector2 d = p1 - p0;
29 | return dot(d, d);
30 | }
31 |
32 | /// Counter clockwise angle of the vector going from p0 to p1.
33 | template Real angle(Vector2 const &p0, Vector2 const &p1) {
34 | return std::atan2(p1.y() - p0.y(), p1.x() - p0.x());
35 | }
36 |
37 | /// Returns the midpoint between p0 and p1.
38 | template Vector2 midpoint(Vector2 const &p0, Vector2 const &p1) {
39 | return Vector2{(p0.x() + p1.x()) / Real(2), (p0.y() + p1.y()) / Real(2)};
40 | }
41 |
42 | /// Computes the point on the circle with radius, center, and polar angle given.
43 | template
44 | Vector2 pointOnCircle(Real radius, Vector2 const ¢er, Real angle) {
45 | return Vector2{center.x() + radius * std::cos(angle),
46 | center.y() + radius * std::sin(angle)};
47 | }
48 |
49 | /// Return the point on the segment going from p0 to p1 at parametric value t.
50 | template
51 | Vector2 pointFromParametric(Vector2 const &p0, Vector2 const &p1, Real t) {
52 | return p0 + t * (p1 - p0);
53 | }
54 |
55 | /// Returns the closest point that lies on the line segment from p0 to p1 to the point given.
56 | template
57 | Vector2 closestPointOnLineSeg(Vector2 const &p0, Vector2 const &p1,
58 | Vector2 const &point) {
59 | // Dot product used to find angles
60 | // See: http://geomalgorithms.com/a02-_lines.html
61 | Vector2 v = p1 - p0;
62 | Vector2 w = point - p0;
63 | Real c1 = dot(w, v);
64 | if (c1 < utils::realThreshold()) {
65 | return p0;
66 | }
67 |
68 | Real c2 = dot(v, v);
69 | if (c2 < c1 + utils::realThreshold()) {
70 | return p1;
71 | }
72 |
73 | Real b = c1 / c2;
74 | return p0 + b * v;
75 | }
76 |
77 | /// Returns true if point is left of the line pointing in the direction of the vector (p1 - p0).
78 | template
79 | bool isLeft(Vector2 const &p0, Vector2 const &p1, Vector2 const &point) {
80 | return (p1.x() - p0.x()) * (point.y() - p0.y()) - (p1.y() - p0.y()) * (point.x() - p0.x()) >
81 | Real(0);
82 | }
83 |
84 | /// Same as isLeft but uses <= operator rather than < for boundary inclusion.
85 | template
86 | bool isLeftOrEqual(Vector2 const &p0, Vector2 const &p1, Vector2 const &point) {
87 | return (p1.x() - p0.x()) * (point.y() - p0.y()) - (p1.y() - p0.y()) * (point.x() - p0.x()) >=
88 | Real(0);
89 | }
90 |
91 | /// Returns true if point is left or fuzzy coincident with the line pointing in the direction of the
92 | /// vector (p1 - p0).
93 | template
94 | bool isLeftOrCoincident(Vector2 const &p0, Vector2 const &p1,
95 | Vector2 const &point, Real epsilon = utils::realThreshold()) {
96 | return (p1.x() - p0.x()) * (point.y() - p0.y()) - (p1.y() - p0.y()) * (point.x() - p0.x()) >
97 | -epsilon;
98 | }
99 |
100 | /// Returns true if point is right or fuzzy coincident with the line pointing in the direction of
101 | /// the vector (p1 - p0).
102 | template
103 | bool isRightOrCoincident(Vector2 const &p0, Vector2 const &p1,
104 | Vector2 const &point, Real epsilon = utils::realThreshold()) {
105 | return (p1.x() - p0.x()) * (point.y() - p0.y()) - (p1.y() - p0.y()) * (point.x() - p0.x()) <
106 | epsilon;
107 | }
108 |
109 | /// Test if a point is within a arc sweep angle region defined by center, start, end, and bulge.
110 | template
111 | bool pointWithinArcSweepAngle(Vector2 const ¢er, Vector2 const &arcStart,
112 | Vector2 const &arcEnd, Real bulge, Vector2 const &point) {
113 | CAVC_ASSERT(std::abs(bulge) > utils::realThreshold(), "expected arc");
114 | CAVC_ASSERT(std::abs(bulge) <= Real(1), "bulge should always be between -1 and 1");
115 |
116 | if (bulge > Real(0)) {
117 | return isLeftOrCoincident(center, arcStart, point) &&
118 | isRightOrCoincident(center, arcEnd, point);
119 | }
120 |
121 | return isRightOrCoincident(center, arcStart, point) && isLeftOrCoincident(center, arcEnd, point);
122 | }
123 | } // namespace cavc
124 |
125 | #endif // CAVC_VECTOR2_HPP
126 |
--------------------------------------------------------------------------------
/libcavalier/offsets.h:
--------------------------------------------------------------------------------
1 | #ifndef OFFSETS_H
2 | #define OFFSETS_H
3 |
4 | #include
5 |
6 | struct POINTS{ // To be replaced by gp_Pnt.
7 | double x,y,z;
8 | };
9 |
10 | class offsets
11 | {
12 | public:
13 | offsets();
14 |
15 | //! Example of standard offset use -abs value for negative offset.
16 | void do_offset(double offset, enum offset_action action, double lead_in, double lead_out);
17 | void rotate_primairy_contour(unsigned int i /* contourvec[i] */);
18 | void create_lead_in_out(double lead_in, double lead_out);
19 |
20 | //! Helper functions, grabbed from old code example :
21 | std::vector arc_bulge(datas p /* primitive */);
22 | double arc_determinant(POINTS a /* arc startpoint */, POINTS b /* a arc circumfence point*/, POINTS c /* arc endpoint*/);
23 | POINTS arc_center(POINTS a /* arc startpoint */, POINTS b /* arc controlpoint*/, POINTS c /* arc endpoint */);
24 | POINTS offset_point_on_line(double xs, double ys, double xe, double ye, double offset_from_xs_ys);
25 | POINTS bulge_to_arc_controlpoint(POINTS p1, POINTS p2, double bulge); //find the arc center
26 | POINTS rotate_3d(double x_to_rotate,double y_to_rotate, double z_to_rotate, double rotate_degrees_x, double rotate_degrees_y, double rotate_degrees_z);
27 | void swap_contour(unsigned int i /*contourvec.at(i)*/); // A copy function from contour class.
28 |
29 | //! Create pockets, with or without islands.
30 | void do_pocket();
31 | //void process_pocket(cavc::Polyline outerloop, std::vector> islands, unsigned int contourvec_i);
32 | //void draw_pocket(cavc::OffsetLoopSet results, unsigned int contourvec_i);
33 |
34 | };
35 |
36 |
37 | #endif // OFFSETS_H
38 |
--------------------------------------------------------------------------------
/libcavalier/tests/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.11)
2 | add_subdirectory(polylinefactory)
3 | add_subdirectory(tests)
4 | add_subdirectory(benchmarks)
5 |
--------------------------------------------------------------------------------
/libcavalier/tests/benchmarks/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.11)
2 |
3 | include(googlebenchmark.cmake)
4 | include(clipper.cmake)
5 |
6 | macro(add_benchmark name)
7 | add_executable(${name} ${name}.cpp)
8 | target_link_libraries(${name}
9 | PRIVATE ${CAVC_CPP_HEADER_ONLY_LIB})
10 | target_link_libraries(${name}
11 | PRIVATE PolylineFactory)
12 | target_link_libraries(${name}
13 | PRIVATE benchmark::benchmark)
14 | if (MSVC)
15 | target_link_options(${name} PRIVATE $<$:/PROFILE>)
16 | endif()
17 | endmacro()
18 |
19 | add_benchmark(offsetbenchmarks)
20 | add_benchmark(spatialindexbenchmarks)
21 | add_benchmark(extentsbenchmarks)
22 | add_benchmark(areabenchmarks)
23 | add_benchmark(pathlengthbenchmarks)
24 | add_benchmark(windingnumberbenchmarks)
25 | add_benchmark(combinebenchmarks)
26 |
27 | add_benchmark(clipperbenchmarks)
28 | target_link_libraries(clipperbenchmarks
29 | PRIVATE clipper_static)
30 |
--------------------------------------------------------------------------------
/libcavalier/tests/benchmarks/areabenchmarks.cpp:
--------------------------------------------------------------------------------
1 | #include "benchmarkprofiles.h"
2 | #include "cavc/polyline.hpp"
3 | #include
4 |
5 | static void area(NoSetup, TestProfile const &profile) {
6 | cavc::getArea(profile.pline);
7 | }
8 | CAVC_CREATE_BENCHMARKS(area, NoSetup,area, benchmark::kNanosecond)
9 | CAVC_CREATE_NO_ARCS_BENCHMARKS(area, NoSetup, area, 0.01, benchmark::kNanosecond)
10 |
11 | BENCHMARK_MAIN();
12 |
--------------------------------------------------------------------------------
/libcavalier/tests/benchmarks/clipper.cmake:
--------------------------------------------------------------------------------
1 | include(FetchContent)
2 | FetchContent_Declare(
3 | clipper_static
4 | GIT_REPOSITORY https://github.com/jbuckmccready/clipper-lib
5 | GIT_TAG origin/master
6 | )
7 |
8 | fetchcontent_getproperties(clipper_static)
9 | if(NOT clipper_static_POPULATED)
10 | fetchcontent_populate(clipper_static)
11 | add_subdirectory(${clipper_static_SOURCE_DIR} ${clipper_static_BINARY_DIR}
12 | EXCLUDE_FROM_ALL)
13 | endif()
14 |
--------------------------------------------------------------------------------
/libcavalier/tests/benchmarks/clipperbenchmarks.cpp:
--------------------------------------------------------------------------------
1 | #include "clipper/clipper.hpp"
2 | #include "benchmarkprofiles.h"
3 | #include "cavc/polyline.hpp"
4 | #include
5 |
6 | const double clipperScaleFactor = 1e8;
7 | const double unscaledArcError = 0.01;
8 | const double clipperRoundPrecision = clipperScaleFactor * unscaledArcError;
9 |
10 | static ClipperLib::Path polylineToClipperPath(const cavc::Polyline &pline,
11 | double unscaledArcError) {
12 | auto noArcsPline = cavc::convertArcsToLines(pline, unscaledArcError);
13 |
14 | ClipperLib::Path clipperPath;
15 | clipperPath.reserve(noArcsPline.size());
16 | for (const auto &v : noArcsPline.vertexes()) {
17 | ClipperLib::cInt xInt = static_cast(v.x() * clipperScaleFactor);
18 | ClipperLib::cInt yInt = static_cast(v.y() * clipperScaleFactor);
19 | clipperPath.push_back(ClipperLib::IntPoint(xInt, yInt));
20 | }
21 |
22 | return clipperPath;
23 | }
24 |
25 | static void clipperOffset(const ClipperLib::Path &path, double delta, double roundPrecision,
26 | ClipperLib::Paths &results) {
27 | ClipperLib::ClipperOffset clipperOffset(2, roundPrecision);
28 | clipperOffset.AddPath(path, ClipperLib::JoinType::jtRound, ClipperLib::EndType::etClosedPolygon);
29 | clipperOffset.Execute(results, delta);
30 | }
31 |
32 | struct ClipperSetup {
33 | ClipperLib::Path clipperPath;
34 | ClipperSetup(TestProfile const &profile)
35 | : clipperPath(polylineToClipperPath(profile.pline, unscaledArcError)) {}
36 | };
37 |
38 | static void offset(ClipperSetup const &setup, TestProfile const &profile) {
39 | // negate delta to match orientation that cavc uses
40 | double offsetDelta = -clipperScaleFactor * profile.offsetDelta;
41 |
42 | for (std::size_t i = 1; i <= profile.offsetCount; ++i) {
43 | double offset = i * offsetDelta;
44 | {
45 | ClipperLib::Paths results;
46 | clipperOffset(setup.clipperPath, offset, clipperRoundPrecision, results);
47 | }
48 | {
49 | ClipperLib::Paths results;
50 | clipperOffset(setup.clipperPath, -offset, clipperRoundPrecision, results);
51 | }
52 | }
53 | }
54 |
55 | CAVC_CREATE_BENCHMARKS(clipperOffset, ClipperSetup, offset, benchmark::kMillisecond)
56 |
57 | BENCHMARK_MAIN();
58 |
--------------------------------------------------------------------------------
/libcavalier/tests/benchmarks/combinebenchmarks.cpp:
--------------------------------------------------------------------------------
1 | #include "benchmarkprofiles.h"
2 | #include "cavc/polylinecombine.hpp"
3 | #include
4 |
5 | struct CombineShiftedSetup {
6 | std::vector> shiftedProfiles;
7 |
8 | CombineShiftedSetup(TestProfile const &profile) {
9 | auto extents = cavc::getExtents(profile.pline);
10 | double halfWidth = (extents.xMax - extents.xMin) / 2.0;
11 | double halfHeight = (extents.yMax - extents.yMin) / 2.0;
12 |
13 | auto createShifted = [&](double angle) {
14 | auto shifted = profile.pline;
15 | double xOffset = halfWidth * std::cos(angle);
16 | double yOffset = halfHeight * std::sin(angle);
17 | cavc::translatePolyline(shifted, {xOffset, yOffset});
18 | return shifted;
19 | };
20 |
21 | std::size_t shiftedCount = 16;
22 | shiftedProfiles.reserve(shiftedCount);
23 | for (std::size_t i = 0; i < shiftedCount; ++i) {
24 | double angle = static_cast(i) / shiftedCount * cavc::utils::tau();
25 | shiftedProfiles.push_back(createShifted(angle));
26 | }
27 | }
28 | };
29 |
30 | static void combineShifted(CombineShiftedSetup const &setup, TestProfile const &profile) {
31 | for (auto const &shifted : setup.shiftedProfiles) {
32 | cavc::combinePolylines(profile.pline, shifted, cavc::PlineCombineMode::Union);
33 | cavc::combinePolylines(profile.pline, shifted, cavc::PlineCombineMode::Exclude);
34 | cavc::combinePolylines(profile.pline, shifted, cavc::PlineCombineMode::Intersect);
35 | cavc::combinePolylines(profile.pline, shifted, cavc::PlineCombineMode::XOR);
36 | }
37 | }
38 |
39 | CAVC_CREATE_BENCHMARKS(combine16Shifted, CombineShiftedSetup, combineShifted,
40 | benchmark::kMicrosecond)
41 | CAVC_CREATE_NO_ARCS_BENCHMARKS(combine16Shifted, CombineShiftedSetup, combineShifted, 0.01,
42 | benchmark::kMicrosecond)
43 |
44 | static void combineCoincident(NoSetup, TestProfile const &profile) {
45 | cavc::combinePolylines(profile.pline, profile.pline, cavc::PlineCombineMode::Union);
46 | cavc::combinePolylines(profile.pline, profile.pline, cavc::PlineCombineMode::Exclude);
47 | cavc::combinePolylines(profile.pline, profile.pline, cavc::PlineCombineMode::Intersect);
48 | cavc::combinePolylines(profile.pline, profile.pline, cavc::PlineCombineMode::XOR);
49 | }
50 |
51 | CAVC_CREATE_BENCHMARKS(combineCoincident, NoSetup, combineCoincident, benchmark::kMicrosecond)
52 | CAVC_CREATE_NO_ARCS_BENCHMARKS(combineCoincident, NoSetup, combineCoincident, 0.01,
53 | benchmark::kMicrosecond)
54 |
55 | BENCHMARK_MAIN();
56 |
--------------------------------------------------------------------------------
/libcavalier/tests/benchmarks/extentsbenchmarks.cpp:
--------------------------------------------------------------------------------
1 | #include "benchmarkprofiles.h"
2 | #include "cavc/polyline.hpp"
3 | #include
4 |
5 | static void extents(NoSetup, TestProfile const &profile) {
6 | cavc::getExtents(profile.pline);
7 | }
8 | CAVC_CREATE_BENCHMARKS(extents, NoSetup, extents, benchmark::kNanosecond)
9 | CAVC_CREATE_NO_ARCS_BENCHMARKS(extents, NoSetup, extents, 0.01, benchmark::kNanosecond)
10 |
11 | BENCHMARK_MAIN();
12 |
--------------------------------------------------------------------------------
/libcavalier/tests/benchmarks/googlebenchmark.cmake:
--------------------------------------------------------------------------------
1 | include(FetchContent)
2 | FetchContent_Declare(
3 | benchmark
4 | GIT_REPOSITORY https://github.com/google/benchmark.git
5 | GIT_TAG origin/master
6 | )
7 |
8 | fetchcontent_getproperties(benchmark)
9 | if(NOT benchmark_POPULATED)
10 | set(BENCHMARK_ENABLE_TESTING OFF
11 | CACHE BOOL "Enable testing of the benchmark library."
12 | FORCE)
13 | set(BENCHMARK_ENABLE_GTEST_TESTS OFF
14 | CACHE BOOL "Enable building the unit tests which depend on gtest"
15 | FORCE)
16 | fetchcontent_populate(benchmark)
17 | add_subdirectory(${benchmark_SOURCE_DIR} ${benchmark_BINARY_DIR}
18 | EXCLUDE_FROM_ALL)
19 | endif()
20 |
--------------------------------------------------------------------------------
/libcavalier/tests/benchmarks/offsetbenchmarks.cpp:
--------------------------------------------------------------------------------
1 | #include "benchmarkprofiles.h"
2 | #include "cavc/polylineoffset.hpp"
3 | #include
4 |
5 | const double arcError = 0.01;
6 |
7 | static void offset(NoSetup, TestProfile const &profile) {
8 | for (std::size_t i = 1; i <= profile.offsetCount; ++i) {
9 | double offset = i * profile.offsetDelta;
10 | cavc::parallelOffset(profile.pline, offset);
11 | cavc::parallelOffset(profile.pline, -offset);
12 | }
13 | }
14 |
15 | CAVC_CREATE_BENCHMARKS(offset, NoSetup, offset, benchmark::kMillisecond)
16 |
17 | CAVC_CREATE_NO_ARCS_BENCHMARKS(offset, NoSetup, offset, arcError, benchmark::kMillisecond)
18 |
19 | BENCHMARK_MAIN();
20 |
--------------------------------------------------------------------------------
/libcavalier/tests/benchmarks/pathlengthbenchmarks.cpp:
--------------------------------------------------------------------------------
1 | #include "benchmarkprofiles.h"
2 | #include "cavc/polyline.hpp"
3 | #include
4 |
5 | static void pathLength(NoSetup, TestProfile const &profile) {
6 | cavc::getPathLength(profile.pline);
7 | }
8 | CAVC_CREATE_BENCHMARKS(pathLength, NoSetup, pathLength, benchmark::kNanosecond)
9 | CAVC_CREATE_NO_ARCS_BENCHMARKS(pathLength, NoSetup, pathLength, 0.01, benchmark::kNanosecond)
10 |
11 | BENCHMARK_MAIN();
12 |
--------------------------------------------------------------------------------
/libcavalier/tests/benchmarks/spatialindexbenchmarks.cpp:
--------------------------------------------------------------------------------
1 | #include "benchmarkprofiles.h"
2 | #include "cavc/staticspatialindex.hpp"
3 | #include
4 |
5 | static void createIndex(NoSetup, TestProfile const &profile) {
6 | cavc::createApproxSpatialIndex(profile.pline);
7 | }
8 |
9 | CAVC_CREATE_BENCHMARKS(createIndex, NoSetup, createIndex, benchmark::kMicrosecond)
10 | CAVC_CREATE_NO_ARCS_BENCHMARKS(createIndex, NoSetup, createIndex, 0.01, benchmark::kMicrosecond)
11 |
12 | struct QuerySetup {
13 | std::vector queryResults;
14 | std::vector queryStack;
15 | cavc::StaticSpatialIndex spatialIndex;
16 | QuerySetup(TestProfile const &profile)
17 | : spatialIndex(cavc::createApproxSpatialIndex(profile.pline)) {}
18 | };
19 |
20 | static void queryIndexReuseStack(QuerySetup &setup, TestProfile const &profile) {
21 | profile.pline.visitSegIndices([&](std::size_t i, std::size_t j) {
22 | cavc::AABB bb = cavc::createFastApproxBoundingBox(profile.pline[i], profile.pline[j]);
23 | setup.queryResults.clear();
24 | bb.expand(0.1);
25 | setup.spatialIndex.query(bb.xMin, bb.yMin, bb.xMax, bb.yMax, setup.queryResults,
26 | setup.queryStack);
27 | return true;
28 | });
29 | }
30 |
31 | CAVC_CREATE_BENCHMARKS(queryIndexReuseStack, QuerySetup, queryIndexReuseStack,
32 | benchmark::kMicrosecond)
33 | CAVC_CREATE_NO_ARCS_BENCHMARKS(queryIndexReuseStack, QuerySetup, queryIndexReuseStack, 0.01,
34 | benchmark::kMicrosecond)
35 |
36 | BENCHMARK_MAIN();
37 |
--------------------------------------------------------------------------------
/libcavalier/tests/benchmarks/windingnumberbenchmarks.cpp:
--------------------------------------------------------------------------------
1 | #include "benchmarkprofiles.h"
2 | #include "cavc/polyline.hpp"
3 | #include
4 |
5 | struct WindingNumberSetup {
6 | std::vector> testPts;
7 | WindingNumberSetup(TestProfile const &profile) {
8 | auto extents = cavc::getExtents(profile.pline);
9 | // expand out all directions by half the polyline width for some of the test points to for sure
10 | // be outside the polyline
11 | extents.expand((extents.xMax - extents.xMin) / 2.0);
12 | double width = extents.xMax - extents.xMin;
13 | double height = extents.yMax - extents.yMin;
14 |
15 | std::size_t gridDim = 10;
16 | testPts.reserve(gridDim * gridDim);
17 | // grid is inclusive at ends so we go from 0 to gridDim - 1
18 | for (std::size_t i = 0; i < gridDim; ++i) {
19 | for (std::size_t j = 0; j < gridDim; ++j) {
20 | // scale by gridDim - 1 (max iteration value)
21 | double x = static_cast(i) / (gridDim - 1) * width + extents.xMin;
22 | double y = static_cast(j) / (gridDim - 1) * height + extents.yMin;
23 | testPts.emplace_back(x, y);
24 | }
25 | }
26 | }
27 | };
28 |
29 | static void windingNumber(WindingNumberSetup const &setup, TestProfile const &profile) {
30 | for (auto const &pt : setup.testPts) {
31 | cavc::getWindingNumber(profile.pline, pt);
32 | }
33 | }
34 |
35 | CAVC_CREATE_BENCHMARKS(windingNumber100PtGrid, WindingNumberSetup, windingNumber,
36 | benchmark::kMicrosecond)
37 | CAVC_CREATE_NO_ARCS_BENCHMARKS(windingNumber100PtGrid, WindingNumberSetup, windingNumber, 0.01,
38 | benchmark::kMicrosecond)
39 |
40 | BENCHMARK_MAIN();
41 |
--------------------------------------------------------------------------------
/libcavalier/tests/polylinefactory/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(PolylineFactory src/polylinefactory.cpp)
2 |
3 | target_include_directories(PolylineFactory
4 | PUBLIC include/)
5 |
6 | target_link_libraries(PolylineFactory
7 | PUBLIC ${CAVC_CPP_HEADER_ONLY_LIB})
8 |
9 | target_link_libraries(PolylineFactory
10 | PUBLIC ${CAVC_C_API_LIB})
11 |
--------------------------------------------------------------------------------
/libcavalier/tests/polylinefactory/include/polylinefactory.hpp:
--------------------------------------------------------------------------------
1 | #ifndef CAVC_POLYLINEFACTORY_HPP
2 | #define CAVC_POLYLINEFACTORY_HPP
3 | #include "cavaliercontours.h"
4 | #include "cavc/polyline.hpp"
5 |
6 | struct cavc_pline_deleter {
7 | void operator()(cavc_pline *pline) { cavc_pline_delete(pline); }
8 | };
9 |
10 | using cavc_pline_ptr = std::unique_ptr;
11 |
12 | class PolylineFactory {
13 | public:
14 | // Create a circle with radius and center, vertexRotAngle rotates the vertexes about the circle
15 | // center, if isCW is true then circle goes clockwise else counter clockwise.
16 | static std::vector createCircle(cavc_real radius, cavc_point center,
17 | cavc_real vertexRotAngle, bool isCW);
18 | static cavc_pline_ptr vertexesToPline(std::vector const &vertexes, bool isClosed);
19 | };
20 |
21 | #endif // CAVC_POLYLINEPATHFACTORY_HPP
22 |
--------------------------------------------------------------------------------
/libcavalier/tests/polylinefactory/src/polylinefactory.cpp:
--------------------------------------------------------------------------------
1 | #include "polylinefactory.hpp"
2 |
3 | static cavc_point pointOnCircle(cavc_real radius, cavc_point center, cavc_real angle) {
4 | return {center.x + radius * std::cos(angle), center.y + radius * std::sin(angle)};
5 | }
6 |
7 | inline static cavc_real PI() { return 3.14159265358979323846264338327950288; }
8 |
9 | std::vector PolylineFactory::createCircle(cavc_real radius, cavc_point center,
10 | cavc_real vertexRotAngle, bool isCW) {
11 | std::vector result;
12 | result.reserve(2);
13 |
14 | cavc_point point1 = pointOnCircle(radius, center, vertexRotAngle);
15 | cavc_point point2 = pointOnCircle(radius, center, vertexRotAngle + PI());
16 | cavc_real bulge = isCW ? -1.0 : 1.0;
17 | result.push_back({point1.x, point1.y, bulge});
18 | result.push_back({point2.x, point2.y, bulge});
19 |
20 | return result;
21 | }
22 |
23 | cavc_pline_ptr PolylineFactory::vertexesToPline(std::vector const &vertexes,
24 | bool isClosed) {
25 | return cavc_pline_ptr(
26 | cavc_pline_new(&vertexes[0], static_cast(vertexes.size()), isClosed));
27 | }
28 |
--------------------------------------------------------------------------------
/libcavalier/tests/tests/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.11)
2 | include (googletest.cmake)
3 |
4 | macro(cavc_add_test name)
5 | add_executable(${name} ${name}.cpp)
6 |
7 | target_include_directories(${name}
8 | PRIVATE include/)
9 |
10 | target_link_libraries(${name}
11 | PRIVATE PolylineFactory)
12 |
13 | target_link_libraries(${name}
14 | PRIVATE ${CAVC_C_API_LIB})
15 |
16 | target_link_libraries(${name}
17 | PRIVATE gtest_main gtest gmock)
18 |
19 | # note: adding executable as single test rather than discovering tests via gtest_discover_tests
20 | # because it adds parmeterized tests as individual tests and ctest runs them one by one which is
21 | # quite slow
22 | add_test(NAME ${name} COMMAND ${name})
23 | endmacro()
24 |
25 | cavc_add_test(cavc_pline_tests)
26 | cavc_add_test(cavc_pline_function_tests)
27 | cavc_add_test(cavc_parallel_offset_tests)
28 | cavc_add_test(cavc_combine_plines_tests)
29 | cavc_add_test(staticspatialindex_tests)
30 |
--------------------------------------------------------------------------------
/libcavalier/tests/tests/cavc_pline_tests.cpp:
--------------------------------------------------------------------------------
1 | #include "c_api_test_helpers.hpp"
2 | #include "cavaliercontours.h"
3 | #include "gmock/gmock.h"
4 | #include "gtest/gtest.h"
5 | #include
6 |
7 | namespace t = testing;
8 |
9 | class cavc_plineTests : public t::Test {
10 | protected:
11 | void SetUp() override;
12 |
13 | void TearDown() override;
14 |
15 | std::vector pline1Vertexes;
16 | cavc_pline *pline1 = nullptr;
17 | cavc_pline *pline2 = nullptr;
18 | std::size_t origPline1Size = 0;
19 | uint32_t initialPline1Size() { return static_cast(origPline1Size); }
20 | };
21 |
22 | void cavc_plineTests::SetUp() {
23 | pline1Vertexes = {{1, 2, 0.1}, {33, 3, 0.2}, {34, 35, 0.3}, {2, 36, 0.4}};
24 | origPline1Size = pline1Vertexes.size();
25 | pline1 = cavc_pline_new(&pline1Vertexes[0], initialPline1Size(), 0);
26 | pline2 = cavc_pline_new(nullptr, 0, 1);
27 | }
28 |
29 | void cavc_plineTests::TearDown() {
30 | cavc_pline_delete(pline1);
31 | cavc_pline_delete(pline2);
32 | }
33 |
34 | TEST_F(cavc_plineTests, cavc_pline_new) {
35 |
36 | // test capacity
37 | EXPECT_EQ(cavc_pline_capacity(pline1), initialPline1Size());
38 |
39 | // test is_closed
40 | EXPECT_EQ(cavc_pline_is_closed(pline1), 0);
41 |
42 | // test vertex_count
43 | auto pline1Count = cavc_pline_vertex_count(pline1);
44 | ASSERT_EQ(pline1Count, initialPline1Size());
45 |
46 | // test vertex_data
47 | std::vector read_vertexes(pline1Count);
48 | cavc_pline_vertex_data(pline1, &read_vertexes[0]);
49 | ASSERT_THAT(pline1Vertexes, t::Pointwise(VertexEqual(), read_vertexes));
50 |
51 | // test on empty pline
52 | // test capacity
53 | EXPECT_EQ(cavc_pline_capacity(pline2), 0);
54 |
55 | // test is_closed
56 | EXPECT_EQ(cavc_pline_is_closed(pline2), 1);
57 |
58 | // test vertex_count
59 | auto pline2Count = cavc_pline_vertex_count(pline2);
60 | ASSERT_EQ(pline2Count, 0);
61 |
62 | // test vertex_data
63 | cavc_pline_vertex_data(pline2, &read_vertexes[0]);
64 | // nothing should have been written to the buffer
65 | ASSERT_THAT(read_vertexes, t::Pointwise(VertexEqual(), pline1Vertexes));
66 | }
67 |
68 | TEST_F(cavc_plineTests, cavc_pline_set_capacity) {
69 | // setting capacity less than current does nothing
70 | cavc_pline_set_capacity(pline1, 1);
71 | ASSERT_EQ(cavc_pline_capacity(pline1), 4);
72 |
73 | cavc_pline_set_capacity(pline1, 11);
74 | ASSERT_EQ(cavc_pline_capacity(pline1), 11);
75 | }
76 |
77 | TEST_F(cavc_plineTests, cavc_pline_set_vertex_data) {
78 | cavc_pline_set_vertex_data(pline2, &pline1Vertexes[0], initialPline1Size());
79 | ASSERT_EQ(cavc_pline_vertex_count(pline2), initialPline1Size());
80 |
81 | std::vector readVertexes(initialPline1Size());
82 | cavc_pline_vertex_data(pline2, &readVertexes[0]);
83 | ASSERT_THAT(readVertexes, t::Pointwise(VertexEqual(), pline1Vertexes));
84 | }
85 |
86 | TEST_F(cavc_plineTests, cavc_pline_add_vertex) {
87 | cavc_vertex v{555, 666, 0.777};
88 | cavc_pline_add_vertex(pline1, v);
89 | ASSERT_EQ(cavc_pline_vertex_count(pline1), initialPline1Size() + 1);
90 |
91 | std::vector readVertexes(initialPline1Size() + 1);
92 | cavc_pline_vertex_data(pline1, &readVertexes[0]);
93 | pline1Vertexes.push_back(v);
94 | ASSERT_THAT(readVertexes, t::Pointwise(VertexEqual(), pline1Vertexes));
95 |
96 | cavc_pline_add_vertex(pline2, v);
97 | ASSERT_EQ(cavc_pline_vertex_count(pline2), 1);
98 |
99 | readVertexes.resize(1);
100 | cavc_pline_vertex_data(pline2, &readVertexes[0]);
101 | ASSERT_THAT(readVertexes, t::Pointwise(VertexEqual(), {v}));
102 | }
103 |
104 | TEST_F(cavc_plineTests, cavc_pline_remove_range) {
105 | // remove first vertex
106 | cavc_pline_remove_range(pline1, 0, 1);
107 | ASSERT_EQ(cavc_pline_vertex_count(pline1), initialPline1Size() - 1);
108 |
109 | std::vector readVertexes(initialPline1Size() - 1);
110 | cavc_pline_vertex_data(pline1, &readVertexes[0]);
111 | pline1Vertexes.erase(pline1Vertexes.begin());
112 | ASSERT_THAT(readVertexes, t::Pointwise(VertexEqual(), pline1Vertexes));
113 |
114 | // remove 2nd and 3rd vertex
115 | cavc_pline_remove_range(pline1, 1, 2);
116 | ASSERT_EQ(cavc_pline_vertex_count(pline1), initialPline1Size() - 3);
117 | readVertexes.resize(1);
118 | cavc_pline_vertex_data(pline1, &readVertexes[0]);
119 | pline1Vertexes.erase(pline1Vertexes.begin() + 1, pline1Vertexes.begin() + 3);
120 | ASSERT_THAT(readVertexes, t::Pointwise(VertexEqual(), pline1Vertexes));
121 |
122 | // remove last vertex
123 | cavc_pline_remove_range(pline1, 0, 1);
124 | ASSERT_EQ(cavc_pline_vertex_count(pline1), initialPline1Size() - 4);
125 |
126 | readVertexes.resize(10);
127 | std::fill(readVertexes.begin(), readVertexes.end(), cavc_vertex{-1, -2, -3});
128 | auto copy = readVertexes;
129 | cavc_pline_vertex_data(pline1, &readVertexes[0]);
130 | // nothing should have been written to the buffer
131 | ASSERT_THAT(readVertexes, t::Pointwise(VertexEqual(), copy));
132 | }
133 |
134 | TEST_F(cavc_plineTests, cavc_pline_clear) {
135 | cavc_pline_clear(pline1);
136 | ASSERT_EQ(cavc_pline_vertex_count(pline1), 0);
137 |
138 | cavc_pline_clear(pline2);
139 | ASSERT_EQ(cavc_pline_vertex_count(pline2), 0);
140 | }
141 |
142 | int main(int argc, char **argv) {
143 | t::InitGoogleTest(&argc, argv);
144 | return RUN_ALL_TESTS();
145 | }
146 |
--------------------------------------------------------------------------------
/libcavalier/tests/tests/googletest.cmake:
--------------------------------------------------------------------------------
1 | include(FetchContent)
2 | FetchContent_Declare(
3 | googletest
4 | GIT_REPOSITORY https://github.com/google/googletest.git
5 | GIT_TAG origin/master
6 | )
7 |
8 | fetchcontent_getproperties(googletest)
9 | if(NOT googletest_POPULATED)
10 | # this is required for msvc or we get linker errors
11 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
12 | fetchcontent_populate(googletest)
13 | add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}
14 | EXCLUDE_FROM_ALL)
15 | endif()
16 |
--------------------------------------------------------------------------------
/libcavalier/tests/tests/include/c_api_test_helpers.hpp:
--------------------------------------------------------------------------------
1 | #ifndef CAVC_API_TEST_HELPERS_HPP
2 | #define CAVC_API_TEST_HELPERS_HPP
3 | #include "cavaliercontours.h"
4 | #include "testhelpers.hpp"
5 | #include "gmock/gmock.h"
6 | #include "gtest/gtest.h"
7 | #include
8 |
9 | inline bool vertexesFuzzyEqual(cavc_vertex const &left, cavc_vertex const &right) {
10 | return fuzzyEqual(left.x, right.x) && fuzzyEqual(left.y, right.y) &&
11 | fuzzyEqual(left.bulge, right.bulge);
12 | }
13 |
14 | template
15 | inline std::size_t nextWrappingIndex(Container const &container, std::size_t index) {
16 | if (index == container.size() - 1) {
17 | return 0;
18 | }
19 | return index + 1;
20 | }
21 |
22 | MATCHER(VertexFuzzyEqual, "") {
23 | auto const &left = std::get<0>(arg);
24 | auto const &right = std::get<1>(arg);
25 | return vertexesFuzzyEqual(left, right);
26 | }
27 |
28 | MATCHER(VertexEqual, "") {
29 | auto const &left = std::get<0>(arg);
30 | auto const &right = std::get<1>(arg);
31 | return left.x == right.x && left.y == right.y && left.bulge == right.bulge;
32 | }
33 |
34 | MATCHER(PointFuzzyEqual, "") {
35 | auto const &left = std::get<0>(arg);
36 | auto const &right = std::get<1>(arg);
37 | return fuzzyEqual(left.x, right.x) && fuzzyEqual(left.y, right.y);
38 | }
39 |
40 | MATCHER_P(VertexListsFuzzyEqual, isClosed, "") {
41 | auto const &left = std::get<0>(arg);
42 | auto const &right = std::get<1>(arg);
43 | if (left.size() != right.size()) {
44 | *result_listener << "sizes of vertex lists do not match ";
45 | return false;
46 | }
47 |
48 | std::size_t const vertexCount = left.size();
49 |
50 | if (!isClosed) {
51 | // open polyline indexes much match up
52 | for (std::size_t i = 0; i < vertexCount; ++i) {
53 | if (!vertexesFuzzyEqual(left[i], right[i])) {
54 | *result_listener << "vertexes not equal at index: " << i << " ";
55 | return false;
56 | }
57 | }
58 | return true;
59 | }
60 |
61 | // vertexes may not have same indexes in the case of a closed polyline, find first matching and
62 | // start matching from there
63 | std::size_t startIndex = 0;
64 | for (; startIndex < vertexCount; ++startIndex) {
65 | if (vertexesFuzzyEqual(left[0], right[startIndex])) {
66 | break;
67 | }
68 | }
69 |
70 | if (startIndex == vertexCount) {
71 | *result_listener << "did not find matching vertex to start with ";
72 | return false;
73 | }
74 |
75 | *result_listener << " started matching at index: " << startIndex << " ";
76 | // first one already matched, start at next index
77 | std::size_t index = nextWrappingIndex(left, startIndex);
78 | for (std::size_t i = 1; i < vertexCount; ++i) {
79 | if (!vertexesFuzzyEqual(left[i], right[index])) {
80 | *result_listener << "vertexes not equal at index: " << index << " ";
81 | return false;
82 | }
83 | index = nextWrappingIndex(left, index);
84 | }
85 |
86 | return true;
87 | }
88 |
89 | inline std::ostream &operator<<(std::ostream &os, cavc_vertex const &v) {
90 | os << '[' << v.x << "," << v.y << "," << v.bulge << ']';
91 | return os;
92 | }
93 |
94 | inline std::ostream &operator<<(std::ostream &os, cavc_point const &p) {
95 | os << '[' << p.x << "," << p.y << ']';
96 | return os;
97 | }
98 |
99 | // helper to just create a cavc_pline from vertexes
100 | cavc_pline *plineFromVertexes(std::vector const &vertexes, bool isClosed) {
101 | return cavc_pline_new(&vertexes[0], static_cast(vertexes.size()), isClosed ? 1 : 0);
102 | }
103 |
104 | // reverses the direction of the polyline defined by vertexes
105 | void reverseDirection(std::vector &vertexes) {
106 | if (vertexes.size() < 2) {
107 | return;
108 | }
109 |
110 | std::reverse(vertexes.begin(), vertexes.end());
111 | cavc_real firstBulge = vertexes[0].bulge;
112 | for (std::size_t i = 1; i < vertexes.size(); ++i) {
113 | vertexes[i - 1].bulge = -vertexes[i].bulge;
114 | }
115 |
116 | vertexes.back().bulge = -firstBulge;
117 | }
118 |
119 | // create a reversed polyline from the given pline (caller must delete the created pline)
120 | cavc_pline *createRevseredPline(cavc_pline *pline) {
121 | uint32_t count = cavc_pline_vertex_count(pline);
122 | std::vector vertexes(count);
123 | cavc_pline_vertex_data(pline, &vertexes[0]);
124 | reverseDirection(vertexes);
125 | return cavc_pline_new(&vertexes[0], count, cavc_pline_is_closed(pline));
126 | }
127 |
128 | #endif // CAVC_API_TEST_HELPERS_HPP
129 |
--------------------------------------------------------------------------------
/libcavalier/tests/tests/include/testhelpers.hpp:
--------------------------------------------------------------------------------
1 | #ifndef CAVC_TESTHELPERS_HPP
2 | #define CAVC_TESTHELPERS_HPP
3 | #include "cavaliercontours.h"
4 | #include
5 | #include
6 | constexpr inline cavc_real PI() { return 3.14159265358979323846264338327950288; }
7 | constexpr inline cavc_real TEST_EPSILON() { return 1e-5; }
8 | template inline bool fuzzyEqual(Real const &left, cavc_real const &right) {
9 | return std::abs(left - right) < TEST_EPSILON();
10 | }
11 |
12 | // type to hold summary properties of a polyline for test comparing, acts as a sort of geometric
13 | // hash of the polyline, it is very unlikely that two polylines have the same PolylineProperties
14 | // without being the same polyline, especially accidentally via generation in an algorithm
15 | struct PolylineProperties {
16 | std::size_t vertexCount;
17 | cavc_real area;
18 | cavc_real pathLength;
19 | cavc_real minX;
20 | cavc_real minY;
21 | cavc_real maxX;
22 | cavc_real maxY;
23 |
24 | PolylineProperties(std::size_t vertexCount, cavc_real area, cavc_real pathLength, cavc_real minX,
25 | cavc_real minY, cavc_real maxX, cavc_real maxY)
26 | : vertexCount(vertexCount), area(area), pathLength(pathLength), minX(minX), minY(minY),
27 | maxX(maxX), maxY(maxY) {}
28 |
29 | PolylineProperties(cavc_pline *pline) {
30 | vertexCount = cavc_pline_vertex_count(pline);
31 | area = cavc_get_area(pline);
32 | pathLength = cavc_get_path_length(pline);
33 | cavc_get_extents(pline, &minX, &minY, &maxX, &maxY);
34 | }
35 | };
36 |
37 | // fuzzy equality operator== for testing
38 | inline bool operator==(PolylineProperties const &left, PolylineProperties const &right) {
39 | return left.vertexCount == right.vertexCount && fuzzyEqual(left.area, right.area) &&
40 | fuzzyEqual(left.pathLength, right.pathLength) && fuzzyEqual(left.minX, right.minX) &&
41 | fuzzyEqual(left.minY, right.minY) && fuzzyEqual(left.maxX, right.maxX) &&
42 | fuzzyEqual(left.maxY, right.maxY);
43 | }
44 |
45 | inline std::ostream &operator<<(std::ostream &os, PolylineProperties const &p) {
46 | os << "{ vertexCount: " << p.vertexCount << ", area: " << p.area
47 | << ", pathLength: " << p.pathLength << ", minX: " << p.minX << ", minY: " << p.minY
48 | << ", maxX: " << p.maxX << ", maxY: " << p.maxY << " }";
49 | return os;
50 | }
51 | #endif // CAVC_TESTHELPERS_HPP
52 |
--------------------------------------------------------------------------------
/libcontour/contours.h:
--------------------------------------------------------------------------------
1 | #ifndef CONTOURS_H
2 | #define CONTOURS_H
3 |
4 | #include
5 | #include "opencascade.h"
6 | #include "draw_primitives.h"
7 | using namespace occ;
8 |
9 | class contours
10 | {
11 | public:
12 | contours();
13 |
14 | void main(double tol, std::string layer);
15 |
16 | //! Wich layer to cut?
17 | void init_primitives(std::string layer);
18 |
19 | //! Check if open contours are open or closed. For example a spline can be closed if startpoint = endpoint.
20 | //! tol = tollerance to classify a startpoint-endpoint match.
21 | void check_for_single_closed_contours(double tol);
22 | void check_for_multi_contours(double tol);
23 | void check_for_single_open_contours(double tol);
24 |
25 | //! Add contour depth squence. This is not a "keep parts together" algoritme. It cut's depth for depth.
26 | //! It algoritme is preferred to be used to avoid thermal distortion.
27 | void add_contour_depth_sequence();
28 | void add_contour_ccw();
29 | std::vector get_childs(std::vector list);
30 | std::vector get_toplevel(std::vector list);
31 | std::vector lista_min_listb(std::vector lista, std::vector listb);
32 |
33 | //! Helper functions
34 | void area();
35 | void swap(unsigned int i);
36 | void swap_contour(unsigned int i /*contourvec.at(i)*/);
37 | int find_next(unsigned int i /*source*/, unsigned int &target /*target*/, double tol);
38 |
39 | int find_start_start_match(unsigned int i /*source*/, unsigned int j /*target*/, double tol);
40 | int find_start_end_match(unsigned int i /*source*/, unsigned int j /*target*/, double tol);
41 | int find_end_start_match(unsigned int i /*source*/, unsigned int j /*target*/, double tol);
42 | int find_end_end_match(unsigned int i /*source*/, unsigned int j /*target*/, double tol);
43 |
44 | void print_result();
45 | void print_depth_sequence();
46 |
47 | int point_in_polygon(std::vector polygon, gp_Pnt point);
48 |
49 | //! A toplevel contour is depth=0. A inside contour of depth=0 would be depth=1. How deep parts are nested is saved by the maxdepth value.
50 | int get_max_depth();
51 |
52 | //! Keep parts together algorimte, shortened "kpt".
53 | //! Return a organized list in cut seauence of contourvec.at[i]
54 | std::vector keep_parts_together();
55 |
56 | private:
57 |
58 | };
59 |
60 | #endif // CONTOURS_H
61 |
--------------------------------------------------------------------------------
/libdata/variable.cpp:
--------------------------------------------------------------------------------
1 | #include "variable.h"
2 |
3 | std::vector datavec, dxfvec, rapidvec;
4 | std::vector contourvec, pocketvec;
5 | gcode_setup gc;
6 | double last_x=0,last_y=0,last_z=0;
7 | int linenumber;
8 | std::vector gcvec;
9 | std::vector kpt_sequence; // Keep parts together list.
10 | int maxdepth;
11 |
12 | variable::variable()
13 | {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/libdata/variable.h:
--------------------------------------------------------------------------------
1 | #ifndef VARIABLE_H
2 | #define VARIABLE_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | enum primitive_type {
9 | point=0,
10 | line=1,
11 | lwpolyline=2, // Wire, linestrip
12 | circle=3,
13 | arc=4,
14 | spline=5,
15 | ellipse=6,
16 | };
17 |
18 | enum contour_type {
19 | none=0,
20 | single_open=1,
21 | single_closed=2,
22 | multi_open=3,
23 | multi_closed=4,
24 | };
25 |
26 | enum contour_dir {
27 | cw=0,
28 | ccw=1,
29 | };
30 |
31 | enum offset_action {
32 | offset_contour=0,
33 | lead_base_contour=1,
34 | lead_in_contour=2,
35 | lead_out_contour=3,
36 | pocket=4,
37 | };
38 |
39 | struct datas {
40 | Handle(AIS_Shape) ashape;
41 | primitive_type primitivetype;
42 | contour_type contourtype=contour_type::none;
43 | std::string acad_layer;
44 | //! Startpoint, endpoint
45 | gp_Pnt start{0,0,0}, end{0,0,0}, center{0,0,0}; // Center is used by cavalier functions.
46 | std::vector control, arcmid; // Arcmid is used by cavalier function and is the point at half way arc circumfence.
47 | double radius=0; // Radius is used by cavalier functions.
48 | double bulge=0; // For arc's to define if the arc is [cw] or [ccw]. bulge<0=g2
49 | bool select=0; // Helper for contour algoritme.
50 | };
51 | //! Datavec contains the filtered cad_layers to cut, dxfvec is the complete dxf data.
52 | extern std::vector datavec, dxfvec, rapidvec;
53 |
54 | struct lead_in_out {
55 | //! The lead-in, lead-out shape (line, arc, etc).
56 | Handle(AIS_Shape) ashape;
57 | //! Choose one of the parallel Points to draw the lead-in, lead-out.
58 | std::vector points;
59 | };
60 |
61 | struct contour {
62 | std::vector primitive_sequence;
63 | contour_dir dir;
64 | double area=0;
65 |
66 | //! Lead_base=base points circumfence contour, Lead_in=circumfence points at lead_in offset, Lead_out=circumfence points at lead_out offset.
67 | lead_in_out lead_in, lead_out, lead_base;
68 | //! Current lead_base point position. If the lead_in-out is shifted to another position, this value stores the current lead_in-out index position.
69 | // unsigned int lead_index=0;
70 |
71 | std::vector offset_sequence; // Primitives in cw order.
72 |
73 | // Contour depth.
74 | std::vector childcontours;
75 | bool select=false;
76 | int depth=0;
77 | };
78 | extern std::vector contourvec, pocketvec;
79 |
80 | struct gcode_setup{
81 | std::string filename="gcode.ngc";
82 | std::string linenumber_format="N";
83 | std::string z_axis_format="Z";
84 | std::string tool_on_macro="";
85 |
86 | bool print_linenumbers=true;
87 | double lead_in;
88 | double lead_out;
89 | int lead_start_i;
90 | double offset;
91 | double travelheight;
92 | double pierceheight;
93 | double piercespeed;
94 | double piercedelay;
95 | double cutheight;
96 | double power;
97 | double feedrate;
98 | double internal_pocket_offset;
99 | double tooloffset_x, tooloffset_y, tooloffset_z;
100 |
101 |
102 | std::string layer;
103 | std::vector intro;
104 | std::vector outtro;
105 |
106 | std::string operation_type; // Offset, pocket, drill.
107 | };
108 | extern gcode_setup gc;
109 | extern std::vector gcvec;
110 | extern double last_x,last_y,last_z; // Used for displaying rapids. Can be used for toolhomepos.
111 |
112 | extern int linenumber;
113 |
114 | extern std::vector kpt_sequence; // Keep parts together list.
115 |
116 | extern int maxdepth;
117 |
118 | class variable
119 | {
120 | public:
121 | variable();
122 | };
123 |
124 | #endif // VARIABLE_H
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/libdialog/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.1.0)
2 |
3 | project(portable_file_dialogs VERSION 1.00 LANGUAGES CXX)
4 |
5 | add_library(${PROJECT_NAME} INTERFACE)
6 | target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
--------------------------------------------------------------------------------
/libdialog/COPYING:
--------------------------------------------------------------------------------
1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2 | Version 2, December 2004
3 |
4 | Copyright (C) 2004 Sam Hocevar
5 |
6 | Everyone is permitted to copy and distribute verbatim or modified
7 | copies of this license document, and changing it is allowed as long
8 | as the name is changed.
9 |
10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12 |
13 | 0. You just DO WHAT THE FUCK YOU WANT TO.
14 |
15 |
--------------------------------------------------------------------------------
/libdialog/README.md:
--------------------------------------------------------------------------------
1 | # Portable File Dialogs
2 |
3 | A free C++11 file dialog library.
4 |
5 | - works on Windows, Mac OS X, Linux
6 | - **single-header**, no extra library dependencies
7 | - **synchronous *or* asynchronous** (does not block the rest of your program!)
8 | - **cancelable** (kill asynchronous dialogues without user interaction)
9 | - **secure** (immune to shell-quote vulnerabilities)
10 |
11 | [](https://www.codacy.com/manual/samhocevar/portable-file-dialogs?utm_source=github.com&utm_medium=referral&utm_content=samhocevar/portable-file-dialogs&utm_campaign=Badge_Grade)
12 |
13 | ## Status
14 |
15 | The library is now pretty robust. It is not as feature-complete as
16 | [Tiny File Dialogs](https://sourceforge.net/projects/tinyfiledialogs/),
17 | but has asynchonous dialogs, more maintainable code, and fewer potential
18 | security issues.
19 |
20 | The currently available backends are:
21 |
22 | - Win32 API (all known versions of Windows)
23 | - Mac OS X (using AppleScript)
24 | - GNOME desktop (using [Zenity](https://en.wikipedia.org/wiki/Zenity) or its clones Matedialog and Qarma)
25 | - KDE desktop (using [KDialog](https://github.com/KDE/kdialog))
26 |
27 | Experimental support for Emscripten is on its way.
28 |
29 | ## Documentation
30 |
31 | - [`pfd`](doc/pfd.md) general documentation
32 | - [`pfd::message`](doc/message.md) message box
33 | - [`pfd::notify`](doc/notify.md) notification
34 | - [`pfd::open_file`](doc/open_file.md) file open
35 | - [`pfd::save_file`](doc/save_file.md) file save
36 | - [`pfd::select_folder`](doc/select_folder.md) folder selection
37 |
38 | ## History
39 |
40 | - 0.1.0 (July 16, 2020): first public release
41 |
42 | ## Screenshots (Windows 10)
43 |
44 | 
45 | 
46 | 
47 |
48 | ## Screenshots (Mac OS X, dark theme)
49 |
50 | 
51 | 
52 | 
53 |
54 | ## Screenshots (Linux, GNOME desktop)
55 |
56 | 
57 | 
58 | 
59 |
60 | ## Screenshots (Linux, KDE Plasma desktop)
61 |
62 | 
63 | 
64 | 
65 |
--------------------------------------------------------------------------------
/libdialog/doc/message.md:
--------------------------------------------------------------------------------
1 | ## Message Box API
2 |
3 | Displaying a message box is done using the `pfd::message` class. It can be provided a title, a
4 | message text, a `choice` representing which buttons need to be rendered, and an `icon` for the
5 | message:
6 |
7 | ```cpp
8 | pfd::message::message(std::string const &title,
9 | std::string const &text,
10 | pfd::choice choice = pfd::choice::ok_cancel,
11 | pfd::icon icon = pfd::icon::info);
12 |
13 | enum class pfd::choice { ok, ok_cancel, yes_no, yes_no_cancel };
14 |
15 | enum class pfd::icon { info, warning, error, question };
16 | ```
17 |
18 | The pressed button is queried using `pfd::message::result()`. If the dialog box is closed by any
19 | other means, the `pfd::button::cancel` is assumed:
20 |
21 | ```cpp
22 | pfd::button pfd::message::result();
23 |
24 | enum class pfd::button { ok, cancel, yes, no };
25 | ```
26 |
27 | It is possible to ask the dialog box whether the user took action using the `pfd::message::ready()`
28 | method, with an optional `timeout` argument. If the user did not press a button within `timeout`
29 | milliseconds, the function will return `false`:
30 |
31 | ```cpp
32 | bool pfd::message::ready(int timeout = pfd::default_wait_timeout);
33 | ```
34 |
35 | ## Example 1: simple notification
36 |
37 | The `pfd::message` destructor waits for user action, so this operation will block until the user
38 | closes the message box:
39 |
40 | ```cpp
41 | pfd::message("Problem", "An error occurred while doing things",
42 | pfd::choice::ok, pfd::icon::error);
43 | ```
44 |
45 | ## Example 2: retrieving the pressed button
46 |
47 | Using `pfd::message::result()` will also wait for user action before returning. This operation will block and return the user choice:
48 |
49 | ```cpp
50 | // Ask for user opinion
51 | auto button = pfd::message("Action requested", "Do you want to proceed with things?",
52 | pfd::choice::yes_no, pfd::icon::question).result();
53 | // Do something with button…
54 | ```
55 |
56 | ## Example 3: asynchronous message box
57 |
58 | Using `pfd::message::ready()` allows the application to perform other tasks while waiting for
59 | user input:
60 |
61 | ```cpp
62 | // Message box with nice message
63 | auto box = pfd::message("Unsaved Files", "Do you want to save the current "
64 | "document before closing the application?",
65 | pfd::choice::yes_no_cancel,
66 | pfd::icon::warning);
67 |
68 | // Do something while waiting for user input
69 | while (!box.ready(1000))
70 | std::cout << "Waited 1 second for user input...\n";
71 |
72 | // Act depending on the selected button
73 | switch (box.result())
74 | {
75 | case pfd::button::yes: std::cout << "User agreed.\n"; break;
76 | case pfd::button::no: std::cout << "User disagreed.\n"; break;
77 | case pfd::button::cancel: std::cout << "User freaked out.\n"; break;
78 | }
79 | ```
80 |
81 | ## Screenshots
82 |
83 | #### Windows 10
84 |
85 | 
86 |
87 | #### Mac OS X
88 |
89 |  
90 |
91 | #### Linux (GNOME desktop)
92 |
93 | 
94 |
95 | #### Linux (KDE desktop)
96 |
97 | 
98 |
--------------------------------------------------------------------------------
/libdialog/doc/notify.md:
--------------------------------------------------------------------------------
1 | ## Notification API
2 |
3 | Displaying a desktop notification is done using the `pfd::notify` class. It can be provided a
4 | title, a message text, and an `icon` for the notification style:
5 |
6 | ```cpp
7 | pfd::notify::notify(std::string const &title,
8 | std::string const &text,
9 | pfd::icon icon = pfd::icon::info);
10 |
11 | enum class pfd::icon { info, warning, error };
12 | ```
13 |
14 | ## Example
15 |
16 | Displaying a notification is straightforward. Emoji are supported:
17 |
18 | ```cpp
19 | pfd::notify("System event", "Something might be on fire 🔥",
20 | pfd::icon::warning);
21 | ```
22 |
23 | The `pfd::notify` object needs not be kept around, letting the object clean up itself is enough.
24 |
25 | ## Screenshots
26 |
27 | Windows 10:
28 | 
29 |
30 | Mac OS X (dark theme):
31 | 
32 |
33 | Mac OS X (light theme):
34 | 
35 |
36 | Linux (GNOME desktop):
37 | 
38 |
39 | Linux (KDE desktop):
40 | 
41 |
--------------------------------------------------------------------------------
/libdialog/doc/open_file.md:
--------------------------------------------------------------------------------
1 | ## File Open API
2 |
3 | The `pfd::open_file` class handles file opening dialogs. It can be provided a title, a starting
4 | directory and/or pre-selected file, an optional filter for recognised file types, and an optional
5 | flag to allow multiple selection:
6 |
7 | ```cpp
8 | pfd::open_file::open_file(std::string const &title,
9 | std::string const &initial_path,
10 | std::vector filters = { "All Files", "*" },
11 | pfd::opt option = pfd::opt::none);
12 | ```
13 |
14 | The `option` parameter can be `pfd::opt::multiselect` to allow selecting multiple files.
15 |
16 | The selected files are queried using `pfd::open_file::result()`. If the user canceled the
17 | operation, the returned list is empty:
18 |
19 | ```cpp
20 | std::vector pfd::open_file::result();
21 | ```
22 |
23 | It is possible to ask the file open dialog whether the user took action using the
24 | `pfd::message::ready()` method, with an optional `timeout` argument. If the user did not validate
25 | the dialog within `timeout` milliseconds, the function will return `false`:
26 |
27 | ```cpp
28 | bool pfd::open_file::ready(int timeout = pfd::default_wait_timeout);
29 | ```
30 |
31 | ## Example 1: simple file selection
32 |
33 | Using `pfd::open_file::result()` will wait for user action before returning. This operation will
34 | block and return the user choice:
35 |
36 | ```cpp
37 | auto selection = pfd::open_file("Select a file").result();
38 | if (!selection.empty())
39 | std::cout << "User selected file " << selection[0] << "\n";
40 | ```
41 |
42 | ## Example 2: filters
43 |
44 | The filter list enumerates filter names and corresponded space-separated wildcard lists. It
45 | defaults to `{ "All Files", "*" }`, but here is how to use other options:
46 |
47 | ```cpp
48 | auto selection = pfd::open_file("Select a file", ".",
49 | { "Image Files", "*.png *.jpg *.jpeg *.bmp",
50 | "Audio Files", "*.wav *.mp3",
51 | "All Files", "*" },
52 | pfd::opt::multiselect).result();
53 | // Do something with selection
54 | for (auto const &filename : dialog.result())
55 | std::cout << "Selected file: " << filename << "\n";
56 | ```
57 |
58 | ## Example 3: asynchronous file open
59 |
60 | Using `pfd::open_file::ready()` allows the application to perform other tasks while waiting for
61 | user input:
62 |
63 | ```cpp
64 | // File open dialog
65 | auto dialog = pfd::open_file("Select file to open");
66 |
67 | // Do something while waiting for user input
68 | while (!dialog.ready(1000))
69 | std::cout << "Waited 1 second for user input...\n";
70 |
71 | // Act depending on the user choice
72 | std::cout << "Number of selected files: " << dialog.result().size() << "\n";
73 | ```
74 |
75 | ## Screenshots
76 |
77 | Windows 10:
78 | 
79 |
80 | Mac OS X (dark theme):
81 | 
82 |
83 | Mac OS X (light theme):
84 | 
85 |
86 | Linux (GNOME desktop):
87 | 
88 |
89 | Linux (KDE desktop):
90 | 
91 |
--------------------------------------------------------------------------------
/libdialog/doc/pfd.md:
--------------------------------------------------------------------------------
1 | ## Portable File Dialogs documentation
2 |
3 | The library can be used either as a [header-only library](https://en.wikipedia.org/wiki/Header-only),
4 | or as a [single file library](https://github.com/nothings/single_file_libs).
5 |
6 | ### Use as header-only library
7 |
8 | Just include the main header file wherever needed:
9 |
10 | ```cpp
11 | #include "portable-file-dialogs.h"
12 |
13 | /* ... */
14 |
15 | pfd::message::message("Hello", "This is a test");
16 |
17 | /* ... */
18 | ```
19 |
20 | ### Use as a single-file library
21 |
22 | Defining the `PFD_SKIP_IMPLEMENTATION` macro before including `portable-file-dialogs.h` will
23 | skip all the implementation code and reduce compilation times. You still need to include the
24 | header without the macro at least once, typically in a `pfd-impl.cpp` file.
25 |
26 | ```cpp
27 | // In pfd-impl.cpp
28 | #include "portable-file-dialogs.h"
29 | ```
30 |
31 | ```cpp
32 | // In all other files
33 | #define PFD_SKIP_IMPLEMENTATION 1
34 | #include "portable-file-dialogs.h"
35 | ```
36 |
37 | ### General concepts
38 |
39 | Dialogs inherit from `pfd::dialog` and are created by calling their class constructor. Their
40 | destructor will block until the window is closed by user interaction. So for instance this
41 | will block until the end of the line:
42 |
43 | ```cpp
44 | pfd::message::message("Hi", "there");
45 | ```
46 |
47 | Whereas this will only block until the end of the scope, allowing the program to perform
48 | additional operations while the dialog is open:
49 |
50 | ```cpp
51 | {
52 | auto m = pfd::message::message("Hi", "there");
53 |
54 | // ... perform asynchronous operations here
55 | }
56 | ```
57 |
58 | It is possible to call `bool pfd::dialog::ready(timeout)` on the dialog in order to query its
59 | status and perform asynchronous operations as long as the user has not interacted:
60 |
61 | ```cpp
62 | {
63 | auto m = pfd::message::message("Hi", "there");
64 |
65 | while (!m.ready())
66 | {
67 | // ... perform asynchronous operations here
68 | }
69 | }
70 | ```
71 |
72 | If necessary, a dialog can be forcibly closed using `bool pfd::dialog::kill()`. Note that this
73 | may be confusing to the user and should only be used in very specific situations. It is also not
74 | possible to close a Windows message box that provides no _Cancel_ button.
75 |
76 | ```cpp
77 | {
78 | auto m = pfd::message::message("Hi", "there");
79 |
80 | while (!m.ready())
81 | {
82 | // ... perform asynchronous operations here
83 |
84 | if (too_much_time_has_passed())
85 | m.kill();
86 | }
87 | }
88 | ```
89 |
90 | Finally, the user response can be retrieved using `pfd::dialog::result()`. The return value of
91 | this function depends on which dialog is being used. See their respective documentation for more
92 | information:
93 |
94 | * [`pfd::message`](message.md) (message box)
95 | * [`pfd::notify`](notify.md) (notification)
96 | * [`pfd::open_file`](open_file.md) (file open)
97 | * [`pfd::save_file`](save_file.md) (file save)
98 | * [`pfd::select_folder`](select_folder.md) (folder selection)
99 |
100 | ### Settings
101 |
102 | The library can be queried and configured through the `pfd::settings` class.
103 |
104 | ```cpp
105 | bool pfd::settings::available();
106 | void pfd::settings::verbose(bool value);
107 | void pfd::settings::rescan();
108 | ```
109 |
110 | The return value of `pfd::settings::available()` indicates whether a suitable dialog backend (such
111 | as Zenity or KDialog on Linux) has been found. If not, the library will not work and all dialog
112 | invocations will be no-ops. The program will not crash but you should account for this situation
113 | and add a fallback mechanism or exit gracefully.
114 |
115 | Calling `pfd::settings::rescan()` will force a rescan of available backends. This may change the
116 | result of `pfd::settings::available()` if a backend was installed on the system in the meantime.
117 | This is probably only useful for debugging purposes.
118 |
119 | Calling `pfd::settings::verbose(true)` may help debug the library. It will output debug information
120 | to `std::cout` about some operations being performed.
121 |
--------------------------------------------------------------------------------
/libdialog/doc/save_file.md:
--------------------------------------------------------------------------------
1 | ## File Open API
2 |
3 | The `pfd::save_file` class handles file saving dialogs. It can be provided a title, a starting
4 | directory and/or pre-selected file, an optional filter for recognised file types, and an optional
5 | flag to allow multiple selection:
6 |
7 | ```cpp
8 | pfd::save_file::save_file(std::string const &title,
9 | std::string const &initial_path,
10 | std::vector filters = { "All Files", "*" },
11 | pfd::opt option = pfd::opt::none);
12 | ```
13 |
14 | The `option` parameter can be `pfd::opt::force_overwrite` to disable a potential warning when
15 | saving to an existing file.
16 |
17 | The selected file is queried using `pfd::save_file::result()`. If the user canceled the
18 | operation, the returned file name is empty:
19 |
20 | ```cpp
21 | std::string pfd::save_file::result();
22 | ```
23 |
24 | It is possible to ask the file save dialog whether the user took action using the
25 | `pfd::message::ready()` method, with an optional `timeout` argument. If the user did not validate
26 | the dialog within `timeout` milliseconds, the function will return `false`:
27 |
28 | ```cpp
29 | bool pfd::save_file::ready(int timeout = pfd::default_wait_timeout);
30 | ```
31 |
32 | ## Example 1: simple file selection
33 |
34 | Using `pfd::save_file::result()` will wait for user action before returning. This operation will
35 | block and return the user choice:
36 |
37 | ```cpp
38 | auto destination = pfd::save_file("Select a file").result();
39 | if (!destination.empty())
40 | std::cout << "User selected file " << destination << "\n";
41 | ```
42 |
43 | ## Example 2: filters
44 |
45 | The filter list enumerates filter names and corresponded space-separated wildcard lists. It
46 | defaults to `{ "All Files", "*" }`, but here is how to use other options:
47 |
48 | ```cpp
49 | auto destination = pfd::save_file("Select a file", ".",
50 | { "Image Files", "*.png *.jpg *.jpeg *.bmp",
51 | "Audio Files", "*.wav *.mp3",
52 | "All Files", "*" },
53 | pfd::opt::force_overwrite).result();
54 | // Do something with destination
55 | std::cout << "Selected file: " << destination << "\n";
56 | ```
57 |
58 | ## Example 3: asynchronous file save
59 |
60 | Using `pfd::save_file::ready()` allows the application to perform other tasks while waiting for
61 | user input:
62 |
63 | ```cpp
64 | // File save dialog
65 | auto dialog = pfd::save_file("Select file to save");
66 |
67 | // Do something while waiting for user input
68 | while (!dialog.ready(1000))
69 | std::cout << "Waited 1 second for user input...\n";
70 |
71 | // Act depending on the user choice
72 | std::cout << "User selected file: " << dialog.result() << "\n";
73 | ```
74 |
--------------------------------------------------------------------------------
/libdialog/doc/select_folder.md:
--------------------------------------------------------------------------------
1 | ## Folder Selection API
2 |
3 | The `pfd::select_folder` class handles folder opening dialogs. It can be provided a title, and an
4 | optional starting directory:
5 |
6 | ```cpp
7 | pfd::select_folder::select_folder(std::string const &title,
8 | std::string const &default_path = "",
9 | pfd::opt option = pfd::opt::none);
10 | ```
11 |
12 | The `option` parameter can be `pfd::opt::force_path` to force the operating system to use the
13 | provided path. Some systems default to the most recently used path, if applicable.
14 |
15 | The selected folder is queried using `pfd::select_folder::result()`. If the user canceled the
16 | operation, the returned string is empty:
17 |
18 | ```cpp
19 | std::string pfd::select_folder::result();
20 | ```
21 |
22 | It is possible to ask the folder selection dialog whether the user took action using the
23 | `pfd::message::ready()` method, with an optional `timeout` argument. If the user did not validate
24 | the dialog within `timeout` milliseconds, the function will return `false`:
25 |
26 | ```cpp
27 | bool pfd::select_folder::ready(int timeout = pfd::default_wait_timeout);
28 | ```
29 |
30 | ## Example 1: simple folder selection
31 |
32 | Using `pfd::select_folder::result()` will wait for user action before returning. This operation
33 | will block and return the user choice:
34 |
35 | ```cpp
36 | auto selection = pfd::select_folder("Select a folder").result();
37 | if (!selection.empty())
38 | std::cout << "User selected folder " << selection << "\n";
39 | ```
40 |
41 | ## Example 2: asynchronous folder open
42 |
43 | Using `pfd::select_folder::ready()` allows the application to perform other tasks while waiting for user input:
44 |
45 | ```cpp
46 | // Folder selection dialog
47 | auto dialog = pfd::select_folder("Select folder to open");
48 |
49 | // Do something while waiting for user input
50 | while (!dialog.ready(1000))
51 | std::cout << "Waited 1 second for user input...\n";
52 |
53 | // Act depending on the user choice
54 | std::cout << "Selected folder: " << dialog.result() << "\n";
55 | ```
56 |
--------------------------------------------------------------------------------
/libdialog/examples/Makefile:
--------------------------------------------------------------------------------
1 |
2 | BINARIES = example kill
3 |
4 | CXXFLAGS = -I.. -std=c++11 -g -ggdb -Wall -Wextra
5 |
6 | all: $(BINARIES)
7 |
8 | example: example.cpp ../portable-file-dialogs.h
9 | $(CXX) $(CXXFLAGS) $(filter %.cpp, $^) -o $@
10 |
11 | kill: kill.cpp ../portable-file-dialogs.h
12 | $(CXX) $(CXXFLAGS) $(filter %.cpp, $^) -o $@
13 |
14 | clean:
15 | rm -f $(BINARIES)
16 |
17 |
--------------------------------------------------------------------------------
/libdialog/examples/example.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Portable File Dialogs
3 | //
4 | // Copyright © 2018—2020 Sam Hocevar
5 | //
6 | // This program is free software. It comes without any warranty, to
7 | // the extent permitted by applicable law. You can redistribute it
8 | // and/or modify it under the terms of the Do What the Fuck You Want
9 | // to Public License, Version 2, as published by the WTFPL Task Force.
10 | // See http://www.wtfpl.net/ for more details.
11 | //
12 |
13 | #include "portable-file-dialogs.h"
14 |
15 | #include
16 |
17 | #if _WIN32
18 | #define DEFAULT_PATH "C:\\"
19 | #else
20 | #define DEFAULT_PATH "/tmp"
21 | #endif
22 |
23 | int main()
24 | {
25 | // Check that a backend is available
26 | if (!pfd::settings::available())
27 | {
28 | std::cout << "Portable File Dialogs are not available on this platform.\n";
29 | return 1;
30 | }
31 |
32 | // Set verbosity to true
33 | pfd::settings::verbose(true);
34 |
35 | // Notification
36 | pfd::notify("Important Notification",
37 | "This is ' a message, pay \" attention \\ to it!",
38 | pfd::icon::info);
39 |
40 | // Message box with nice message
41 | auto m = pfd::message("Personal Message",
42 | "You are an amazing person, don’t let anyone make you think otherwise.",
43 | pfd::choice::yes_no_cancel,
44 | pfd::icon::warning);
45 |
46 | // Optional: do something while waiting for user action
47 | for (int i = 0; i < 10 && !m.ready(1000); ++i)
48 | std::cout << "Waited 1 second for user input...\n";
49 |
50 | // Do something according to the selected button
51 | switch (m.result())
52 | {
53 | case pfd::button::yes: std::cout << "User agreed.\n"; break;
54 | case pfd::button::no: std::cout << "User disagreed.\n"; break;
55 | case pfd::button::cancel: std::cout << "User freaked out.\n"; break;
56 | default: break; // Should not happen
57 | }
58 |
59 | // Directory selection
60 | auto dir = pfd::select_folder("Select any directory", DEFAULT_PATH).result();
61 | std::cout << "Selected dir: " << dir << "\n";
62 |
63 | // File open
64 | auto f = pfd::open_file("Choose files to read", DEFAULT_PATH,
65 | { "Text Files (.txt .text)", "*.txt *.text",
66 | "All Files", "*" },
67 | pfd::opt::multiselect);
68 | std::cout << "Selected files:";
69 | for (auto const &name : f.result())
70 | std::cout << " " + name;
71 | std::cout << "\n";
72 | }
73 |
74 | // Unused function that just tests the whole API
75 | void api()
76 | {
77 | // pfd::settings
78 | pfd::settings::verbose(true);
79 | pfd::settings::rescan();
80 |
81 | // pfd::notify
82 | pfd::notify("", "");
83 | pfd::notify("", "", pfd::icon::info);
84 | pfd::notify("", "", pfd::icon::warning);
85 | pfd::notify("", "", pfd::icon::error);
86 | pfd::notify("", "", pfd::icon::question);
87 |
88 | pfd::notify a("", "");
89 | (void)a.ready();
90 | (void)a.ready(42);
91 |
92 | // pfd::message
93 | pfd::message("", "");
94 | pfd::message("", "", pfd::choice::ok);
95 | pfd::message("", "", pfd::choice::ok_cancel);
96 | pfd::message("", "", pfd::choice::yes_no);
97 | pfd::message("", "", pfd::choice::yes_no_cancel);
98 | pfd::message("", "", pfd::choice::retry_cancel);
99 | pfd::message("", "", pfd::choice::abort_retry_ignore);
100 | pfd::message("", "", pfd::choice::ok, pfd::icon::info);
101 | pfd::message("", "", pfd::choice::ok, pfd::icon::warning);
102 | pfd::message("", "", pfd::choice::ok, pfd::icon::error);
103 | pfd::message("", "", pfd::choice::ok, pfd::icon::question);
104 |
105 | pfd::message b("", "");
106 | (void)b.ready();
107 | (void)b.ready(42);
108 | (void)b.result();
109 | }
110 |
111 |
--------------------------------------------------------------------------------
/libdialog/examples/example.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | 15.0
29 | {10F4364D-27C4-4C74-8079-7C42971E81E7}
30 | Win32Proj
31 | example
32 | 10.0
33 |
34 |
35 |
36 | Application
37 | v142
38 | Unicode
39 |
40 |
41 | true
42 |
43 |
44 | false
45 | true
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | true
58 |
59 |
60 | false
61 |
62 |
63 |
64 | ..
65 | NotUsing
66 | Level3
67 | true
68 | true
69 |
70 |
71 | Console
72 | true
73 |
74 |
75 |
76 |
77 | Disabled
78 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
79 |
80 |
81 |
82 |
83 | MaxSpeed
84 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
85 | true
86 | true
87 |
88 |
89 | true
90 | true
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/libdialog/examples/kill.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Portable File Dialogs
3 | //
4 | // Copyright © 2018—2020 Sam Hocevar
5 | //
6 | // This program is free software. It comes without any warranty, to
7 | // the extent permitted by applicable law. You can redistribute it
8 | // and/or modify it under the terms of the Do What the Fuck You Want
9 | // to Public License, Version 2, as published by the WTFPL Task Force.
10 | // See http://www.wtfpl.net/ for more details.
11 | //
12 |
13 | #include "portable-file-dialogs.h"
14 |
15 | #include
16 |
17 | int main()
18 | {
19 | // Set verbosity to true
20 | pfd::settings::verbose(true);
21 |
22 | // Message box with nice message
23 | auto m = pfd::message("Upgrade software?",
24 | "Press OK to upgrade this software.\n"
25 | "\n"
26 | "By default, the software will update itself\n"
27 | "automatically in 10 seconds.",
28 | pfd::choice::ok_cancel,
29 | pfd::icon::warning);
30 |
31 | // Wait for an answer for up to 10 seconds
32 | for (int i = 0; i < 10 && !m.ready(1000); ++i)
33 | ;
34 |
35 | // Upgrade software if user clicked OK, or if user didn’t interact
36 | bool upgrade = m.ready() ? m.result() == pfd::button::ok : m.kill();
37 | if (upgrade)
38 | std::cout << "Upgrading software!\n";
39 | else
40 | std::cout << "Not upgrading software.\n";
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/libdialog/examples/kill.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | 15.0
29 | {B94D26B1-7EF7-43A2-A973-9A96A08E2E17}
30 | Win32Proj
31 | kill
32 | 10.0
33 |
34 |
35 |
36 | Application
37 | v142
38 | Unicode
39 |
40 |
41 | true
42 |
43 |
44 | false
45 | true
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | true
58 |
59 |
60 | false
61 |
62 |
63 |
64 | ..
65 | NotUsing
66 | Level3
67 | true
68 | true
69 |
70 |
71 | Console
72 | true
73 |
74 |
75 |
76 |
77 | Disabled
78 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
79 |
80 |
81 |
82 |
83 | MaxSpeed
84 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
85 | true
86 | true
87 |
88 |
89 | true
90 | true
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/libdxfrw/Makefile.am:
--------------------------------------------------------------------------------
1 | # -*- Makefile -*-
2 |
3 | AM_CPPFLAGS = ${my_CPPFLAGS} -Wall -Woverloaded-virtual
4 | ACLOCAL_AMFLAGS = -I m4
5 |
6 | library_includedir=$(includedir)/libdxfrw$(LIBRARY_AGE)
7 | library_include_HEADERS = drw_base.h drw_entities.h drw_interface.h \
8 | drw_objects.h drw_header.h drw_classes.h libdxfrw.h libdwgr.h
9 | dist_noinst_HEADERS = intern/dxfreader.h intern/dxfwriter.h intern/drw_dbg.h \
10 | intern/dwgutil.h intern/dwgreader.h intern/dwgreader15.h \
11 | intern/dwgreader18.h intern/dwgreader21.h intern/dwgreader24.h \
12 | intern/dwgreader27.h intern/dwgbuffer.h intern/drw_cptable932.h \
13 | intern/drw_cptable936.h intern/drw_cptable949.h intern/drw_cptable950.h \
14 | intern/drw_cptables.h intern/drw_textcodec.h intern/rscodec.h
15 |
16 | lib_LTLIBRARIES = libdxfrw.la
17 |
18 | libdxfrw_la_SOURCES = drw_entities.cpp drw_objects.cpp drw_header.cpp intern/drw_dbg.cpp \
19 | drw_classes.cpp libdwgr.cpp libdxfrw.cpp intern/dwgutil.cpp \
20 | intern/dxfreader.cpp intern/dwgreader15.cpp intern/dwgreader18.cpp intern/dwgreader21.cpp \
21 | intern/dwgreader24.cpp intern/dwgreader27.cpp intern/dxfwriter.cpp intern/dwgreader.cpp \
22 | intern/dwgbuffer.cpp intern/drw_textcodec.cpp intern/rscodec.cpp
23 |
24 | libdxfrw_la_LDFLAGS = -no-undefined -version-number $(LIBRARY_AGE):$(LIBRARY_CURRENT):$(LIBRARY_REVISION)
25 |
26 | libdxfrw_la_LIBADD =
27 |
28 | pkgconfigdir = ${libdir}/pkgconfig
29 | pkgconfig_DATA = ${top_builddir}/libdxfrw$(LIBRARY_AGE).pc
30 |
31 | EXTRA_DIST = ${top_builddir}/autogen.sh ${top_builddir}/makefile.mingw ${top_builddir}/vs2013/*
32 |
33 | libdxfrw.pc: ${top_builddir}/config.status
34 |
--------------------------------------------------------------------------------
/libdxfrw/drw_classes.cpp:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | ** libDXFrw - Library to read/write DXF files (ascii & binary) **
3 | ** **
4 | ** Copyright (C) 2011-2015 José F. Soriano, rallazz@gmail.com **
5 | ** **
6 | ** This library is free software, licensed under the terms of the GNU **
7 | ** General Public License as published by the Free Software Foundation, **
8 | ** either version 2 of the License, or (at your option) any later version. **
9 | ** You should have received a copy of the GNU General Public License **
10 | ** along with this program. If not, see . **
11 | ******************************************************************************/
12 |
13 | #include "drw_classes.h"
14 | #include "intern/dxfreader.h"
15 | #include "intern/dxfwriter.h"
16 | #include "intern/dwgbuffer.h"
17 | #include "intern/drw_dbg.h"
18 |
19 | void DRW_Class::parseCode(int code, dxfReader *reader){
20 | switch (code) {
21 | case 1:
22 | recName = reader->getUtf8String();
23 | break;
24 | case 2:
25 | className = reader->getUtf8String();
26 | break;
27 | case 3:
28 | appName = reader->getUtf8String();
29 | break;
30 | case 90:
31 | proxyFlag = reader->getInt32();
32 | break;
33 | case 91:
34 | instanceCount = reader->getInt32();
35 | break;
36 | case 280:
37 | wasaProxyFlag = reader->getInt32();
38 | break;
39 | case 281:
40 | entityFlag = reader->getInt32();
41 | break;
42 | default:
43 | break;
44 | }
45 | }
46 |
47 | bool DRW_Class::parseDwg(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf){
48 | DRW_DBG("\n***************************** parsing Class *********************************************\n");
49 |
50 | classNum = buf->getBitShort();
51 | DRW_DBG("Class number: "); DRW_DBG(classNum);
52 | proxyFlag = buf->getBitShort(); //in dwg specs says "version"
53 |
54 | appName = strBuf->getVariableText(version, false);
55 | className = strBuf->getVariableText(version, false);
56 | recName = strBuf->getVariableText(version, false);
57 |
58 | DRW_DBG("\napp name: "); DRW_DBG(appName.c_str());
59 | DRW_DBG("\nclass name: "); DRW_DBG(className.c_str());
60 | DRW_DBG("\ndxf rec name: "); DRW_DBG(recName.c_str());
61 | wasaProxyFlag = buf->getBit(); //in dwg says wasazombie
62 | entityFlag = buf->getBitShort();
63 | entityFlag = entityFlag == 0x1F2 ? 1: 0;
64 |
65 | DRW_DBG("\nProxy capabilities flag: "); DRW_DBG(proxyFlag);
66 | DRW_DBG(", proxy flag (280): "); DRW_DBG(wasaProxyFlag);
67 | DRW_DBG(", entity flag: "); DRW_DBGH(entityFlag);
68 |
69 | if (version > DRW::AC1015) {//2004+
70 | instanceCount = buf->getBitLong();
71 | DRW_DBG("\nInstance Count: "); DRW_DBG(instanceCount);
72 | duint32 dwgVersion = buf->getBitLong();
73 | DRW_DBG("\nDWG version: "); DRW_DBG(dwgVersion);
74 | DRW_DBG("\nmaintenance version: "); DRW_DBG(buf->getBitLong());
75 | DRW_DBG("\nunknown 1: "); DRW_DBG(buf->getBitLong());
76 | DRW_DBG("\nunknown 2: "); DRW_DBG(buf->getBitLong());
77 | }
78 | DRW_DBG("\n");
79 | toDwgType();
80 | return buf->isGood();
81 | }
82 |
83 | void DRW_Class::write(dxfWriter *writer, DRW::Version ver){
84 | if (ver > DRW::AC1009) {
85 | writer->writeString(0, "CLASS");
86 | writer->writeString(1, recName);
87 | writer->writeString(2, className);
88 | writer->writeString(3, appName);
89 | writer->writeInt32(90, proxyFlag);
90 | if (ver > DRW::AC1015) { //2004+
91 | writer->writeInt32(91, instanceCount);
92 | }
93 | writer->writeInt16(280, wasaProxyFlag);
94 | writer->writeInt16(281, entityFlag);
95 | }
96 | }
97 |
98 | void DRW_Class::toDwgType(){
99 | if (recName == "LWPOLYLINE")
100 | dwgType = 77;
101 | else if (recName == "HATCH")
102 | dwgType = 78;
103 | else if (recName == "GROUP")
104 | dwgType = 72;
105 | /* else if (recName == "GROUP")
106 | dwgType = 72;*/
107 | else if (recName == "LAYOUT")
108 | dwgType = 82;
109 | else if (recName == "IMAGE")
110 | dwgType = 101;
111 | else if (recName == "IMAGEDEF")
112 | dwgType = 102;
113 | else
114 | dwgType =0;
115 | }
116 |
--------------------------------------------------------------------------------
/libdxfrw/drw_classes.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | ** libDXFrw - Library to read/write DXF files (ascii & binary) **
3 | ** **
4 | ** Copyright (C) 2011-2015 José F. Soriano, rallazz@gmail.com **
5 | ** **
6 | ** This library is free software, licensed under the terms of the GNU **
7 | ** General Public License as published by the Free Software Foundation, **
8 | ** either version 2 of the License, or (at your option) any later version. **
9 | ** You should have received a copy of the GNU General Public License **
10 | ** along with this program. If not, see . **
11 | ******************************************************************************/
12 |
13 | #ifndef DRW_CLASSES_H
14 | #define DRW_CLASSES_H
15 |
16 |
17 | #include "drw_base.h"
18 | //#include "libdwgr.h"
19 |
20 | class dxfReader;
21 | class dxfWriter;
22 | class dwgBuffer;
23 |
24 | //! Class to handle classes entries
25 | /*!
26 | * Class to handle classes table entries
27 | * TODO: verify the dxf read/write part
28 | * @author Rallaz
29 | */
30 | class DRW_Class {
31 | public:
32 | DRW_Class() {
33 | }
34 | ~DRW_Class() {
35 | }
36 |
37 | void parseCode(int code, dxfReader *reader);
38 | void write(dxfWriter *writer, DRW::Version ver);
39 | bool parseDwg(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf);
40 |
41 | private:
42 | void toDwgType();
43 | public:
44 | UTF8STRING recName; /*!< record name, code 1 */
45 | UTF8STRING className; /*!< C++ class name, code 2 */
46 | UTF8STRING appName; /*!< app name, code 3 */
47 | int proxyFlag; /*!< Proxy capabilities flag, code 90 */
48 | int instanceCount; /*!< number of instances for a custom class, code 91*/
49 | int wasaProxyFlag; /*!< proxy flag (app loaded on save), code 280 */
50 | int entityFlag; /*!< entity flag, code 281 (0 object, 1 entity)*/
51 | public: //only for read dwg
52 | duint16 classNum;
53 | int dwgType;
54 | };
55 |
56 | #endif
57 |
58 | // EOF
59 |
60 |
--------------------------------------------------------------------------------
/libdxfrw/drw_header.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | ** libDXFrw - Library to read/write DXF files (ascii & binary) **
3 | ** **
4 | ** Copyright (C) 2011-2015 José F. Soriano, rallazz@gmail.com **
5 | ** **
6 | ** This library is free software, licensed under the terms of the GNU **
7 | ** General Public License as published by the Free Software Foundation, **
8 | ** either version 2 of the License, or (at your option) any later version. **
9 | ** You should have received a copy of the GNU General Public License **
10 | ** along with this program. If not, see . **
11 | ******************************************************************************/
12 |
13 | #ifndef DRW_HEADER_H
14 | #define DRW_HEADER_H
15 |
16 |
17 | #include