├── CMakeLists.txt ├── CPackConfig.cmake ├── LICENSE ├── README.md ├── cmake └── FindPolyclipping.cmake └── src ├── GcodeWriter.cpp ├── GcodeWriter.h ├── OrderOptimizer.cpp ├── OrderOptimizer.h ├── PathReader.cpp ├── PathReader.h ├── Statistics.cpp ├── Statistics.h ├── TestGeometry ├── Jin.h ├── Microstructure.h ├── Moessen.h ├── Pika.h ├── Prescribed.h ├── SVGloader.h ├── Spiky.h ├── TXTloader.h └── VariableWidthGcodeTester.h ├── main.cpp ├── pathOrderOptimizer.cpp ├── pathOrderOptimizer.h ├── timeEstimate.cpp ├── timeEstimate.h ├── utils ├── AABB.cpp ├── AABB.h ├── AABB3D.cpp ├── AABB3D.h ├── Coord_t.h ├── ExtrusionJunction.h ├── ExtrusionLine.h ├── ExtrusionSegment.cpp ├── ExtrusionSegment.h ├── HalfEdge.h ├── HalfEdgeGraph.h ├── HalfEdgeNode.h ├── IntPoint.h ├── LinearAlg2D.cpp ├── ListPolyIt.cpp ├── ListPolyIt.h ├── NoCopy.h ├── Point3.cpp ├── Point3.h ├── PolygonsPointIndex.cpp ├── PolygonsPointIndex.h ├── PolygonsSegmentIndex.h ├── STLwriter.cpp ├── STLwriter.h ├── SVG.cpp ├── SVG.h ├── SparseGrid.h ├── SparseLineGrid.h ├── SparsePointGrid.h ├── SparsePointGridInclusive.h ├── SymmetricPair.h ├── UnionFind.h ├── VoronoiUtils.cpp ├── VoronoiUtils.h ├── floatpoint.h ├── gettime.cpp ├── gettime.h ├── linearAlg2D.h ├── logoutput.cpp ├── logoutput.h ├── macros.h ├── math.h ├── polygon.cpp ├── polygon.h ├── polygonUtils.cpp ├── polygonUtils.h └── types │ ├── AngleDegrees.h │ ├── AngleRadians.h │ ├── Duration.h │ ├── EnumSettings.h │ ├── LayerIndex.h │ ├── Ratio.h │ ├── Temperature.h │ └── Velocity.h ├── voronoi_visual_utils.hpp └── voronoi_visualizer.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake) 3 | project(visualizer) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | ############################################################################### 8 | ## file globbing ############################################################## 9 | ############################################################################### 10 | 11 | # these instructions search the directory tree when cmake is 12 | # invoked and put all files that match the pattern in the variables 13 | # `sources` and `data` 14 | file(GLOB_RECURSE sources src/*.cpp src/*.h) 15 | file(GLOB_RECURSE sources_test src/test/*.cpp) 16 | file(GLOB_RECURSE data resources/*) 17 | 18 | 19 | ############################################################################### 20 | ## target definitions ######################################################### 21 | ############################################################################### 22 | 23 | # add the data to the target, so it becomes visible in some IDE 24 | add_executable(visualizer ${sources}) 25 | 26 | # just for example add some compiler flags 27 | target_compile_options(visualizer PUBLIC -std=c++17 -Wall -Wno-unused-variable -Wno-sign-compare -Wno-unused-but-set-variable -Wno-unknown-pragmas -o3) 28 | 29 | # this lets me include files relative to the root src dir with a <> pair 30 | target_include_directories(visualizer PUBLIC src) 31 | 32 | if(CMAKE_BUILD_TYPE) 33 | string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPER) 34 | endif() 35 | if(CMAKE_BUILD_TYPE_UPPER MATCHES "DEBUG") 36 | message(STATUS "Building debug release.") 37 | add_definitions(-DDEBUG) 38 | endif() 39 | 40 | ############################################################################### 41 | ## dependencies ############################################################### 42 | ############################################################################### 43 | 44 | # this defines the variables Boost_LIBRARIES that contain all library names 45 | # that we need to link to 46 | #find_package(Stb REQUIRED) 47 | #include_directories(${Stb_INCLUDE_DIRS}) 48 | #include_directories(${CMAKE_CURRENT_BINARY_DIR} libs) 49 | #add_dependencies(visualizer stb) 50 | 51 | find_package( Boost 1.65 COMPONENTS REQUIRED ) 52 | include_directories( ${Boost_INCLUDE_DIR} ) 53 | 54 | target_link_libraries( visualizer LINK_PUBLIC ${Boost_LIBRARIES} ) 55 | 56 | 57 | find_package(Polyclipping REQUIRED) 58 | include_directories(${Polyclipping_INCLUDE_DIRS} ${CMAKE_BINARY_DIR} ${RAPIDJSON_INCLUDE_DIRS}) 59 | target_link_libraries(visualizer ${Polyclipping_LIBRARIES}) 60 | 61 | 62 | ############################################################################### 63 | ## testing #################################################################### 64 | ############################################################################### 65 | 66 | # this is for our testing framework 67 | # we don't add REQUIRED because it's just for testing 68 | #find_package(GTest) 69 | 70 | if(GTEST_FOUND) 71 | add_executable(unit_tests ${sources_test} ${sources}) 72 | 73 | # we add this define to prevent collision with the main 74 | # this might be better solved by not adding the source with the main to the 75 | # testing target 76 | target_compile_definitions(unit_tests PUBLIC UNIT_TESTS) 77 | 78 | # this allows us to use our executable as a link library 79 | # therefore we can inherit all compiler options and library dependencies 80 | set_target_properties(visualizer PROPERTIES ENABLE_EXPORTS on) 81 | 82 | target_link_libraries(unit_tests PUBLIC 83 | ${GTEST_BOTH_LIBRARIES} 84 | visualizer 85 | ) 86 | 87 | target_include_directories(unit_tests PUBLIC 88 | ${GTEST_INCLUDE_DIRS} # doesn't do anything on Linux 89 | ) 90 | endif() 91 | 92 | ############################################################################### 93 | ## packaging ################################################################## 94 | ############################################################################### 95 | 96 | # all install commands get the same destination. this allows us to use paths 97 | # relative to the executable. 98 | install(TARGETS visualizer DESTINATION example_destination) 99 | # this is basically a repeat of the file copy instruction that copies the 100 | # resources in the build directory, but here we tell cmake that we want it 101 | # in the package 102 | install(DIRECTORY resources DESTINATION example_destination) 103 | 104 | # now comes everything we need, to create a package 105 | # there are a lot more variables you can set, and some 106 | # you need to set for some package types, but we want to 107 | # be minimal here 108 | set(CPACK_PACKAGE_NAME "visualizer") 109 | set(CPACK_PACKAGE_VERSION "1.0.0") 110 | 111 | # we don't want to split our program up into several things 112 | set(CPACK_MONOLITHIC_INSTALL 1) 113 | 114 | # This must be last 115 | include(CPackConfig.cmake) 116 | -------------------------------------------------------------------------------- /CPackConfig.cmake: -------------------------------------------------------------------------------- 1 | set(CPACK_PACKAGE_VENDOR "Ultimaker") 2 | set(CPACK_PACKAGE_CONTACT "Tim Kuipers ") 3 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Lithophane 3D") 4 | set(CPACK_PACKAGE_VERSION "0.0.1") 5 | set(CPACK_GENERATOR "DEB") 6 | if(NOT DEFINED CPACK_DEBIAN_PACKAGE_ARCHITECTURE) 7 | execute_process(COMMAND dpkg --print-architecture OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE) 8 | endif() 9 | set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") 10 | 11 | set(DEB_DEPENDS 12 | "libstdc++6 (>= 4.9.0)" 13 | "libgcc1 (>= 4.9.0)" 14 | ) 15 | string(REPLACE ";" ", " DEB_DEPENDS "${DEB_DEPENDS}") 16 | set(CPACK_DEBIAN_PACKAGE_DEPENDS ${DEB_DEPENDS}) 17 | 18 | include(CPack) 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ToolpathVisualizer 2 | A simple tool to visualize and generate gcode for variable width single layer toolpaths. Used for testing. 3 | 4 | This tool was used in testing [libArachne](https://github.com/Ultimaker/libarachne) and [Variable width contouring](https://github.com/mfx-inria/Variable-width-contouring). 5 | Both these libraries were used to produce files describing the locations and widths of extrusion paths for one single layer of a 3D print. 6 | -------------------------------------------------------------------------------- /cmake/FindPolyclipping.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # - Try to find the polyclipping library 3 | # this will define 4 | # 5 | # Polyclipping_FOUND - polyclipping was found 6 | # Polyclipping_INCLUDE_DIRS - the polyclipping include directory 7 | # Polyclipping_LIBRARIES - The libraries needed to use polyclipping 8 | # Polyclipping_VERSION - The polyclipping library version 9 | 10 | #============================================================================= 11 | # Copyright (c) 2017 Christophe Giboudeaux 12 | # 13 | # 14 | # Redistribution and use in source and binary forms, with or without 15 | # modification, are permitted provided that the following conditions 16 | # are met: 17 | # 18 | # 1. Redistributions of source code must retain the copyright 19 | # notice, this list of conditions and the following disclaimer. 20 | # 2. Redistributions in binary form must reproduce the copyright 21 | # notice, this list of conditions and the following disclaimer in the 22 | # documentation and/or other materials provided with the distribution. 23 | # 3. The name of the author may not be used to endorse or promote products 24 | # derived from this software without specific prior written permission. 25 | # 26 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28 | # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29 | # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | #============================================================================= 37 | 38 | 39 | find_package(PkgConfig QUIET) 40 | pkg_check_modules(PC_Polyclipping QUIET polyclipping) 41 | 42 | find_path(Polyclipping_INCLUDE_DIRS 43 | NAMES clipper.hpp 44 | HINTS ${PC_Polyclipping_INCLUDE_DIRS} 45 | ) 46 | 47 | find_library(Polyclipping_LIBRARIES 48 | NAMES polyclipping 49 | HINTS ${PC_Polyclipping_LIBRARY_DIRS} 50 | ) 51 | 52 | if(EXISTS ${Polyclipping_INCLUDE_DIRS}/clipper.hpp) 53 | file(READ ${Polyclipping_INCLUDE_DIRS}/clipper.hpp CLIPPER_H_CONTENT) 54 | string(REGEX MATCH "#define CLIPPER_VERSION[ ]+\"[0-9]+.[0-9]+.[0-9]+\"" CLIPPER_H_VERSION_MATCH ${CLIPPER_H_CONTENT}) 55 | string(REGEX REPLACE "^.*CLIPPER_VERSION[ ]+\"([0-9]+.[0-9]+.[0-9]+).*$" "\\1" Polyclipping_VERSION "${CLIPPER_H_VERSION_MATCH}") 56 | endif() 57 | 58 | include(FindPackageHandleStandardArgs) 59 | 60 | find_package_handle_standard_args(Polyclipping 61 | FOUND_VAR Polyclipping_FOUND 62 | REQUIRED_VARS Polyclipping_LIBRARIES Polyclipping_INCLUDE_DIRS 63 | VERSION_VAR Polyclipping_VERSION 64 | ) 65 | 66 | mark_as_advanced(Polyclipping_LIBRARIES Polyclipping_INCLUDE_DIRS Polyclipping_VERSION) 67 | 68 | -------------------------------------------------------------------------------- /src/GcodeWriter.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef GCODER_H 5 | #define GCODER_H 6 | 7 | #include 8 | 9 | #include "utils/polygon.h" 10 | #include "utils/AABB.h" 11 | #include "utils/ExtrusionJunction.h" 12 | #include "utils/ExtrusionLine.h" 13 | #include "utils/types/Duration.h" 14 | 15 | #include "timeEstimate.h" 16 | 17 | namespace visualizer 18 | { 19 | 20 | /*! 21 | * Write gcode for a single layer variable line width print 22 | */ 23 | class GcodeWriter 24 | { 25 | struct Path 26 | { 27 | std::vector junctions; 28 | bool is_closed; 29 | Path(bool is_closed) 30 | : is_closed(is_closed) 31 | {} 32 | }; 33 | public: 34 | GcodeWriter(std::string filename, int type, bool dual_extrusion, coord_t layer_thickness, float print_speed, float travel_speed, float extrusion_multiplier, bool equalize_flow); 35 | ~GcodeWriter(); 36 | static constexpr int type_UM3 = 1; 37 | static constexpr int type_UMS5 = 2; 38 | void printBrim(const Polygons& outline, coord_t count, coord_t w = 400, coord_t dist = 600); 39 | void printRaft(const Polygons& outline); 40 | 41 | std::vector generateLines(const Polygons& outline, coord_t line_width, coord_t spacing, float angle); 42 | std::vector generateLines(const Polygons& outline, coord_t line_width, coord_t spacing); 43 | /*! 44 | * 45 | * \param ordered Whether to print all index 0 polylines before any index 1 polylines etc. 46 | */ 47 | void print(const std::vector> polygons_per_index, const std::vector> polylines_per_index, bool ordered = false, bool startup_and_reduction = true); 48 | void printOrdered(const std::vector>& polygons_per_index, const std::vector>& polylines_per_index, bool startup_and_reduction); 49 | void printUnordered(const std::vector>& polygons_per_index, const std::vector>& polylines_per_index, bool startup_and_reduction); 50 | 51 | void printLinesByOptimizer(const std::vector& lines); 52 | void printPolygon(Path& polygon, int start_idx); 53 | void printPolyline(Path& poly, int start_idx); 54 | void reduce(Path& polyline, size_t start_point_idx, coord_t initial_width, coord_t traveled_dist); 55 | void setTranslation(Point p); 56 | void setNominalSpeed(float print_speed); 57 | void setFlowModifier(float flow_ratio) { extrusion_multiplier = flow_ratio; } 58 | void retract(); 59 | void move(Point p); 60 | void print(ExtrusionJunction from, ExtrusionJunction to); 61 | void extrude(float amount); 62 | 63 | void switchExtruder(int extruder_nr); 64 | 65 | void setBackPressureCompensation(float bpc); 66 | void setTemp(int temp); 67 | template 68 | void comment(std::string format, Args... args) 69 | { 70 | char buffer[80]; // max line limit in firmware 71 | sprintf(buffer, format.c_str(), args...); 72 | file << ";" << buffer << "\n"; 73 | } 74 | TimeEstimateCalculator marlin_estimates; 75 | Duration total_naive_print_time = 0.0; 76 | private: 77 | void printSingleExtrusionMove(ExtrusionJunction& from, ExtrusionJunction& to); 78 | std::ofstream file; 79 | int type; 80 | Point build_plate_middle; 81 | float filament_diameter = 2.85; 82 | coord_t discretization_size = MM2INT(0.2); 83 | coord_t nozzle_size = MM2INT(0.4); 84 | float retraction_distance = 6.5; 85 | Point extruder_offset[2] = {Point(0,0), Point(0,0)}; // set in constructor 86 | float temp = 0; // set in constructor 87 | 88 | 89 | coord_t layer_thickness; 90 | float print_speed; 91 | float travel_speed; 92 | float extrusion_multiplier; 93 | bool equalize_flow; 94 | float flow; 95 | 96 | float back_pressure_compensation; 97 | 98 | Point translation; 99 | 100 | int current_extruder; 101 | Point cur_pos; 102 | coord_t cur_z; 103 | bool is_retracted; 104 | float last_E = 0; 105 | 106 | float getExtrusionFilamentMmPerMmMove(coord_t width) const; 107 | float getExtrusionFilamentMmPerCubicMm() const; 108 | }; 109 | 110 | 111 | 112 | 113 | } // namespace visualizer 114 | #endif // GCODER_H 115 | -------------------------------------------------------------------------------- /src/OrderOptimizer.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | //CuraEngine is released under the terms of the AGPLv3 or higher. 3 | 4 | #include 5 | #include "OrderOptimizer.h" 6 | #include "utils/logoutput.h" 7 | #include "utils/SparsePointGridInclusive.h" 8 | #include "utils/linearAlg2D.h" 9 | 10 | #define INLINE static inline 11 | 12 | namespace visualizer { 13 | 14 | /** 15 | * 16 | */ 17 | void OrderOptimizer::optimize() 18 | { 19 | // NOTE: Keep this vector fixed-size, it replaces an (non-standard, sized at runtime) array: 20 | std::vector picked(polys.size(), false); 21 | loc_to_line = nullptr; 22 | 23 | for (unsigned poly_idx = 0; poly_idx < polys.size(); ++poly_idx) /// find closest point to initial starting point within each poly +initialize picked 24 | { 25 | const ConstPolygonRef poly = *polys[poly_idx].poly; 26 | switch (config.type) 27 | { 28 | case EZSeamType::USER_SPECIFIED: 29 | polyStart.push_back(getClosestPointInPoly(config.pos, poly_idx)); 30 | break; 31 | case EZSeamType::RANDOM: 32 | polyStart.push_back(getRandomPointInPoly(poly_idx)); 33 | break; 34 | case EZSeamType::SHARPEST_CORNER: 35 | case EZSeamType::SHORTEST: 36 | default: 37 | polyStart.push_back(getClosestPointInPoly(startPoint, poly_idx)); 38 | break; 39 | } 40 | // assert(poly.size() != 2); 41 | } 42 | 43 | 44 | Point prev_point; 45 | switch (config.type) 46 | { 47 | case EZSeamType::USER_SPECIFIED: 48 | prev_point = config.pos; 49 | break; 50 | case EZSeamType::RANDOM: //TODO: Starting position of the first poly isn't random. 51 | case EZSeamType::SHARPEST_CORNER: 52 | case EZSeamType::SHORTEST: 53 | default: 54 | prev_point = startPoint; 55 | } 56 | for (unsigned int poly_order_idx = 0; poly_order_idx < polys.size(); poly_order_idx++) /// actual path order optimizer 57 | { 58 | int best_poly_idx = -1; 59 | float bestDist2 = std::numeric_limits::infinity(); 60 | 61 | 62 | for (unsigned int poly_idx = 0; poly_idx < polys.size(); poly_idx++) 63 | { 64 | if (picked[poly_idx] || polys[poly_idx].poly->size() < 1) /// skip single-point-polys 65 | { 66 | continue; 67 | } 68 | 69 | // assert (polys[poly_idx]->size() != 2); 70 | 71 | size_t start_idx = getClosestPointInPoly(prev_point, poly_idx); 72 | polyStart[poly_idx] = start_idx; 73 | const Point& p = (*polys[poly_idx].poly)[start_idx]; 74 | float dist2 = vSize2f(p - prev_point); 75 | if (dist2 < bestDist2) 76 | { 77 | best_poly_idx = poly_idx; 78 | bestDist2 = dist2; 79 | } 80 | 81 | } 82 | 83 | 84 | if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next poly 85 | { 86 | // assert(polys[best_poly_idx]->size() != 2); 87 | if (polys[best_poly_idx].is_closed) 88 | { 89 | prev_point = (*polys[best_poly_idx].poly)[polyStart[best_poly_idx]]; 90 | } 91 | else 92 | { 93 | if (polyStart[best_poly_idx] == 0) 94 | prev_point = polys[best_poly_idx].poly->back(); 95 | else 96 | prev_point = polys[best_poly_idx].poly->front(); 97 | } 98 | 99 | picked[best_poly_idx] = true; 100 | polyOrder.push_back(best_poly_idx); 101 | } 102 | else 103 | { 104 | logError("Failed to find next closest poly.\n"); 105 | } 106 | } 107 | 108 | if (loc_to_line != nullptr) 109 | { 110 | delete loc_to_line; 111 | } 112 | } 113 | 114 | int OrderOptimizer::getClosestPointInPoly(Point prev_point, int poly_idx) 115 | { 116 | ConstPolygonRef poly = *polys[poly_idx].poly; 117 | if (polys[poly_idx].is_closed) 118 | { 119 | int best_point_idx = -1; 120 | float best_point_score = std::numeric_limits::infinity(); 121 | Point p0 = poly.back(); 122 | for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) 123 | { 124 | const Point& p1 = poly[point_idx]; 125 | const Point& p2 = poly[(point_idx + 1) % poly.size()]; 126 | // when type is SHARPEST_CORNER, actual distance is ignored, we use a fixed distance and decision is based on curvature only 127 | float dist_score = (config.type == EZSeamType::SHARPEST_CORNER && config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE)? 10000 : vSize2(p1 - prev_point); 128 | const float corner_angle = LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI; // 0 -> 2 129 | float corner_shift; 130 | if (config.type == EZSeamType::SHORTEST) 131 | { 132 | // the more a corner satisfies our criteria, the closer it appears to be 133 | // shift 10mm for a very acute corner 134 | corner_shift = 10000 * 10000; 135 | } 136 | else 137 | { 138 | // the larger the distance from prev_point to p1, the more a corner will "attract" the seam 139 | // so the user has some control over where the seam will lie. 140 | 141 | // the divisor here may need adjusting to obtain the best results (TBD) 142 | corner_shift = dist_score / 10; 143 | } 144 | switch (config.corner_pref) 145 | { 146 | case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER: 147 | if (corner_angle > 1) 148 | { 149 | // p1 lies on a concave curve so reduce the distance to favour it 150 | // the more concave the curve, the more we reduce the distance 151 | dist_score -= (corner_angle - 1) * corner_shift; 152 | } 153 | break; 154 | case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER: 155 | if (corner_angle < 1) 156 | { 157 | // p1 lies on a convex curve so reduce the distance to favour it 158 | // the more convex the curve, the more we reduce the distance 159 | dist_score -= (1 - corner_angle) * corner_shift; 160 | } 161 | break; 162 | case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY: 163 | // the more curved the region, the more we reduce the distance 164 | dist_score -= fabs(corner_angle - 1) * corner_shift; 165 | break; 166 | case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_WEIGHTED: 167 | //More curve is better score (reduced distance), but slightly in favour of concave curves. 168 | dist_score -= fabs(corner_angle - 0.8) * corner_shift; 169 | break; 170 | case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE: 171 | default: 172 | // do nothing 173 | break; 174 | } 175 | if (dist_score < best_point_score) 176 | { 177 | best_point_idx = point_idx; 178 | best_point_score = dist_score; 179 | } 180 | p0 = p1; 181 | } 182 | return best_point_idx; 183 | } 184 | else 185 | { 186 | Point start = poly.front(); 187 | Point end = poly.back(); 188 | if (vSize2(start - prev_point) < vSize2(end - prev_point)) 189 | { 190 | return 0; 191 | } 192 | else 193 | { 194 | return poly.size() - 1; 195 | } 196 | } 197 | } 198 | 199 | int OrderOptimizer::getRandomPointInPoly(int poly_idx) 200 | { 201 | ConstPolygonRef poly = *polys[poly_idx].poly; 202 | 203 | if (polys[poly_idx].is_closed) 204 | { 205 | return rand() % poly.size(); 206 | } 207 | else 208 | { 209 | int pos = rand() % 2; 210 | if (pos) 211 | { 212 | return poly.size() - 1; 213 | } 214 | else 215 | { 216 | return 0; 217 | } 218 | } 219 | } 220 | 221 | static inline bool pointsAreCoincident(const Point& a, const Point& b) 222 | { 223 | return vSize2(a - b) < 25; // points are closer than 5uM, consider them coincident 224 | } 225 | 226 | 227 | }//namespace cura 228 | -------------------------------------------------------------------------------- /src/OrderOptimizer.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | //CuraEngine is released under the terms of the AGPLv3 or higher. 3 | 4 | #ifndef ORDER_OPTIMIZER_H 5 | #define ORDER_OPTIMIZER_H 6 | 7 | #include 8 | #include "pathOrderOptimizer.h" // for ZSeamConfig 9 | #include "utils/polygon.h" 10 | #include "utils/polygonUtils.h" 11 | #include "utils/types/EnumSettings.h" 12 | 13 | namespace visualizer { 14 | 15 | /*! 16 | * Parts order optimization class. 17 | * 18 | * Utility class for optimizing the path order by minimizing the distance traveled between printing different parts in the layer. 19 | * The order of polygons is optimized and the startingpoint within each polygon is chosen. 20 | */ 21 | class OrderOptimizer 22 | { 23 | struct Path 24 | { 25 | ConstPolygonPointer poly; 26 | bool is_closed; 27 | Path(ConstPolygonPointer poly, bool is_closed) 28 | : poly(poly) 29 | , is_closed(is_closed) 30 | {} 31 | }; 32 | std::vector polys; //!< the parts of the layer (in arbitrary order) 33 | public: 34 | Point startPoint; //!< A location near the prefered start location 35 | const ZSeamConfig config; 36 | std::vector polyStart; //!< polys[i][polyStart[i]] = point of poly i which is to be the starting point in printing the polygon 37 | std::vector polyOrder; //!< the optimized order as indices in #polys 38 | LocToLineGrid* loc_to_line; 39 | 40 | OrderOptimizer(Point startPoint, const ZSeamConfig config = ZSeamConfig()) 41 | : startPoint(startPoint) 42 | , config(config) 43 | { 44 | } 45 | 46 | void addPoly(PolygonRef poly, bool is_closed) 47 | { 48 | polys.emplace_back(poly, is_closed); 49 | } 50 | 51 | void addPoly(ConstPolygonRef poly, bool is_closed) 52 | { 53 | polys.emplace_back(poly, is_closed); 54 | } 55 | 56 | void addPolys(const Polygons& polys, bool is_closed) 57 | { 58 | for(unsigned int i = 0; i < polys.size(); i++) 59 | { 60 | this->polys.emplace_back(polys[i], is_closed); 61 | } 62 | } 63 | 64 | void optimize(); //!< sets #polyStart and #polyOrder 65 | 66 | private: 67 | int getClosestPointInPoly(Point prev, int i_poly); //!< returns the index of the closest point 68 | int getRandomPointInPoly(int poly_idx); 69 | }; 70 | 71 | }//namespace cura 72 | 73 | #endif//ORDER_OPTIMIZER_H 74 | -------------------------------------------------------------------------------- /src/PathReader.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #include "PathReader.h" 5 | 6 | #include 7 | 8 | #include "utils/linearAlg2D.h" 9 | 10 | namespace visualizer 11 | { 12 | 13 | extern bool process_even_toolpaths_only; 14 | extern bool simplify_input_toolpaths; 15 | extern coord_t simplify_min_length_3D; 16 | 17 | int PathReader::read(std::vector> & result_polygons_per_index, std::vector> & result_polylines_per_index) 18 | { 19 | std::string line; 20 | 21 | int size, inset_nr, last_inset_nr; 22 | double x, y, r, dx, dy; 23 | 24 | 25 | std::vector>> lines_per_index; 26 | std::vector>> polys_per_index; 27 | std::list * last_polyline = nullptr; 28 | 29 | while (getline(file, line)) 30 | { 31 | inset_nr = 0; 32 | if (std::sscanf(line.c_str(), "closed %d %d", &size, &inset_nr) >= 1) 33 | { 34 | last_inset_nr = inset_nr; 35 | if (!process_even_toolpaths_only || last_inset_nr % 2 == 0) 36 | { 37 | if (inset_nr >= polys_per_index.size()) { 38 | polys_per_index.resize(inset_nr + 1); 39 | } 40 | polys_per_index[inset_nr].emplace_back(); 41 | last_polyline = & polys_per_index[inset_nr].back(); 42 | } 43 | } 44 | else if (std::sscanf(line.c_str(), "open %d %d", &size, &inset_nr) >= 1) 45 | { 46 | last_inset_nr = inset_nr; 47 | if (!process_even_toolpaths_only || last_inset_nr % 2 == 0) 48 | { 49 | if (inset_nr >= lines_per_index.size()) { 50 | lines_per_index.resize(inset_nr + 1); 51 | } 52 | lines_per_index[inset_nr].emplace_back(); 53 | last_polyline = & lines_per_index[inset_nr].back(); 54 | } 55 | } 56 | else if (std::sscanf(line.c_str(), "%lf %lf %lf %lf %lf", &x, &y, &r, &dx, &dy) >= 2) 57 | { 58 | assert(last_polyline); 59 | if (!process_even_toolpaths_only || last_inset_nr % 2 == 0) 60 | { 61 | last_polyline->emplace_back(Point(MM2INT(x), MM2INT(y)), MM2INT(2.0 * r)); 62 | } 63 | } 64 | else 65 | { 66 | std::cerr << "Couldn't parse '" << line << "'...\n"; 67 | } 68 | } 69 | 70 | result_polygons_per_index.resize(polys_per_index.size()); 71 | result_polylines_per_index.resize(lines_per_index.size()); 72 | for (bool closed : {true, false}) 73 | { 74 | std::vector>> & in_per_index = closed? polys_per_index : lines_per_index; 75 | std::vector> & results_per_index = closed? result_polygons_per_index : result_polylines_per_index; 76 | for ( size_t index = 0; index < in_per_index.size(); index++ ) 77 | { 78 | for ( std::list & line : in_per_index[index] ) 79 | { 80 | if (simplify_input_toolpaths) 81 | { 82 | simplify(line, closed); 83 | } 84 | 85 | results_per_index[index].emplace_back(-1); 86 | for (ExtrusionJunction j : line) 87 | results_per_index[index].back().junctions.emplace_back(j); 88 | } 89 | } 90 | } 91 | 92 | for (auto results : {result_polygons_per_index, result_polylines_per_index}) 93 | { 94 | for (auto result_polygons : results) 95 | { 96 | std::remove_if(result_polygons.begin(), result_polygons.end(), [](const ExtrusionLine& line) { return line.junctions.empty(); } ); 97 | } 98 | } 99 | 100 | return 0; 101 | } 102 | 103 | void PathReader::simplify(std::list& line, bool closed) const 104 | { 105 | using it = std::list::iterator; 106 | it prev = --(--line.end()); 107 | it here = --line.end(); 108 | it next_start = line.begin(); 109 | if ( ! closed) 110 | { 111 | prev = line.begin(); 112 | here = ++line.begin(); 113 | next_start = ++(++line.begin()); 114 | } 115 | for ( it next = next_start; next != line.end(); ++next) 116 | { 117 | if (shouldRemove(*prev, *here, *next)) 118 | { 119 | line.erase(here); 120 | here = next; 121 | } 122 | else 123 | { 124 | prev = here; 125 | here = next; 126 | } 127 | } 128 | 129 | // TODO: how to deal with the first vertex again? 130 | // It seems like this algorithm already automatically takes care of it... 131 | } 132 | 133 | bool PathReader::shouldRemove(ExtrusionJunction & prev, ExtrusionJunction & here, ExtrusionJunction & next) const 134 | { 135 | if (vSize2(here.p - prev.p) < 20 * 20 ) return true; 136 | if ((next.toPoint3() - prev.toPoint3()).vSize2() > simplify_min_length_3D * simplify_min_length_3D ) return false; 137 | if (vSize2(here.p - prev.p) + vSize2(next.p - here.p) < 20 * 20 ) return true; 138 | if (LinearAlg2D::getDist2FromLineSegment(prev.p, here.p, next.p) > max_deviation * max_deviation) return false; 139 | Point a = prev.p; 140 | Point b = here.p; 141 | Point c = next.p; 142 | if (a == b || b == c) return true; 143 | Point ba = a - b; 144 | Point bc = c - b; 145 | double cos_angle = INT2MM2(dot(ba, bc)) / vSizeMM(ba) / vSizeMM(bc); 146 | assert(std::isfinite(cos_angle)); 147 | if (cos_angle > cos(max_reduce_angle)) return false; 148 | return true; 149 | } 150 | 151 | } // namespace visualizer 152 | -------------------------------------------------------------------------------- /src/PathReader.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef PATH_READER_H 5 | #define PATH_READER_H 6 | 7 | 8 | #include 9 | #include 10 | 11 | #include "utils/ExtrusionLine.h" 12 | 13 | namespace visualizer 14 | { 15 | 16 | /*! 17 | * Read variable width paths 18 | */ 19 | class PathReader 20 | { 21 | public: 22 | PathReader() 23 | {} 24 | 25 | int open(std::string filename) { 26 | try 27 | { 28 | file.open(filename); 29 | } 30 | catch (const std::exception& e) 31 | { 32 | std::printf("Couldn't open file '%s' for reading svg.\n", filename.c_str()); 33 | std::exit(-1); 34 | return -1; 35 | } 36 | return 0; 37 | } 38 | /*! 39 | * \return error code 40 | */ 41 | int read(std::vector> & result_polygons_per_index, std::vector> & result_polylines_per_index); 42 | protected: 43 | std::ifstream file; 44 | static constexpr coord_t max_deviation = MM2INT(0.025); 45 | static constexpr float max_reduce_angle = 130.0/180.0 * M_PI; 46 | 47 | void simplify(std::list& line, bool closed) const; 48 | bool shouldRemove(ExtrusionJunction & before, ExtrusionJunction & here, ExtrusionJunction & next) const; 49 | }; 50 | 51 | 52 | 53 | 54 | } // namespace visualizer 55 | #endif // PATH_READER_H 56 | -------------------------------------------------------------------------------- /src/Statistics.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef STATISTICS_H 5 | #define STATISTICS_H 6 | 7 | #include "utils/polygon.h" 8 | #include "utils/ExtrusionJunction.h" 9 | #include "utils/ExtrusionSegment.h" 10 | #include "utils/ExtrusionLine.h" 11 | namespace visualizer 12 | { 13 | 14 | /*! 15 | * Get statistics of the resulting toolpaths 16 | */ 17 | class Statistics 18 | { 19 | public: 20 | Statistics(std::string test_type, std::string output_prefix, Polygons& input, double print_time) 21 | : print_time(print_time) 22 | , test_type(test_type) 23 | , output_prefix(output_prefix) 24 | , input(input) 25 | , angle_bins(int(180.0 / angle_bin_size) + 1) 26 | , width_bins(int(MM2INT(1.0) / width_bin_size)) 27 | { 28 | input = input.processEvenOdd(); 29 | total_target_area = INT2MM2(input.area()); 30 | total_target_area_length = INT2MM(input.polygonLength()); 31 | } 32 | void analyse(std::vector>& polygons_per_index, std::vector>& polylines_per_index, bool perform_overfill_underfill_analysis); 33 | void visualize(coord_t min_nozzle_size, coord_t max_nozzle_size, bool output_toolpaths = false, bool output_widths = true, bool include_legend = false, bool output_accuracy = true, bool exaggerate_widths = false, bool rounded_visualization = true); 34 | void saveResultsCSV(); 35 | void saveWidthsCSV(); 36 | void saveAnglesCSV(); 37 | void saveSegmentsCSV(); 38 | double print_time = -1; 39 | double overfill_area = -1; 40 | double double_overfill_area = -1; 41 | double total_underfill_area = -1; 42 | double outer_underfill_area = -1; 43 | double total_target_area = -1; 44 | double total_target_area_length = -1; 45 | double total_toolpath_length = -1; 46 | int closed_toolpaths = -1; 47 | int open_toolpaths = -1; 48 | private: 49 | struct Segment 50 | { 51 | ExtrusionSegment s; 52 | bool is_full; 53 | Segment(ExtrusionSegment s, bool is_full) 54 | : s(s) 55 | , is_full(is_full) 56 | {} 57 | Polygons toPolygons() 58 | { 59 | return s.toPolygons(!is_full); 60 | } 61 | }; 62 | std::string test_type; 63 | std::string output_prefix; 64 | const Polygons& input; 65 | 66 | std::vector>* polygons_per_index; 67 | std::vector>* polylines_per_index; 68 | std::vector all_segments; 69 | Polygons area_covered; 70 | Polygons overlaps; 71 | Polygons underfills; 72 | Polygons outer_underfills; 73 | Polygons overfills; 74 | Polygons double_overfills; 75 | Polygons paths; 76 | float angle_bin_size = 1.0; 77 | std::vector angle_bins; 78 | coord_t width_bin_size = 10; 79 | std::vector width_bins; 80 | 81 | void generateAllSegments(std::vector>& polygons_per_index, std::vector>& polylines_per_index); 82 | 83 | std::vector discretize(const Segment& ss, coord_t step_size); 84 | }; 85 | 86 | 87 | 88 | 89 | } // namespace visualizer 90 | #endif // STATISTICS_H 91 | -------------------------------------------------------------------------------- /src/TestGeometry/Microstructure.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef TEST_MICROSTRUCTURE_H 5 | #define TEST_MICROSTRUCTURE_H 6 | 7 | #include 8 | 9 | #include "../utils/IntPoint.h" 10 | #include "../utils/logoutput.h" 11 | #include "../utils/polygon.h" 12 | #include "../utils/ExtrusionSegment.h" 13 | 14 | namespace visualizer 15 | { 16 | 17 | class Microstructure 18 | { 19 | public: 20 | Microstructure() 21 | {} 22 | Polygons squareGrid(Point grid_size, Point cell_size) 23 | { 24 | Polygons ret; 25 | for (int x = 0; x < grid_size.X; x++) 26 | for (int y = 0; y < grid_size.Y; y++) 27 | { 28 | Point min(cell_size.X * x, cell_size.Y * y); 29 | Point max = min + cell_size; 30 | if (y < grid_size.Y - 1) ret.add(generateSegment(min, Point(min.X, max.Y))); 31 | if (x < grid_size.X - 1) ret.add(generateSegment(min, Point(max.X, min.Y))); 32 | } 33 | ret = ret.execute(ClipperLib::pftPositive); 34 | return ret; 35 | } 36 | Polygons hexGrid(Point grid_size, coord_t cell_size) 37 | { 38 | coord_t r = cell_size / 2; 39 | Polygons ret; 40 | coord_t x_skip = sin(M_PI / 3) * r * 2; 41 | coord_t y_skip = r + cos(M_PI / 3) * r; 42 | for (int x = 0; x < grid_size.X; x++) 43 | { 44 | for (int y = 0; y < grid_size.Y; y++) 45 | { 46 | coord_t extra_x = (y % 2) * x_skip / 2; 47 | Point mid(x_skip * x + extra_x, y_skip * y); 48 | for (float a = 0; a < 3; a++) 49 | { 50 | if (a == 0 && x == grid_size.X - 1) continue; 51 | if (a == 1 && x == 0) continue; 52 | if (a == 2 && y == 0) continue; 53 | Point to = mid + Point(cos(2*M_PI * (a + .25) / 3) * r, sin(2*M_PI * (a + .25) / 3) * r); 54 | ret.add(generateSegment(mid, to)); 55 | } 56 | } 57 | } 58 | ret = ret.execute(ClipperLib::pftPositive); 59 | return ret; 60 | } 61 | private: 62 | coord_t step_size = MM2INT(0.2); 63 | Polygons generateSegment(Point from, Point to) 64 | { 65 | Polygons ret; 66 | Point vec = to - from; 67 | coord_t length = vSize(vec); 68 | coord_t step_count = std::max(coord_t(0), (length + step_size / 2) / step_size); 69 | ExtrusionJunction prev(from, getW(from), 0); 70 | for (coord_t step = 0; step < step_count; step++) 71 | { 72 | Point p = from + vec * (step + 1) / step_count; 73 | ExtrusionJunction here(p, getW(p), 0); 74 | ExtrusionSegment s(prev, here, true); 75 | Polygons area = s.toPolygons(false); 76 | ret.add(area); 77 | prev = here; 78 | } 79 | return ret; 80 | } 81 | coord_t getW(Point p) 82 | { 83 | Point diff = p - Point(5000, 2000); 84 | diff.X = diff.X / 2 + diff.Y / 4; 85 | diff = diff / 2; 86 | float d = INT2MM(vSize(diff) / 3) / 4; 87 | return std::max(350.0, 2020 * 1.0 / (1.0 + d * d)); 88 | } 89 | }; 90 | 91 | } // namespace visualizer 92 | #endif // TEST_MICROSTRUCTURE_H 93 | -------------------------------------------------------------------------------- /src/TestGeometry/Moessen.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef TEST_GEOMETRY_MOESSEN_H 5 | #define TEST_GEOMETRY_MOESSEN_H 6 | 7 | #include 8 | 9 | #include "../utils/IntPoint.h" 10 | #include "../utils/logoutput.h" 11 | #include "../utils/polygon.h" 12 | 13 | namespace visualizer 14 | { 15 | 16 | class MoessenTests 17 | { 18 | public: 19 | static Polygons generateCircles(Point grid_size, coord_t min_r, coord_t max_r, coord_t skip_r, coord_t angle_count = 50) 20 | { 21 | Polygons ret; 22 | PolygonRef square = ret.newPoly(); 23 | square.emplace_back(0, 0); 24 | square.emplace_back(0, grid_size.Y * skip_r * 2); 25 | square.emplace_back(grid_size * skip_r * 2); 26 | square.emplace_back(grid_size.X * skip_r * 2, 0); 27 | 28 | for (int x = 0; x < grid_size.X; x++) 29 | for (int y = 0; y < grid_size.Y; y++) 30 | { 31 | PolygonRef circle = ret.newPoly(); 32 | coord_t r = min_r + rand() % (max_r - min_r); 33 | for (coord_t v = 0; v < angle_count; v++) 34 | { 35 | float a = 2.0 * M_PI / angle_count * v; 36 | circle.emplace_back(Point(x * 2 + 1, y * 2 + 1) * skip_r + Point(r * cos(a), r * sin(a))); 37 | } 38 | } 39 | return ret; 40 | } 41 | 42 | static Polygons generateTriangles(Point grid_size, coord_t min_r, coord_t max_r, coord_t skip_r) 43 | { 44 | Polygons ret; 45 | PolygonRef square = ret.newPoly(); 46 | coord_t side_length = skip_r * 2 * sqrt(3.0); 47 | coord_t height = skip_r * 3; 48 | square.emplace_back(0, 0); 49 | square.emplace_back(grid_size.Y * side_length / 2, grid_size.Y * height); 50 | square.emplace_back(grid_size.Y * side_length / 2 + grid_size.X * side_length / 2, grid_size.Y * height); 51 | square.emplace_back(grid_size.X * side_length / 2, 0); 52 | 53 | 54 | for (int x = 0; x < grid_size.X; x++) 55 | for (int y = 0; y < grid_size.Y; y++) 56 | { 57 | PolygonRef circle = ret.newPoly(); 58 | coord_t r = (max_r == min_r)? min_r : min_r + rand() % (max_r - min_r); 59 | r *= 2; 60 | for (coord_t v = 0; v < 3; v++) 61 | { 62 | float a = 2.0 * M_PI / 3 * v + M_PI / 2; 63 | Point mid = Point(x * side_length / 2 + side_length * y / 2 + side_length / 2, y * height + skip_r); 64 | if (x % 2 == 1) 65 | { 66 | mid += Point(0, skip_r); 67 | a += M_PI; 68 | } 69 | circle.emplace_back(mid + Point(r * cos(a), r * sin(a))); 70 | } 71 | } 72 | return ret; 73 | } 74 | }; 75 | 76 | } // namespace visualizer 77 | #endif // TEST_GEOMETRY_MOESSEN_H 78 | -------------------------------------------------------------------------------- /src/TestGeometry/Prescribed.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef TEST_GEOMETRY_PRESCRIBED_H 5 | #define TEST_GEOMETRY_PRESCRIBED_H 6 | 7 | #include 8 | 9 | #include "../utils/IntPoint.h" 10 | #include "../utils/logoutput.h" 11 | #include "../utils/polygonUtils.h" 12 | #include "../utils/ExtrusionSegment.h" 13 | #include "../utils/polygon.h" 14 | 15 | namespace visualizer 16 | { 17 | 18 | class Prescribed 19 | { 20 | public: 21 | static Polygons fromDistances(const std::vector& height_data) 22 | { 23 | coord_t y = 0; 24 | Polygons ret; 25 | 26 | if (height_data.size() < 1) return ret; 27 | 28 | ExtrusionJunction prev(Point(height_data.front().X, y), height_data.front().Y, 0); 29 | for (coord_t datum_idx = 1; datum_idx < height_data.size(); datum_idx++) 30 | { 31 | Point datum = height_data[datum_idx]; 32 | ExtrusionJunction junction(Point(datum.X, y), datum.Y, 0); 33 | ExtrusionSegment segment(prev, junction, true); 34 | ret = ret.unionPolygons(segment.toPolygons(false)); 35 | prev = junction; 36 | } 37 | return ret; 38 | }; 39 | }; 40 | 41 | } // namespace visualizer 42 | #endif // TEST_GEOMETRY_PRESCRIBED_H 43 | -------------------------------------------------------------------------------- /src/TestGeometry/SVGloader.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef TEST_GEOMETRY_SVG_LOADER_H 5 | #define TEST_GEOMETRY_SVG_LOADER_H 6 | 7 | #include 8 | #include 9 | 10 | #include "utils/polygon.h" 11 | #include "utils/floatpoint.h" 12 | #include "utils/logoutput.h" 13 | 14 | namespace visualizer 15 | { 16 | 17 | class SVGloader 18 | { 19 | public: 20 | static Polygons load(std::string filename) 21 | { 22 | 23 | Polygons ret; 24 | 25 | std::ifstream file; 26 | try 27 | { 28 | file.open(filename); 29 | } 30 | catch (const std::exception& e) 31 | { 32 | logError("Couldn't open file '%s' for reading svg.\n", filename.c_str()); 33 | std::cerr << std::flush; 34 | std::exit(-1); 35 | return ret; 36 | } 37 | if (!file.is_open()) 38 | { 39 | logError("Couldn't open file '%s' for reading svg.\n", filename.c_str()); 40 | std::exit(-1); 41 | return ret; 42 | } 43 | std::regex poly_regex("<(polygon)|(path) .*/>"); 44 | 45 | std::regex points_regex(" points=\"([^\"]*)\""); 46 | std::smatch points_sm; 47 | 48 | std::regex d_regex("d=\"([^\"]*)\""); 49 | std::smatch d_sm; 50 | 51 | std::regex point_regex("([^ ]+)"); 52 | std::smatch point_sm; 53 | 54 | std::string line; 55 | while (getline(file, line)) 56 | { 57 | if (std::regex_search(line, points_sm, points_regex)) 58 | { 59 | if (points_sm.size() < 2) 60 | { 61 | assert(false); 62 | continue; // didn't contain points 63 | } 64 | std::string points_str = points_sm[1].str(); 65 | 66 | std::transform(points_str.begin(), points_str.end(), points_str.begin(), 67 | [](unsigned char c){ return std::tolower(c); }); 68 | 69 | PolygonRef poly = ret.newPoly(); 70 | PolygonPointer poly_p(poly); 71 | bool additive = false; 72 | bool painting = true; 73 | bool is_vertical = false; 74 | bool is_horizontal = false; 75 | Point current(0,0); 76 | while (std::regex_search (points_str, point_sm, point_regex)) 77 | { 78 | float x, y; 79 | if (point_sm.size() <= 0) continue; 80 | std::string point_str = point_sm[0].str(); 81 | if (point_str.compare("") == 0) break; 82 | if (std::sscanf(point_str.c_str(), "%f,%f", &x, &y) == 2) 83 | { 84 | Point here(MM2INT(x), MM2INT(y)); 85 | current = additive? current + here : here; 86 | poly_p->emplace_back(current); 87 | } 88 | else 89 | { 90 | std::cerr << "Couldn't parse '" << point_str << "'\n"; 91 | } 92 | points_str = point_sm.suffix().str(); 93 | } 94 | } 95 | } 96 | return ret; 97 | } 98 | }; 99 | 100 | } // namespace visualizer 101 | #endif // TEST_GEOMETRY_SVG_LOADER_H 102 | -------------------------------------------------------------------------------- /src/TestGeometry/Spiky.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef TEST_GEOMETRY_SPIKY_H 5 | #define TEST_GEOMETRY_SPIKY_H 6 | 7 | #include 8 | 9 | #include "../utils/IntPoint.h" 10 | #include "../utils/logoutput.h" 11 | #include "../utils/polygonUtils.h" 12 | #include "../utils/ExtrusionSegment.h" 13 | #include "../utils/polygon.h" 14 | 15 | namespace visualizer 16 | { 17 | 18 | class Spiky 19 | { 20 | public: 21 | static Polygons oneSpike(coord_t gap = 300, coord_t size = 3000) 22 | { 23 | coord_t hole = 10; 24 | Polygons ret; 25 | PolygonRef poly = ret.newPoly(); 26 | poly.emplace_back(0,0); 27 | poly.emplace_back(size,0); 28 | poly.emplace_back(size,size); 29 | poly.emplace_back(size / 2 + hole,size); 30 | poly.emplace_back(size / 2, gap); 31 | poly.emplace_back(size / 2 - hole,size); 32 | poly.emplace_back(0,size); 33 | return ret; 34 | } 35 | static Polygons oneSpikeOneCorner(coord_t gap = 300, coord_t size = 3000, coord_t hole = 100) 36 | { 37 | Polygons ret; 38 | PolygonRef poly = ret.newPoly(); 39 | poly.emplace_back(size / 2 - gap,size / 4); 40 | poly.emplace_back(size,size / 4); 41 | poly.emplace_back(size,size - hole); 42 | poly.emplace_back(size / 2 + gap * .5 * sqrt(2.0), size / 2 + gap * .5 * sqrt(2.0)); 43 | poly.emplace_back(size,size); 44 | poly.emplace_back(0,size); 45 | poly.emplace_back(size / 2,size / 2); 46 | return ret; 47 | } 48 | static Polygons twoSpikes(coord_t gap = 300, coord_t size = 3000, coord_t hole = 100) 49 | { 50 | Polygons ret; 51 | PolygonRef poly = ret.newPoly(); 52 | poly.emplace_back(0,0); 53 | poly.emplace_back(size / 2 - hole,0); 54 | poly.emplace_back(size / 2,size/2 - gap / 2); 55 | poly.emplace_back(size / 2 + hole,0); 56 | poly.emplace_back(size,0); 57 | poly.emplace_back(size,size); 58 | poly.emplace_back(size / 2 + hole,size); 59 | poly.emplace_back(size / 2,size/2 + gap / 2); 60 | poly.emplace_back(size / 2 - hole,size); 61 | poly.emplace_back(0,size); 62 | return ret; 63 | } 64 | static Polygons twoSpikesDiamond(coord_t gap = 300, coord_t size = 3000, coord_t hole = 100) 65 | { 66 | Polygons ret; 67 | PolygonRef poly = ret.newPoly(); 68 | poly.emplace_back(size / 2 - hole,0); 69 | poly.emplace_back(size / 2,size/2 - gap / 2); 70 | poly.emplace_back(size / 2 + hole,0); 71 | poly.emplace_back(size,size / 2); 72 | poly.emplace_back(size / 2 + hole,size); 73 | poly.emplace_back(size / 2,size/2 + gap / 2); 74 | poly.emplace_back(size / 2 - hole,size); 75 | poly.emplace_back(0,size / 2); 76 | return ret; 77 | } 78 | static Polygons fourSpikes(coord_t gap1 = 500, coord_t gap2 = 1200, coord_t size = 3000) 79 | { 80 | coord_t hole = 10; 81 | Polygons ret; 82 | PolygonRef poly = ret.newPoly(); 83 | poly.emplace_back(0,0); 84 | poly.emplace_back(size / 2 - hole,0); 85 | poly.emplace_back(size / 2,size/2 - gap1 / 2); 86 | poly.emplace_back(size / 2 + hole,0); 87 | poly.emplace_back(size,0); 88 | poly.emplace_back(size,size / 2 - hole); 89 | poly.emplace_back(size/2 + gap2 / 2, size / 2); 90 | poly.emplace_back(size,size / 2 + hole); 91 | poly.emplace_back(size,size); 92 | poly.emplace_back(size / 2 + hole,size); 93 | poly.emplace_back(size / 2,size/2 + gap1 / 2); 94 | poly.emplace_back(size / 2 - hole,size); 95 | poly.emplace_back(0,size); 96 | poly.emplace_back(0,size / 2 + hole); 97 | poly.emplace_back(size/2 - gap2 / 2, size / 2); 98 | poly.emplace_back(0,size / 2 - hole); 99 | return ret; 100 | } 101 | 102 | static Polygons doubleOutSpike(coord_t hole = 600, coord_t recede_dist = 50, coord_t size = 3000) 103 | { 104 | Polygons ret; 105 | PolygonRef poly = ret.newPoly(); 106 | poly.emplace_back(0,0); 107 | poly.emplace_back(size,0); 108 | poly.emplace_back(size,size); 109 | poly.emplace_back(size / 2 + hole / 2,size); 110 | poly.emplace_back(size / 2 + hole / 4, size * 2); 111 | poly.emplace_back(size / 2, size + recede_dist); 112 | poly.emplace_back(size / 2 - hole / 4, size * 2); 113 | poly.emplace_back(size / 2 - hole / 2,size); 114 | poly.emplace_back(0,size); 115 | return ret; 116 | } 117 | }; 118 | 119 | } // namespace visualizer 120 | #endif // TEST_GEOMETRY_SPIKY_H 121 | -------------------------------------------------------------------------------- /src/TestGeometry/TXTloader.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef TEST_GEOMETRY_TXT_LOADER_H 5 | #define TEST_GEOMETRY_TXT_LOADER_H 6 | 7 | #include 8 | 9 | #include "utils/polygon.h" 10 | #include "utils/floatpoint.h" 11 | #include "utils/logoutput.h" 12 | 13 | namespace visualizer 14 | { 15 | 16 | class TXTloader 17 | { 18 | public: 19 | static Polygons load(std::string filename) 20 | { 21 | 22 | Polygons ret; 23 | 24 | std::ifstream file; 25 | try 26 | { 27 | file.open(filename); 28 | } 29 | catch (const std::exception& e) 30 | { 31 | logError("Couldn't open file '%s' for reading svg.\n", filename.c_str()); 32 | std::cerr << std::flush; 33 | std::exit(-1); 34 | return ret; 35 | } 36 | if (!file.is_open()) 37 | { 38 | logError("Couldn't open file '%s' for reading svg.\n", filename.c_str()); 39 | std::exit(-1); 40 | return ret; 41 | } 42 | 43 | std::string line; 44 | 45 | double min_w, max_w; 46 | int size; 47 | double x, y; 48 | 49 | if (!getline(file, line)) std::cerr << "Premature EOF\n"; 50 | if (sscanf(line.c_str(), "%lf", &min_w) < 1) std::cerr << "Something's wrong\n"; 51 | if (!getline(file, line)) std::cerr << "Premature EOF\n"; 52 | if (sscanf(line.c_str(), "%lf", &max_w) < 1) std::cerr << "Something's wrong\n"; 53 | 54 | while (getline(file, line)) 55 | { 56 | if (std::sscanf(line.c_str(), "%lf %lf", &x, &y) == 2) 57 | { 58 | ret.back().emplace_back(MM2INT(x), MM2INT(y)); 59 | } 60 | else if (std::sscanf(line.c_str(), "%i", &size) == 1) 61 | { 62 | ret.emplace_back(); 63 | } 64 | } 65 | return ret; 66 | } 67 | }; 68 | 69 | } // namespace visualizer 70 | #endif // TEST_GEOMETRY_TXT_LOADER_H 71 | -------------------------------------------------------------------------------- /src/TestGeometry/VariableWidthGcodeTester.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef TEST_GEOMETRY_VARIABLE_WIDTH_GCODE_TESTER_H 5 | #define TEST_GEOMETRY_VARIABLE_WIDTH_GCODE_TESTER_H 6 | 7 | #include 8 | 9 | #include "../utils/ExtrusionSegment.h" 10 | 11 | namespace visualizer 12 | { 13 | 14 | class VariableWidthGcodeTester 15 | { 16 | public: 17 | static std::vector> zigzag(coord_t gap = MM2INT(2.0), coord_t size = MM2INT(50)) 18 | { 19 | std::vector> result_polylines_per_index; 20 | result_polylines_per_index.resize(1); 21 | result_polylines_per_index[0].emplace_back(0); 22 | std::list& polyline = result_polylines_per_index[0].front().junctions; 23 | 24 | coord_t normal_width = 400; 25 | // std::vector widths({0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4}); 26 | std::vector widths({0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9}); 27 | 28 | polyline.emplace_back(Point(-gap, size + normal_width / 2), normal_width, 0); 29 | polyline.emplace_back(Point(widths.size() * gap, size + normal_width / 2), normal_width, 0); 30 | polyline.emplace_back(Point(widths.size() * gap , -normal_width / 2), normal_width, 0); 31 | polyline.emplace_back(Point(-gap, -normal_width / 2), normal_width, 0); 32 | bool downward = true; 33 | for (int idx = 0; idx < widths.size(); idx++) 34 | { 35 | coord_t x = idx * gap; 36 | coord_t w = MM2INT(widths[widths.size() - 1 - idx]); 37 | for (bool first : {true, false}) 38 | { 39 | coord_t y = (downward == first)? w / 2 : size - w / 2; 40 | polyline.emplace_back(Point(x, y), w, 0); 41 | } 42 | downward = !downward; 43 | } 44 | return result_polylines_per_index; 45 | } 46 | }; 47 | 48 | } // namespace visualizer 49 | #endif // TEST_GEOMETRY_VARIABLE_WIDTH_GCODE_TESTER_H 50 | -------------------------------------------------------------------------------- /src/pathOrderOptimizer.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | //CuraEngine is released under the terms of the AGPLv3 or higher. 3 | 4 | #ifndef PATHOPTIMIZER_H 5 | #define PATHOPTIMIZER_H 6 | 7 | #include 8 | #include "utils/polygon.h" 9 | #include "utils/polygonUtils.h" 10 | #include "utils/types/EnumSettings.h" 11 | 12 | namespace visualizer { 13 | 14 | /*! 15 | * Helper class that encapsulates the various criteria that define the location of the z-seam. 16 | * Instances of this are passed to the PathOrderOptimizer to specify where the z-seam is to be located. 17 | */ 18 | class ZSeamConfig 19 | { 20 | public: 21 | EZSeamType type; 22 | Point pos; //!< The position near where to create the z_seam (if \ref PathOrderOptimizer::type == 'back') 23 | EZSeamCornerPrefType corner_pref; 24 | // default constructor 25 | ZSeamConfig() 26 | : type(EZSeamType::SHORTEST) 27 | , pos(Point(0, 0)) 28 | , corner_pref(EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) 29 | { 30 | } 31 | ZSeamConfig(EZSeamType type, Point pos, EZSeamCornerPrefType corner_pref) 32 | : type(type) 33 | , pos(pos) 34 | , corner_pref(corner_pref) 35 | { 36 | } 37 | }; 38 | 39 | /*! 40 | * Parts order optimization class. 41 | * 42 | * Utility class for optimizing the path order by minimizing the distance traveled between printing different parts in the layer. 43 | * The order of polygons is optimized and the startingpoint within each polygon is chosen. 44 | */ 45 | class PathOrderOptimizer 46 | { 47 | public: 48 | Point startPoint; //!< A location near the prefered start location 49 | const ZSeamConfig config; 50 | std::vector polygons; //!< the parts of the layer (in arbitrary order) 51 | std::vector polyStart; //!< polygons[i][polyStart[i]] = point of polygon i which is to be the starting point in printing the polygon 52 | std::vector polyOrder; //!< the optimized order as indices in #polygons 53 | LocToLineGrid* loc_to_line; 54 | const Polygons* combing_boundary; 55 | 56 | PathOrderOptimizer(Point startPoint, const ZSeamConfig config = ZSeamConfig(), const Polygons* combing_boundary = nullptr) 57 | : startPoint(startPoint) 58 | , config(config) 59 | , combing_boundary((combing_boundary != nullptr && combing_boundary->size() > 0) ? combing_boundary : nullptr) 60 | { 61 | } 62 | 63 | void addPolygon(PolygonRef polygon) 64 | { 65 | polygons.emplace_back(polygon); 66 | } 67 | 68 | void addPolygon(ConstPolygonRef polygon) 69 | { 70 | polygons.emplace_back(polygon); 71 | } 72 | 73 | void addPolygons(const Polygons& polygons) 74 | { 75 | for(unsigned int i = 0; i < polygons.size(); i++) 76 | { 77 | this->polygons.emplace_back(polygons[i]); 78 | } 79 | } 80 | 81 | void optimize(); //!< sets #polyStart and #polyOrder 82 | 83 | private: 84 | int getClosestPointInPolygon(Point prev, int i_polygon); //!< returns the index of the closest point 85 | int getRandomPointInPolygon(int poly_idx); 86 | }; 87 | //! Line path order optimization class. 88 | /*! 89 | * Utility class for optimizing the path order by minimizing the distance traveled between printing different lines within a part. 90 | */ 91 | class LineOrderOptimizer 92 | { 93 | public: 94 | Point startPoint; //!< The location of the nozzle before starting to print the current layer 95 | std::vector polygons; //!< the parts of the layer (in arbitrary order) 96 | std::vector polyStart; //!< polygons[i][polyStart[i]] = point of polygon i which is to be the starting point in printing the polygon 97 | std::vector polyOrder; //!< the optimized order as indices in #polygons 98 | LocToLineGrid* loc_to_line; 99 | const Polygons* combing_boundary; //!< travel moves that cross this boundary are penalised so they are less likely to be chosen 100 | 101 | LineOrderOptimizer(Point startPoint, const Polygons* combing_boundary = nullptr) 102 | { 103 | this->startPoint = startPoint; 104 | this->combing_boundary = (combing_boundary != nullptr && combing_boundary->size() > 0) ? combing_boundary : nullptr; 105 | } 106 | 107 | void addPolygon(PolygonRef polygon) 108 | { 109 | polygons.push_back(polygon); 110 | } 111 | 112 | void addPolygon(ConstPolygonRef polygon) 113 | { 114 | polygons.push_back(polygon); 115 | } 116 | 117 | void addPolygons(Polygons& polygons) 118 | { 119 | for(unsigned int i=0;ipolygons.push_back(polygons[i]); 122 | } 123 | } 124 | 125 | /*! 126 | * Do the optimization 127 | * 128 | * \param find_chains Whether to determine when lines are chained together (i.e. zigzag infill) 129 | * 130 | * \return The squared travel distance between the two points 131 | */ 132 | void optimize(bool find_chains = true); //!< sets #polyStart and #polyOrder 133 | 134 | private: 135 | /*! 136 | * Update LineOrderOptimizer::polyStart if the current line is better than the current best. 137 | * 138 | * \param poly_idx[in] The index in LineOrderOptimizer::polygons for the current line to test 139 | * \param best[in, out] The index of current best line 140 | * \param best_score[in, out] The distance score for the current best line 141 | * \param prev_point[in] The previous point from which to find the next best line 142 | * \param just_point[in] If not -1, only look at the line vertex with this index 143 | */ 144 | void updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, int just_point = -1); 145 | 146 | /*! 147 | * Compute the squared distance from \p p0 to \p p1 using combing 148 | * 149 | * \param p0 A point 150 | * \param p1 Another point 151 | * 152 | * \return The squared travel distance between the two points 153 | */ 154 | float combingDistance2(const Point &p0, const Point &p1); 155 | }; 156 | 157 | }//namespace cura 158 | 159 | #endif//PATHOPTIMIZER_H 160 | -------------------------------------------------------------------------------- /src/timeEstimate.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | //CuraEngine is released under the terms of the AGPLv3 or higher. 3 | 4 | #ifndef TIME_ESTIMATE_H 5 | #define TIME_ESTIMATE_H 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "utils/types/Duration.h" //Print time estimates. 12 | #include "utils/types/Velocity.h" //Speeds and accelerations at which we print. 13 | #include "utils/types/Ratio.h" 14 | 15 | namespace visualizer 16 | { 17 | 18 | enum class PrintFeatureType: unsigned char 19 | { 20 | Extrusion = 0, 21 | Move = 1, 22 | NumPrintFeatureTypes = 2 // this number MUST be the last one because other modules will 23 | // use this symbol to get the total number of types, which can 24 | // be used to create an array or so 25 | }; 26 | 27 | /*! 28 | * The TimeEstimateCalculator class generates a estimate of printing time calculated with acceleration in mind. 29 | * Some of this code has been adapted from the Marlin sources. 30 | */ 31 | class TimeEstimateCalculator 32 | { 33 | public: 34 | constexpr static unsigned int NUM_AXIS = 4; 35 | constexpr static unsigned int X_AXIS = 0; 36 | constexpr static unsigned int Y_AXIS = 1; 37 | constexpr static unsigned int Z_AXIS = 2; 38 | constexpr static unsigned int E_AXIS = 3; 39 | 40 | 41 | class Position 42 | { 43 | public: 44 | Position() {for(unsigned int n=0;n blocks; 91 | public: 92 | /*! 93 | * \brief Set the movement configuration of the firmware. 94 | * \param settings_base Where to get the settings from. 95 | */ 96 | void setFirmwareDefaults(); 97 | void setPosition(Position newPos); 98 | void plan(Position newPos, Velocity feedRate, PrintFeatureType type); 99 | void addTime(const Duration& time); 100 | void setAcceleration(const Acceleration& acc); //!< Set the default acceleration to \p acc 101 | void setMaxXyJerk(const Velocity& jerk); //!< Set the max xy jerk to \p jerk 102 | 103 | void reset(); 104 | 105 | std::vector calculate(); 106 | private: 107 | void reverse_pass(); 108 | void forward_pass(); 109 | void recalculate_trapezoids(); 110 | 111 | void calculate_trapezoid_for_block(Block *block, const Ratio entry_factor, const Ratio exit_factor); 112 | void planner_reverse_pass_kernel(Block *previous, Block *current, Block *next); 113 | void planner_forward_pass_kernel(Block *previous, Block *current, Block *next); 114 | }; 115 | 116 | }//namespace visualizer 117 | #endif//TIME_ESTIMATE_H 118 | -------------------------------------------------------------------------------- /src/utils/AABB.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #include 5 | #include "AABB.h" 6 | #include "polygon.h" //To create the AABB of a polygon. 7 | 8 | namespace visualizer 9 | { 10 | 11 | 12 | AABB::AABB() 13 | : min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN) 14 | { 15 | } 16 | 17 | AABB::AABB(const Point& min, const Point& max) 18 | : min(min), max(max) 19 | { 20 | } 21 | 22 | AABB::AABB(const Polygons& polys) 23 | : min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN) 24 | { 25 | calculate(polys); 26 | } 27 | 28 | AABB::AABB(ConstPolygonRef poly) 29 | : min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN) 30 | { 31 | calculate(poly); 32 | } 33 | 34 | Point AABB::getMiddle() const 35 | { 36 | return (min + max) / 2; 37 | } 38 | 39 | void AABB::calculate(const Polygons& polys) 40 | { 41 | min = Point(POINT_MAX, POINT_MAX); 42 | max = Point(POINT_MIN, POINT_MIN); 43 | for (unsigned int i = 0; i < polys.size(); i++) 44 | { 45 | for (unsigned int j = 0; j < polys[i].size(); j++) 46 | { 47 | include(polys[i][j]); 48 | } 49 | } 50 | } 51 | 52 | void AABB::calculate(ConstPolygonRef poly) 53 | { 54 | min = Point(POINT_MAX, POINT_MAX); 55 | max = Point(POINT_MIN, POINT_MIN); 56 | for (const Point& p : poly) 57 | { 58 | include(p); 59 | } 60 | } 61 | 62 | bool AABB::contains(const Point& point) const 63 | { 64 | return point.X >= min.X && point.X <= max.X && point.Y >= min.Y && point.Y <= max.Y; 65 | } 66 | 67 | bool AABB::hit(const AABB& other) const 68 | { 69 | if (max.X < other.min.X) return false; 70 | if (min.X > other.max.X) return false; 71 | if (max.Y < other.min.Y) return false; 72 | if (min.Y > other.max.Y) return false; 73 | return true; 74 | } 75 | 76 | void AABB::include(Point point) 77 | { 78 | min.X = std::min(min.X,point.X); 79 | min.Y = std::min(min.Y,point.Y); 80 | max.X = std::max(max.X,point.X); 81 | max.Y = std::max(max.Y,point.Y); 82 | } 83 | 84 | void AABB::include(const AABB other) 85 | { 86 | include(other.min); 87 | include(other.max); 88 | } 89 | 90 | AABB AABB::expanded(int dist) 91 | { 92 | AABB ret = *this; 93 | return ret.expand(dist); 94 | } 95 | 96 | AABB& AABB::expand(int dist) 97 | { 98 | if (min == Point(POINT_MAX, POINT_MAX) || max == Point(POINT_MIN, POINT_MIN)) 99 | { 100 | return *this; 101 | } 102 | min.X -= dist; 103 | min.Y -= dist; 104 | max.X += dist; 105 | max.Y += dist; 106 | return *this; 107 | } 108 | 109 | Polygons AABB::toPolygons() const 110 | { 111 | Polygons ret; 112 | ret.add(toPolygon()); 113 | return ret; 114 | } 115 | Polygon AABB::toPolygon() const 116 | { 117 | Polygon ret; 118 | ret.add(min); 119 | ret.add(Point(max.X, min.Y)); 120 | ret.add(max); 121 | ret.add(Point(min.X, max.Y)); 122 | return ret; 123 | } 124 | 125 | }//namespace visualizer 126 | 127 | -------------------------------------------------------------------------------- /src/utils/AABB.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_AABB_H 5 | #define UTILS_AABB_H 6 | 7 | #include "IntPoint.h" 8 | 9 | namespace visualizer 10 | { 11 | 12 | class ConstPolygonRef; 13 | class Polygon; 14 | class Polygons; 15 | 16 | /* Axis aligned boundary box */ 17 | class AABB 18 | { 19 | public: 20 | Point min, max; 21 | 22 | AABB(); //!< initializes with invalid min and max 23 | AABB(const Point& min, const Point& max); //!< initializes with given min and max 24 | AABB(const Polygons& polys); //!< Computes the boundary box for the given polygons 25 | AABB(ConstPolygonRef poly); //!< Computes the boundary box for the given polygons 26 | 27 | void calculate(const Polygons& polys); //!< Calculates the aabb for the given polygons (throws away old min and max data of this aabb) 28 | void calculate(ConstPolygonRef poly); //!< Calculates the aabb for the given polygon (throws away old min and max data of this aabb) 29 | 30 | /*! 31 | * Whether the bounding box contains the specified point. 32 | * \param point The point to check whether it is inside the bounding box. 33 | * \return ``true`` if the bounding box contains the specified point, or 34 | * ``false`` otherwise. 35 | */ 36 | bool contains(const Point& point) const; 37 | 38 | /*! 39 | * Get the middle of the bounding box 40 | */ 41 | Point getMiddle() const; 42 | 43 | /*! 44 | * Check whether this aabb overlaps with another. 45 | * 46 | * In the boundary case false is returned. 47 | * 48 | * \param other the aabb to check for overlaps with 49 | * \return Whether the two aabbs overlap 50 | */ 51 | bool hit(const AABB& other) const; 52 | 53 | /*! 54 | * \brief Includes the specified point in the bounding box. 55 | * 56 | * The bounding box is expanded if the point is not within the bounding box. 57 | * 58 | * \param point The point to include in the bounding box. 59 | */ 60 | void include(Point point); 61 | 62 | /*! 63 | * \brief Includes the specified bounding box in the bounding box. 64 | * 65 | * The bounding box is expanded to include the other bounding box. 66 | * 67 | * This performs a union on two bounding boxes. 68 | * 69 | * \param other The bounding box to include in this one. 70 | */ 71 | void include(const AABB other); 72 | 73 | /*! 74 | * Expand the borders of the bounding box in each direction with the given amount 75 | * 76 | * \param dist The distance by which to expand the borders of the bounding box 77 | */ 78 | AABB& expand(int dist); 79 | 80 | AABB expanded(int dist); 81 | 82 | /*! 83 | * Generate a square polygon which coincides with this aabb 84 | * \return the polygon of this aabb 85 | */ 86 | Polygon toPolygon() const; 87 | Polygons toPolygons() const; 88 | }; 89 | 90 | }//namespace visualizer 91 | #endif//UTILS_AABB_H 92 | 93 | -------------------------------------------------------------------------------- /src/utils/AABB3D.cpp: -------------------------------------------------------------------------------- 1 | /** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */ 2 | 3 | #include "AABB3D.h" 4 | 5 | #include 6 | 7 | #include "AABB.h" 8 | 9 | namespace visualizer 10 | { 11 | 12 | AABB3D::AABB3D() 13 | : min(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()) 14 | , max(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()) 15 | { 16 | } 17 | 18 | AABB3D::AABB3D(Point3 min, Point3 max) 19 | : min(min) 20 | , max(max) 21 | { 22 | } 23 | 24 | Point3 AABB3D::getMiddle() const 25 | { 26 | return (min + max) / 2; 27 | } 28 | 29 | AABB AABB3D::flatten() const 30 | { 31 | return AABB(Point(min.x, min.y), Point(max.x, max.y)); 32 | } 33 | 34 | 35 | bool AABB3D::hit(const AABB3D& other) const 36 | { 37 | if ( max.x < other.min.x 38 | || min.x > other.max.x 39 | || max.y < other.min.y 40 | || min.y > other.max.y 41 | || max.z < other.min.z 42 | || min.z > other.max.z) 43 | { 44 | return false; 45 | } 46 | return true; 47 | } 48 | 49 | void AABB3D::include(Point3 p) 50 | { 51 | min.x = std::min(min.x, p.x); 52 | min.y = std::min(min.y, p.y); 53 | min.z = std::min(min.z, p.z); 54 | max.x = std::max(max.x, p.x); 55 | max.y = std::max(max.y, p.y); 56 | max.z = std::max(max.z, p.z); 57 | } 58 | 59 | void AABB3D::include(const AABB3D& aabb) 60 | { 61 | include(aabb.min); 62 | include(aabb.max); 63 | } 64 | 65 | void AABB3D::includeZ(coord_t z) 66 | { 67 | min.z = std::min(min.z, z); 68 | max.z = std::max(max.z, z); 69 | } 70 | 71 | void AABB3D::offset(Point3 offset) 72 | { 73 | min += offset; 74 | max += offset; 75 | } 76 | 77 | void AABB3D::offset(Point offset) 78 | { 79 | min += offset; 80 | max += offset; 81 | } 82 | 83 | void AABB3D::expandXY(int outset) 84 | { 85 | min -= Point3(outset, outset, 0); 86 | max += Point3(outset, outset, 0); 87 | if (min.x > max.x || min.y > max.y) 88 | { // make this AABB3D invalid 89 | *this = AABB3D(); 90 | } 91 | } 92 | 93 | }//namespace visualizer 94 | 95 | -------------------------------------------------------------------------------- /src/utils/AABB3D.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_AABB3D_H 5 | #define UTILS_AABB3D_H 6 | 7 | #include "IntPoint.h" 8 | 9 | namespace visualizer 10 | { 11 | 12 | class AABB; 13 | 14 | /*! 15 | An Axis Aligned Bounding Box. Has a min and max vector, representing minimal and maximal coordinates in the three axes. 16 | */ 17 | struct AABB3D 18 | { 19 | Point3 min; //!< The minimal coordinates in x, y and z direction 20 | Point3 max; //!< The maximal coordinates in x, y and z direction 21 | 22 | /*! 23 | * Create an AABB3D with coordinates at the numeric limits. 24 | */ 25 | AABB3D(); 26 | 27 | /*! 28 | * Create an AABB3D with given limits 29 | */ 30 | AABB3D(Point3 min, Point3 max); 31 | 32 | /*! 33 | * Get the middle of the bounding box 34 | */ 35 | Point3 getMiddle() const; 36 | 37 | /*! 38 | * Creates a 2D version of this bounding box by leaving away the Z 39 | * component. 40 | * 41 | * \return A 2D version of this bounding box. 42 | */ 43 | AABB flatten() const; 44 | 45 | /*! 46 | * Check whether this aabb overlaps with another. 47 | * 48 | * In the boundary case false is returned. 49 | * 50 | * \param other the aabb to check for overlaps with 51 | * \return Whether the two aabbs overlap 52 | */ 53 | bool hit(const AABB3D& other) const; 54 | 55 | /*! 56 | * Expand the AABB3D to include the point \p p. 57 | * \param p The point to include with the bounding box. 58 | */ 59 | void include(Point3 p); 60 | 61 | /*! 62 | * Expand the AABB3D to include the bounding box \p aabb. 63 | * \param aabb The aabb to include with this bounding box. 64 | */ 65 | void include(const AABB3D& aabb); 66 | 67 | /*! 68 | * Expand the AABB3D to include a z-coordinate. 69 | * 70 | * This is for including a point of which the X and Y coordinates are 71 | * unknown but known to already be included in the bounding box. 72 | */ 73 | void includeZ(coord_t z); 74 | 75 | /*! 76 | * Offset the coordinates of the bounding box. 77 | * \param offset The offset with which to offset the AABB3D. 78 | */ 79 | void offset(Point3 offset); 80 | 81 | /*! 82 | * Offset the coordinates of the bounding box. 83 | * \param offset The offset with which to offset the AABB3D. 84 | */ 85 | void offset(Point offset); 86 | 87 | /*! 88 | * Offset the bounding box in the horizontal direction; outward or inward. 89 | * 90 | * \param outset the distance (positive or negative) to expand the bounding box outward 91 | */ 92 | void expandXY(int outset); 93 | }; 94 | 95 | }//namespace visualizer 96 | #endif//UTILS_AABB3D_H 97 | 98 | -------------------------------------------------------------------------------- /src/utils/Coord_t.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_COORD_T_H 5 | #define UTILS_COORD_T_H 6 | 7 | 8 | //Include Clipper to get the ClipperLib::IntPoint definition, which we reuse as Point definition. 9 | #include 10 | 11 | namespace visualizer 12 | { 13 | 14 | using coord_t = ClipperLib::cInt; 15 | 16 | #define INT2MM(n) (double(n) / 1000.0) 17 | #define INT2MM2(n) (double(n) / 1000000.0) 18 | #define MM2INT(n) (coord_t((n) * 1000 + 0.5 * (((n) > 0) - ((n) < 0)))) 19 | #define MM2_2INT(n) (coord_t((n) * 1000000 + 0.5 * (((n) > 0) - ((n) < 0)))) 20 | #define MM3_2INT(n) (coord_t((n) * 1000000000 + 0.5 * (((n) > 0) - ((n) < 0)))) 21 | 22 | #define INT2MICRON(n) ((n) / 1) 23 | #define MICRON2INT(n) ((n) * 1) 24 | 25 | } // namespace visualizer 26 | 27 | 28 | #endif // UTILS_COORD_T_H 29 | -------------------------------------------------------------------------------- /src/utils/ExtrusionJunction.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_EXTRUSION_JUNCTION_H 5 | #define UTILS_EXTRUSION_JUNCTION_H 6 | 7 | #include "IntPoint.h" 8 | #include "Point3.h" 9 | 10 | namespace visualizer 11 | { 12 | 13 | struct ExtrusionJunction 14 | { 15 | Point p; 16 | coord_t w; 17 | coord_t perimeter_index; 18 | ExtrusionJunction(Point p, coord_t w, coord_t perimeter_index) 19 | : p(p), w(w), perimeter_index(perimeter_index) {} 20 | ExtrusionJunction(Point p, coord_t w) 21 | : p(p), w(w), perimeter_index(-1) {} 22 | bool operator==(const ExtrusionJunction& other) const 23 | { 24 | return p == other.p 25 | && w == other.w 26 | && perimeter_index == other.perimeter_index; 27 | } 28 | Point3 toPoint3() const 29 | { 30 | return Point3(p.X, p.Y, w); 31 | } 32 | }; 33 | 34 | 35 | 36 | 37 | } // namespace visualizer 38 | #endif // UTILS_EXTRUSION_JUNCTION_H 39 | -------------------------------------------------------------------------------- /src/utils/ExtrusionLine.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_EXTRUSION_LINE_H 5 | #define UTILS_EXTRUSION_LINE_H 6 | 7 | #include "ExtrusionJunction.h" 8 | 9 | namespace visualizer 10 | { 11 | 12 | struct ExtrusionLine 13 | { 14 | coord_t inset_idx; 15 | std::list junctions; 16 | ExtrusionLine(coord_t inset_idx) 17 | : inset_idx(inset_idx) 18 | {} 19 | ExtrusionLine() 20 | : inset_idx(-1) 21 | {} 22 | coord_t computeLength() 23 | { 24 | if (junctions.size() <= 1) return 0; 25 | coord_t len = 0; 26 | ExtrusionJunction prev = junctions.front(); 27 | for (ExtrusionJunction& next : junctions) 28 | { 29 | len += vSize(next.p - prev.p); 30 | prev = next; 31 | } 32 | return len; 33 | } 34 | }; 35 | 36 | 37 | } // namespace visualizer 38 | #endif // UTILS_EXTRUSION_LINE_H 39 | -------------------------------------------------------------------------------- /src/utils/ExtrusionSegment.cpp: -------------------------------------------------------------------------------- 1 | /** Copyright (C) 2019 Ultimaker */ 2 | #include "ExtrusionSegment.h" 3 | 4 | #include "utils/logoutput.h" 5 | 6 | namespace visualizer 7 | { 8 | 9 | Polygons ExtrusionSegment::toPolygons(bool reduced) const 10 | { 11 | Polygons ret; 12 | Point vec = to.p - from.p; 13 | coord_t vec_length = vSize( vec ); 14 | 15 | if (vec_length <= 0) 16 | { 17 | return ret; 18 | } 19 | 20 | PolygonRef poly = ret.newPoly(); 21 | float delta_r = 0.5f * std::abs(from.w - to.w); 22 | float vec_length_fixed = std::max(delta_r, static_cast(vec_length)); 23 | float alpha = std::acos(delta_r / vec_length_fixed); 24 | if (to.w > from.w) 25 | { 26 | alpha = M_PI - alpha; 27 | } 28 | assert(alpha > - M_PI - 0.0001); 29 | assert(alpha < M_PI + 0.0001); 30 | 31 | float dir = std::atan(vec.Y / static_cast(vec.X)); 32 | if (vec.X < 0) 33 | { 34 | dir += M_PI; 35 | } 36 | 37 | { 38 | poly.emplace_back(from.p + Point(from.w / 2 * cos(alpha + dir), from.w / 2 * sin(alpha + dir))); 39 | float start_a = 2 * M_PI; while (start_a > alpha + dir) start_a -= a_step; 40 | start_a += a_step; 41 | float end_a = -2 * M_PI; while (end_a < 2 * M_PI - alpha + dir) end_a += a_step; 42 | for (float a = start_a; a <= end_a; a += a_step) 43 | { 44 | poly.emplace_back(from.p + Point(from.w / 2 * cos(a), from.w / 2 * sin(a))); 45 | } 46 | poly.emplace_back(from.p + Point(from.w / 2 * cos(2 * M_PI - alpha + dir), from.w / 2 * sin(2 * M_PI - alpha + dir))); 47 | } 48 | { 49 | poly.emplace_back(to.p + Point(to.w / 2 * cos(2 * M_PI - alpha + dir), to.w / 2 * sin(2 * M_PI - alpha + dir))); 50 | float start_a = 2 * M_PI; while (start_a > alpha + dir) start_a -= a_step; 51 | if (reduced) 52 | { 53 | start_a += a_step; 54 | } 55 | float end_a = -2 * M_PI; while (end_a < 2 * M_PI - alpha + dir) end_a += a_step; 56 | if (reduced) 57 | { 58 | end_a -= a_step; 59 | } 60 | else 61 | { 62 | end_a -= 2 * M_PI; 63 | } 64 | if (reduced) 65 | { 66 | for (float a = end_a; a >= start_a; a -= a_step) 67 | { 68 | poly.emplace_back(to.p + Point(to.w / 2 * cos(a), to.w / 2 * sin(a))); 69 | } 70 | } 71 | else 72 | { 73 | for (float a = end_a; a <= start_a; a += a_step) 74 | { 75 | poly.emplace_back(to.p + Point(to.w / 2 * cos(a), to.w / 2 * sin(a))); 76 | } 77 | } 78 | poly.emplace_back(to.p + Point(to.w / 2 * cos(alpha + dir), to.w / 2 * sin(alpha + dir))); 79 | } 80 | 81 | for (Point p : poly) 82 | { 83 | assert(p.X < 0x3FFFFFFFFFFFFFFFLL); 84 | assert(p.Y < 0x3FFFFFFFFFFFFFFFLL); 85 | } 86 | 87 | return ret; 88 | } 89 | 90 | double ExtrusionSegment::getArea(bool reduced) const 91 | { 92 | coord_t r = from.w / 2; 93 | coord_t s = to.w / 2; 94 | coord_t d2 = vSize2(to.p - from.p); 95 | coord_t d = sqrt(d2); 96 | coord_t dr = s - r; 97 | coord_t l2 = d2 - dr * dr; 98 | coord_t l = std::max(0.0, sqrt(l2)); 99 | coord_t trapezoid_area = l * (r + s) / 2; 100 | double a = asin(sqrt(INT2MM2(l2) / INT2MM2(d2))); 101 | double b = M_PI - a; 102 | if (to.w > from.w) 103 | std::swap(a, b); 104 | coord_t r2 = r * r; 105 | coord_t s2 = s * s; 106 | coord_t start_area = reduced? - .5 * b * r2 : .5 * a * r2; 107 | coord_t end_area = .5 * b * s2; 108 | 109 | coord_t total_area = std::max(coord_t(0), (trapezoid_area + end_area + start_area) * 2); 110 | // assert(double(std::abs(total_area - toPolygons(reduced).area())) / double(total_area) < 0.01); 111 | return total_area; 112 | } 113 | 114 | }//namespace cura 115 | -------------------------------------------------------------------------------- /src/utils/ExtrusionSegment.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_EXTRUSION_SEGMENT_H 5 | #define UTILS_EXTRUSION_SEGMENT_H 6 | 7 | #include 8 | 9 | #include "utils/IntPoint.h" 10 | #include "utils/polygon.h" 11 | #include "utils/polygonUtils.h" 12 | #include "utils/ExtrusionJunction.h" 13 | 14 | namespace visualizer 15 | { 16 | 17 | /*! 18 | * extrusion bead 19 | * suports varying width 20 | */ 21 | class ExtrusionSegment 22 | { 23 | static constexpr float a_step = 15 / 180.0 * M_PI; 24 | public: 25 | ExtrusionJunction from; 26 | ExtrusionJunction to; 27 | 28 | bool is_odd; //!< Whether this is a polyline segment rather than a polygonal segment 29 | 30 | ExtrusionSegment(ExtrusionJunction from, ExtrusionJunction to, bool is_odd) 31 | : from(from) 32 | , to(to) 33 | , is_odd(is_odd) 34 | {} 35 | 36 | /*! 37 | * \param reduced Whether to remove the circle from the to-location because it will be included in the next extrusion move 38 | */ 39 | Polygons toPolygons(bool reduced) const; 40 | 41 | double getArea(bool reduced) const; 42 | }; 43 | 44 | 45 | 46 | 47 | } // namespace visualizer 48 | #endif // UTILS_EXTRUSION_SEGMENT_H 49 | -------------------------------------------------------------------------------- /src/utils/HalfEdge.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_HALF_EDGE_H 5 | #define UTILS_HALF_EDGE_H 6 | 7 | #include 8 | 9 | namespace visualizer 10 | { 11 | 12 | template 13 | class HalfEdgeNode; 14 | 15 | 16 | template 17 | class HalfEdge 18 | { 19 | using edge_t = HalfEdge; 20 | using node_t = HalfEdgeNode; 21 | public: 22 | edge_data_t data; 23 | edge_t* twin = nullptr; 24 | edge_t* next = nullptr; 25 | edge_t* prev = nullptr; 26 | node_t* from = nullptr; 27 | node_t* to = nullptr; 28 | HalfEdge(edge_data_t data) 29 | : data(data) 30 | {} 31 | bool operator==(const HalfEdge& other) 32 | { 33 | return this == &other; 34 | } 35 | }; 36 | 37 | 38 | 39 | 40 | } // namespace visualizer 41 | #endif // UTILS_HALF_EDGE_H 42 | -------------------------------------------------------------------------------- /src/utils/HalfEdgeGraph.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_HALF_EDGE_GRAPH_H 5 | #define UTILS_HALF_EDGE_GRAPH_H 6 | 7 | 8 | #include 9 | #include 10 | 11 | 12 | 13 | #include "HalfEdge.h" 14 | #include "HalfEdgeNode.h" 15 | #include "SVG.h" 16 | 17 | namespace visualizer 18 | { 19 | 20 | template // types of data contained in nodes and edges 21 | class HalfEdgeGraph 22 | { 23 | public: 24 | using edge_t = HalfEdge; 25 | using node_t = HalfEdgeNode; 26 | std::list edges; 27 | std::list nodes; 28 | 29 | void debugOutput(std::string filename); 30 | void debugOutput(SVG& svg); 31 | 32 | bool bedugCheckDataCompleteness() const; 33 | }; 34 | 35 | 36 | 37 | template 38 | void HalfEdgeGraph::debugOutput(std::string filename) 39 | { 40 | AABB aabb; 41 | for (node_t& node : nodes) 42 | { 43 | aabb.include(node.p); 44 | } 45 | SVG svg(filename.c_str(), aabb); 46 | debugOutput(svg); 47 | } 48 | 49 | 50 | template 51 | void HalfEdgeGraph::debugOutput(SVG& svg) 52 | { 53 | // for (node_t& node : nodes) 54 | // { 55 | // svg.writePoint(node.p); 56 | // } 57 | for (edge_t& edge : edges) 58 | { 59 | svg.writeLine(edge.from->p, edge.to->p, SVG::Color::RED); 60 | } 61 | } 62 | 63 | 64 | template 65 | bool HalfEdgeGraph::bedugCheckDataCompleteness() const 66 | { 67 | size_t problems = 0; 68 | for (const node_t& node : nodes) 69 | { 70 | if (!node.some_edge) 71 | { 72 | problems++; 73 | assert(false); 74 | } 75 | } 76 | for (const edge_t& edge : edges) 77 | { 78 | if (!edge.twin || !edge.next || !edge.prev || !edge.from || !edge.to) 79 | { 80 | problems++; 81 | assert(false); 82 | } 83 | } 84 | 85 | assert(problems == 0); 86 | return problems == 0; 87 | } 88 | 89 | 90 | 91 | } // namespace visualizer 92 | #endif // UTILS_HALF_EDGE_GRAPH_H 93 | -------------------------------------------------------------------------------- /src/utils/HalfEdgeNode.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_HALF_EDGE_NODE_H 5 | #define UTILS_HALF_EDGE_NODE_H 6 | 7 | #include 8 | 9 | #include "IntPoint.h" 10 | 11 | namespace visualizer 12 | { 13 | 14 | template 15 | class HalfEdge; 16 | 17 | template 18 | class HalfEdgeNode 19 | { 20 | using edge_t = HalfEdge; 21 | using node_t = HalfEdgeNode; 22 | public: 23 | node_data_t data; 24 | Point p; 25 | edge_t* some_edge = nullptr; 26 | HalfEdgeNode(node_data_t data, Point p) 27 | : data(data) 28 | , p(p) 29 | {} 30 | bool operator==(const HalfEdgeNode& other) 31 | { 32 | return this == &other; 33 | } 34 | }; 35 | 36 | 37 | 38 | 39 | } // namespace visualizer 40 | #endif // UTILS_HALF_EDGE_NODE_H 41 | -------------------------------------------------------------------------------- /src/utils/IntPoint.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_INT_POINT_H 5 | #define UTILS_INT_POINT_H 6 | 7 | /** 8 | The integer point classes are used as soon as possible and represent microns in 2D or 3D space. 9 | Integer points are used to avoid floating point rounding errors, and because ClipperLib uses them. 10 | */ 11 | #define INLINE static inline 12 | 13 | //Include Clipper to get the ClipperLib::IntPoint definition, which we reuse as Point definition. 14 | #include 15 | #include 16 | #include // for hash function object 17 | #include // auto-serialization / auto-toString() 18 | #include 19 | #include 20 | 21 | #include "Point3.h" //For applying Point3Matrices. 22 | 23 | 24 | #include "../utils/math.h" // for M_PI. Use relative path to avoid pulling 25 | 26 | #ifdef __GNUC__ 27 | #define DEPRECATED(func) func __attribute__ ((deprecated)) 28 | #elif defined(_MSC_VER) 29 | #define DEPRECATED(func) __declspec(deprecated) func 30 | #else 31 | #pragma message("WARNING: You need to implement DEPRECATED for this compiler") 32 | #define DEPRECATED(func) func 33 | #endif 34 | 35 | 36 | namespace visualizer 37 | { 38 | 39 | /* 64bit Points are used mostly throughout the code, these are the 2D points from ClipperLib */ 40 | typedef ClipperLib::IntPoint Point; 41 | 42 | class IntPoint { 43 | public: 44 | int X, Y; 45 | Point p() { return Point(X, Y); } 46 | }; 47 | #define POINT_MIN std::numeric_limits::min() 48 | #define POINT_MAX std::numeric_limits::max() 49 | 50 | static Point no_point(std::numeric_limits::min(), std::numeric_limits::min()); 51 | 52 | /* Extra operators to make it easier to do math with the 64bit Point objects */ 53 | INLINE Point operator-(const Point& p0) { return Point(-p0.X, -p0.Y); } 54 | INLINE Point operator+(const Point& p0, const Point& p1) { return Point(p0.X+p1.X, p0.Y+p1.Y); } 55 | INLINE Point operator-(const Point& p0, const Point& p1) { return Point(p0.X-p1.X, p0.Y-p1.Y); } 56 | INLINE Point operator*(const Point& p0, const coord_t i) { return Point(p0.X * i, p0.Y * i); } 57 | template::value, T>::type> //Use only for numeric types. 58 | INLINE Point operator*(const Point& p0, const T i) { return Point(p0.X * i, p0.Y * i); } 59 | template::value, T>::type> //Use only for numeric types. 60 | INLINE Point operator*(const T i, const Point& p0) { return p0 * i; } 61 | INLINE Point operator/(const Point& p0, const coord_t i) { return Point(p0.X/i, p0.Y/i); } 62 | INLINE Point operator/(const Point& p0, const Point& p1) { return Point(p0.X/p1.X, p0.Y/p1.Y); } 63 | 64 | INLINE Point& operator += (Point& p0, const Point& p1) { p0.X += p1.X; p0.Y += p1.Y; return p0; } 65 | INLINE Point& operator -= (Point& p0, const Point& p1) { p0.X -= p1.X; p0.Y -= p1.Y; return p0; } 66 | 67 | INLINE bool operator < (const Point& p0, const Point& p1) { return p0.X < p1.X || (p0.X == p1.X && p0.Y < p1.Y); } 68 | 69 | /* ***** NOTE ***** 70 | TL;DR: DO NOT implement operators *= and /= because of the default values in ClipperLib::IntPoint's constructor. 71 | 72 | We DO NOT implement operators *= and /= because the class Point is essentially a ClipperLib::IntPoint and it has a 73 | constructor IntPoint(int x = 0, int y = 0), and this causes problems. If you implement *= as *=(int) and when you 74 | do "Point a = a * 5", you probably intend to do "a.x *= 5" and "a.y *= 5", but with that constructor, it will create 75 | an IntPoint(5, y = 0) and you end up with wrong results. 76 | */ 77 | 78 | //INLINE bool operator==(const Point& p0, const Point& p1) { return p0.X==p1.X&&p0.Y==p1.Y; } 79 | //INLINE bool operator!=(const Point& p0, const Point& p1) { return p0.X!=p1.X||p0.Y!=p1.Y; } 80 | 81 | INLINE coord_t vSize2(const Point& p0) 82 | { 83 | return p0.X*p0.X+p0.Y*p0.Y; 84 | } 85 | INLINE float vSize2f(const Point& p0) 86 | { 87 | return float(p0.X)*float(p0.X)+float(p0.Y)*float(p0.Y); 88 | } 89 | 90 | INLINE bool shorterThen(const Point& p0, int32_t len) 91 | { 92 | if (p0.X > len || p0.X < -len) 93 | return false; 94 | if (p0.Y > len || p0.Y < -len) 95 | return false; 96 | return vSize2(p0) <= len*len; 97 | } 98 | 99 | INLINE coord_t vSize(const Point& p0) 100 | { 101 | return sqrt(vSize2(p0)); 102 | } 103 | 104 | INLINE double vSizeMM(const Point& p0) 105 | { 106 | double fx = INT2MM(p0.X); 107 | double fy = INT2MM(p0.Y); 108 | return sqrt(fx*fx+fy*fy); 109 | } 110 | 111 | INLINE Point normal(const Point& p0, coord_t len) 112 | { 113 | coord_t _len = vSize(p0); 114 | if (_len < 1) 115 | return Point(len, 0); 116 | return p0 * len / _len; 117 | } 118 | 119 | INLINE Point turn90CCW(const Point& p0) 120 | { 121 | return Point(-p0.Y, p0.X); 122 | } 123 | 124 | INLINE Point rotate(const Point& p0, double angle) 125 | { 126 | const double cos_component = std::cos(angle); 127 | const double sin_component = std::sin(angle); 128 | return Point(cos_component * p0.X - sin_component * p0.Y, sin_component * p0.X + cos_component * p0.Y); 129 | } 130 | 131 | INLINE coord_t dot(const Point& p0, const Point& p1) 132 | { 133 | return p0.X * p1.X + p0.Y * p1.Y; 134 | } 135 | 136 | INLINE coord_t cross(const Point& p0, const Point& p1) 137 | { 138 | return p0.X * p1.Y - p0.Y * p1.X; 139 | } 140 | 141 | INLINE int angle(const Point& p) 142 | { 143 | double angle = std::atan2(p.X, p.Y) / M_PI * 180.0; 144 | if (angle < 0.0) angle += 360.0; 145 | return angle; 146 | } 147 | 148 | }//namespace visualizer 149 | 150 | namespace std { 151 | template <> 152 | struct hash { 153 | size_t operator()(const visualizer::Point & pp) const 154 | { 155 | static int prime = 31; 156 | int result = 89; 157 | result = result * prime + pp.X; 158 | result = result * prime + pp.Y; 159 | return result; 160 | } 161 | }; 162 | } 163 | 164 | namespace visualizer 165 | { 166 | 167 | class PointMatrix 168 | { 169 | public: 170 | double matrix[4]; 171 | 172 | PointMatrix() 173 | { 174 | matrix[0] = 1; 175 | matrix[1] = 0; 176 | matrix[2] = 0; 177 | matrix[3] = 1; 178 | } 179 | 180 | PointMatrix(double rotation) 181 | { 182 | rotation = rotation / 180 * M_PI; 183 | matrix[0] = cos(rotation); 184 | matrix[1] = -sin(rotation); 185 | matrix[2] = -matrix[1]; 186 | matrix[3] = matrix[0]; 187 | } 188 | 189 | PointMatrix(const Point p) 190 | { 191 | matrix[0] = p.X; 192 | matrix[1] = p.Y; 193 | double f = sqrt((matrix[0] * matrix[0]) + (matrix[1] * matrix[1])); 194 | matrix[0] /= f; 195 | matrix[1] /= f; 196 | matrix[2] = -matrix[1]; 197 | matrix[3] = matrix[0]; 198 | } 199 | 200 | static PointMatrix scale(double s) 201 | { 202 | PointMatrix ret; 203 | ret.matrix[0] = s; 204 | ret.matrix[3] = s; 205 | return ret; 206 | } 207 | 208 | Point apply(const Point p) const 209 | { 210 | return Point(p.X * matrix[0] + p.Y * matrix[1], p.X * matrix[2] + p.Y * matrix[3]); 211 | } 212 | 213 | Point unapply(const Point p) const 214 | { 215 | return Point(p.X * matrix[0] + p.Y * matrix[2], p.X * matrix[1] + p.Y * matrix[3]); 216 | } 217 | }; 218 | 219 | class Point3Matrix 220 | { 221 | public: 222 | double matrix[9]; 223 | 224 | Point3Matrix() 225 | { 226 | matrix[0] = 1; 227 | matrix[1] = 0; 228 | matrix[2] = 0; 229 | matrix[3] = 0; 230 | matrix[4] = 1; 231 | matrix[5] = 0; 232 | matrix[6] = 0; 233 | matrix[7] = 0; 234 | matrix[8] = 1; 235 | } 236 | 237 | /*! 238 | * Initializes the top left corner with the values of \p b 239 | * and the rest as if it's a unit matrix 240 | */ 241 | Point3Matrix(const PointMatrix& b) 242 | { 243 | matrix[0] = b.matrix[0]; 244 | matrix[1] = b.matrix[1]; 245 | matrix[2] = 0; 246 | matrix[3] = b.matrix[2]; 247 | matrix[4] = b.matrix[3]; 248 | matrix[5] = 0; 249 | matrix[6] = 0; 250 | matrix[7] = 0; 251 | matrix[8] = 1; 252 | } 253 | 254 | Point3 apply(const Point3 p) const 255 | { 256 | return Point3(p.x * matrix[0] + p.y * matrix[1] + p.z * matrix[2] 257 | , p.x * matrix[3] + p.y * matrix[4] + p.z * matrix[5] 258 | , p.x * matrix[6] + p.y * matrix[7] + p.z * matrix[8]); 259 | } 260 | 261 | /*! 262 | * Apply matrix to vector as homogeneous coordinates. 263 | */ 264 | Point apply(const Point p) const 265 | { 266 | Point3 result = apply(Point3(p.X, p.Y, 1)); 267 | return Point(result.x / result.z, result.y / result.z); 268 | } 269 | 270 | static Point3Matrix translate(const Point p) 271 | { 272 | Point3Matrix ret; // uniform matrix 273 | ret.matrix[2] = p.X; 274 | ret.matrix[5] = p.Y; 275 | return ret; 276 | } 277 | 278 | Point3Matrix compose(const Point3Matrix& b) 279 | { 280 | Point3Matrix ret; 281 | for (int outx = 0; outx < 3; outx++) 282 | { 283 | for (int outy = 0; outy < 3; outy++) 284 | { 285 | ret.matrix[outy * 3 + outx] = 0; 286 | for (int in = 0; in < 3; in++) 287 | { 288 | ret.matrix[outy * 3 + outx] += matrix[outy * 3 + in] * b.matrix[in * 3 + outx]; 289 | } 290 | } 291 | } 292 | return ret; 293 | } 294 | }; 295 | 296 | 297 | inline Point3 operator+(const Point3& p3, const Point& p2) { 298 | return Point3(p3.x + p2.X, p3.y + p2.Y, p3.z); 299 | } 300 | inline Point3& operator+=(Point3& p3, const Point& p2) { 301 | p3.x += p2.X; 302 | p3.y += p2.Y; 303 | return p3; 304 | } 305 | 306 | inline Point operator+(const Point& p2, const Point3& p3) { 307 | return Point(p3.x + p2.X, p3.y + p2.Y); 308 | } 309 | 310 | 311 | inline Point3 operator-(const Point3& p3, const Point& p2) { 312 | return Point3(p3.x - p2.X, p3.y - p2.Y, p3.z); 313 | } 314 | inline Point3& operator-=(Point3& p3, const Point& p2) { 315 | p3.x -= p2.X; 316 | p3.y -= p2.Y; 317 | return p3; 318 | } 319 | 320 | inline Point operator-(const Point& p2, const Point3& p3) { 321 | return Point(p2.X - p3.x, p2.Y - p3.y); 322 | } 323 | 324 | }//namespace visualizer 325 | #endif//UTILS_INT_POINT_H 326 | 327 | -------------------------------------------------------------------------------- /src/utils/LinearAlg2D.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2019 Ultimaker B.V. 2 | 3 | 4 | #include "linearAlg2D.h" 5 | 6 | #include // atan2 7 | #include 8 | #include // swap 9 | 10 | #include "IntPoint.h" // dot 11 | 12 | namespace visualizer 13 | { 14 | 15 | float LinearAlg2D::getAngleLeft(const Point& a, const Point& b, const Point& c) 16 | { 17 | const Point ba = a - b; 18 | const Point bc = c - b; 19 | const coord_t dott = dot(ba, bc); // dot product 20 | const coord_t det = ba.X * bc.Y - ba.Y * bc.X; // determinant 21 | const float angle = -atan2(det, dott); // from -pi to pi 22 | if (angle >= 0) 23 | { 24 | return angle; 25 | } 26 | else 27 | { 28 | return M_PI * 2 + angle; 29 | } 30 | } 31 | 32 | 33 | bool LinearAlg2D::getPointOnLineWithDist(const Point& p, const Point& a, const Point& b, const coord_t dist, Point& result) 34 | { 35 | // result 36 | // v 37 | // b<----r---a.......x 38 | // '-. : 39 | // '-. : 40 | // '-.p 41 | const Point ab = b - a; 42 | const coord_t ab_size = vSize(ab); 43 | const Point ap = p - a; 44 | const coord_t ax_size = (ab_size < 50)? dot(normal(ab, 1000), ap) / 1000 : dot(ab, ap) / ab_size; 45 | const coord_t ap_size2 = vSize2(ap); 46 | const coord_t px_size = sqrt(std::max(coord_t(0), ap_size2 - ax_size * ax_size)); 47 | if (px_size > dist) 48 | { 49 | return false; 50 | } 51 | const coord_t xr_size = sqrt(dist * dist - px_size * px_size); 52 | if (ax_size <= 0) 53 | { // x lies before ab 54 | const coord_t ar_size = xr_size + ax_size; 55 | if (ar_size < 0 || ar_size > ab_size) 56 | { // r lies outisde of ab 57 | return false; 58 | } 59 | else 60 | { 61 | result = a + normal(ab, ar_size); 62 | return true; 63 | } 64 | } 65 | else if (ax_size >= ab_size) 66 | { // x lies after ab 67 | // result 68 | // v 69 | // a-----r-->b.......x 70 | // '-. : 71 | // '-. : 72 | // '-.p 73 | const coord_t ar_size = ax_size - xr_size; 74 | if (ar_size < 0 || ar_size > ab_size) 75 | { // r lies outisde of ab 76 | return false; 77 | } 78 | else 79 | { 80 | result = a + normal(ab, ar_size); 81 | return true; 82 | } 83 | } 84 | else // ax_size > 0 && ax_size < ab_size 85 | { // x lies on ab 86 | // result is either or 87 | // v v 88 | // a-----r-----------x-----------r----->b 89 | // '-. : .-' 90 | // '-. : .-' 91 | // '-.p.-' 92 | // or there is not result: 93 | // v v 94 | // r a-------x---->b r 95 | // '-. : .-' 96 | // '-. : .-' 97 | // '-.p.-' 98 | // try r in both directions 99 | const coord_t ar1_size = ax_size - xr_size; 100 | if (ar1_size >= 0) 101 | { 102 | result = a + normal(ab, ar1_size); 103 | return true; 104 | } 105 | const coord_t ar2_size = ax_size + xr_size; 106 | if (ar2_size < ab_size) 107 | { 108 | result = a + normal(ab, ar2_size); 109 | return true; 110 | } 111 | return false; 112 | } 113 | } 114 | 115 | 116 | std::pair LinearAlg2D::getClosestConnection(Point a1, Point a2, Point b1, Point b2) 117 | { 118 | Point b1_on_a = getClosestOnLineSegment(b1, a1, a2); 119 | coord_t b1_on_a_dist2 = vSize2(b1_on_a - b1); 120 | Point b2_on_a = getClosestOnLineSegment(b2, a1, a2); 121 | coord_t b2_on_a_dist2 = vSize2(b2_on_a - b2); 122 | Point a1_on_b = getClosestOnLineSegment(a1, b1, b2); 123 | coord_t a1_on_b_dist2 = vSize2(a1_on_b - a1); 124 | Point a2_on_b = getClosestOnLineSegment(a1, b1, b2); 125 | coord_t a2_on_b_dist2 = vSize2(a2_on_b - a2); 126 | if (b1_on_a_dist2 < b2_on_a_dist2 && b1_on_a_dist2 < a1_on_b_dist2 && b1_on_a_dist2 < a2_on_b_dist2) 127 | { 128 | return std::make_pair(b1_on_a, b1); 129 | } 130 | else if (b2_on_a_dist2 < a1_on_b_dist2 && b2_on_a_dist2 < a2_on_b_dist2) 131 | { 132 | return std::make_pair(b2_on_a, b2); 133 | } 134 | else if (a1_on_b_dist2 < a2_on_b_dist2) 135 | { 136 | return std::make_pair(a1, a1_on_b); 137 | } 138 | else 139 | { 140 | return std::make_pair(a2, a2_on_b); 141 | } 142 | } 143 | 144 | bool LinearAlg2D::lineSegmentsCollide(const Point& a_from_transformed, const Point& a_to_transformed, Point b_from_transformed, Point b_to_transformed) 145 | { 146 | assert(std::abs(a_from_transformed.Y - a_to_transformed.Y) < 2 && "line a is supposed to be transformed to be aligned with the X axis!"); 147 | assert(a_from_transformed.X - 2 <= a_to_transformed.X && "line a is supposed to be aligned with X axis in positive direction!"); 148 | if ((b_from_transformed.Y >= a_from_transformed.Y && b_to_transformed.Y <= a_from_transformed.Y) || (b_to_transformed.Y >= a_from_transformed.Y && b_from_transformed.Y <= a_from_transformed.Y)) 149 | { 150 | if(b_to_transformed.Y == b_from_transformed.Y) 151 | { 152 | if (b_to_transformed.X < b_from_transformed.X) 153 | { 154 | std::swap(b_to_transformed.X, b_from_transformed.X); 155 | } 156 | if (b_from_transformed.X > a_to_transformed.X) 157 | { 158 | return false; 159 | } 160 | if (b_to_transformed.X < a_from_transformed.X) 161 | { 162 | return false; 163 | } 164 | return true; 165 | } 166 | else 167 | { 168 | const coord_t x = b_from_transformed.X + (b_to_transformed.X - b_from_transformed.X) * (a_from_transformed.Y - b_from_transformed.Y) / (b_to_transformed.Y - b_from_transformed.Y); 169 | if (x >= a_from_transformed.X && x <= a_to_transformed.X) 170 | { 171 | return true; 172 | } 173 | } 174 | } 175 | return false; 176 | } 177 | 178 | coord_t LinearAlg2D::getDist2FromLine(const Point& p, const Point& a, const Point& b) 179 | { 180 | // x.......a------------b 181 | // : 182 | // : 183 | // p 184 | // return px_size 185 | const Point vab = b - a; 186 | const Point vap = p - a; 187 | const coord_t ab_size2 = vSize2(vab); 188 | const coord_t ap_size2 = vSize2(vap); 189 | if(ab_size2 == 0) //Line of 0 length. Assume it's a line perpendicular to the direction to p. 190 | { 191 | return ap_size2; 192 | } 193 | const coord_t dott = dot(vab, vap); 194 | const coord_t ax_size2 = dott * dott / vSize2(vab); 195 | const coord_t px_size2 = std::max(coord_t(0), ap_size2 - ax_size2); 196 | return px_size2; 197 | } 198 | 199 | bool LinearAlg2D::isInsideCorner(const Point a, const Point b, const Point c, const Point q) 200 | { 201 | constexpr coord_t normal_length = 10000; 202 | Point ba = normal(a - b, normal_length); 203 | Point bc = normal(c - b, normal_length); 204 | Point bq = q - b; 205 | Point n = turn90CCW(bq); 206 | coord_t anx = dot(ba, n); 207 | coord_t cnx = dot(bc, n); 208 | if ((anx > 0) != (cnx > 0)) 209 | { 210 | return anx > 0; 211 | } 212 | else 213 | { 214 | coord_t ax = dot(ba, bq); 215 | coord_t cx = dot(bc, bq); 216 | return (cx < ax) == (anx > 0); 217 | } 218 | } 219 | 220 | } // namespace visualizer 221 | -------------------------------------------------------------------------------- /src/utils/ListPolyIt.cpp: -------------------------------------------------------------------------------- 1 | #include "ListPolyIt.h" 2 | 3 | #include // isfinite 4 | #include // ostream 5 | 6 | #include "AABB.h" // for debug output svg html 7 | #include "SVG.h" 8 | 9 | namespace visualizer 10 | { 11 | 12 | 13 | void ListPolyIt::convertPolygonsToLists(const Polygons& polys, ListPolygons& result) 14 | { 15 | for (ConstPolygonRef poly : polys) 16 | { 17 | result.emplace_back(); 18 | convertPolygonToList(poly, result.back()); 19 | } 20 | } 21 | 22 | void ListPolyIt::convertPolygonToList(ConstPolygonRef poly, ListPolygon& result) 23 | { 24 | #ifdef DEBUG 25 | Point last = poly.back(); 26 | #endif // DEBUG 27 | for (const Point& p : poly) 28 | { 29 | result.push_back(p); 30 | #ifdef DEBUG 31 | // usually polygons shouldn't have such degenerate verts 32 | // in PolygonProximityLinker (where this function is (also) used) it is 33 | // required to not have degenerate verts, because verts are mapped 34 | // to links, but if two different verts are at the same place the mapping fails. 35 | assert(p != last); 36 | last = p; 37 | #endif // DEBUG 38 | } 39 | } 40 | 41 | 42 | void ListPolyIt::convertListPolygonsToPolygons(const ListPolygons& list_polygons, Polygons& polygons) 43 | { 44 | for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) 45 | { 46 | polygons[poly_idx].clear(); 47 | convertListPolygonToPolygon(list_polygons[poly_idx], polygons[poly_idx]); 48 | } 49 | } 50 | 51 | void ListPolyIt::convertListPolygonToPolygon(const ListPolygon& list_polygon, PolygonRef polygon) 52 | { 53 | for (const Point& p : list_polygon) 54 | { 55 | polygon.add(p); 56 | } 57 | } 58 | 59 | ListPolyIt ListPolyIt::insertPointNonDuplicate(const ListPolyIt before, const ListPolyIt after, const Point to_insert) 60 | { 61 | if (to_insert == before.p()) 62 | { 63 | return before; 64 | } 65 | else if (to_insert == after.p()) 66 | { 67 | return after; 68 | } 69 | else 70 | { 71 | ListPolygon& poly = *after.poly; 72 | return ListPolyIt(poly, poly.insert(after.it, to_insert)); 73 | } 74 | } 75 | 76 | 77 | 78 | }//namespace visualizer 79 | -------------------------------------------------------------------------------- /src/utils/ListPolyIt.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_LIST_POLY_IT_H 5 | #define UTILS_LIST_POLY_IT_H 6 | 7 | #include 8 | #include 9 | 10 | 11 | #include "IntPoint.h" 12 | #include "polygon.h" 13 | 14 | 15 | namespace visualizer 16 | { 17 | 18 | /*! 19 | * A wrapper class for a ListPolygon::iterator and a reference to the containing ListPolygon 20 | */ 21 | class ListPolyIt 22 | { 23 | public: 24 | ListPolygon* poly; //!< The polygon 25 | ListPolygon::iterator it; //!< The iterator into ListPolyIt::poly 26 | ListPolyIt(const ListPolyIt& other) 27 | : poly(other.poly) 28 | , it(other.it) 29 | { 30 | } 31 | ListPolyIt(ListPolygon& poly, ListPolygon::iterator it) 32 | : poly(&poly) 33 | , it(it) 34 | { 35 | } 36 | Point& p() const 37 | { 38 | return *it; 39 | } 40 | /*! 41 | * Test whether two iterators refer to the same polygon in the same polygon list. 42 | * 43 | * \param other The ListPolyIt to test for equality 44 | * \return Wether the right argument refers to the same polygon in the same ListPolygon as the left argument. 45 | */ 46 | bool operator==(const ListPolyIt& other) const 47 | { 48 | return poly == other.poly && it == other.it; 49 | } 50 | bool operator!=(const ListPolyIt& other) const 51 | { 52 | return !(*this == other); 53 | } 54 | ListPolyIt& operator=(const ListPolyIt& other) 55 | { 56 | poly = other.poly; 57 | it = other.it; 58 | return *this; 59 | } 60 | //! move the iterator forward (and wrap around at the end) 61 | ListPolyIt& operator++() 62 | { 63 | ++it; 64 | if (it == poly->end()) { it = poly->begin(); } 65 | return *this; 66 | } 67 | //! move the iterator backward (and wrap around at the beginning) 68 | ListPolyIt& operator--() 69 | { 70 | if (it == poly->begin()) { it = poly->end(); } 71 | --it; 72 | return *this; 73 | } 74 | //! move the iterator forward (and wrap around at the end) 75 | ListPolyIt next() const 76 | { 77 | ListPolyIt ret(*this); 78 | ++ret; 79 | return ret; 80 | } 81 | //! move the iterator backward (and wrap around at the beginning) 82 | ListPolyIt prev() const 83 | { 84 | ListPolyIt ret(*this); 85 | --ret; 86 | return ret; 87 | } 88 | //! Remove this point from the list polygon 89 | void remove() const 90 | { 91 | poly->erase(it); 92 | } 93 | /*! 94 | * Convert Polygons to ListPolygons 95 | * 96 | * \param polys The polygons to convert 97 | * \param result The converted polygons 98 | */ 99 | static void convertPolygonsToLists(const Polygons& polys, ListPolygons& result); 100 | /*! 101 | * Convert Polygons to ListPolygons 102 | * 103 | * \param polys The polygons to convert 104 | * \param result The converted polygons 105 | */ 106 | static void convertPolygonToList(ConstPolygonRef poly, ListPolygon& result); 107 | /*! 108 | * Convert ListPolygons to Polygons 109 | * 110 | * \param list_polygons The polygons to convert 111 | * \param polygons The converted polygons 112 | */ 113 | static void convertListPolygonsToPolygons(const ListPolygons& list_polygons, Polygons& polygons); 114 | /*! 115 | * Convert ListPolygons to Polygons 116 | * 117 | * \param list_polygons The polygons to convert 118 | * \param polygons The converted polygons 119 | */ 120 | static void convertListPolygonToPolygon(const ListPolygon& list_polygon, PolygonRef polygon); 121 | 122 | /*! 123 | * Insert a point into a ListPolygon if it's not a duplicate of the point before or the point after. 124 | * 125 | * \param before Iterator to the point before the point to insert 126 | * \param after Iterator to the point after the point to insert 127 | * \param to_insert The point to insert into the ListPolygon in between \p before and \p after 128 | * \return Iterator to the newly inserted point, or \p before or \p after in case to_insert was already in the polygon 129 | */ 130 | static ListPolyIt insertPointNonDuplicate(const ListPolyIt before, const ListPolyIt after, const Point to_insert); 131 | }; 132 | 133 | 134 | }//namespace visualizer 135 | 136 | namespace std 137 | { 138 | /*! 139 | * Hash function for \ref ListPolyIt 140 | */ 141 | template <> 142 | struct hash 143 | { 144 | size_t operator()(const visualizer::ListPolyIt& lpi) const 145 | { 146 | return std::hash()(lpi.p()); 147 | } 148 | }; 149 | }//namespace std 150 | 151 | 152 | 153 | #endif//UTILS_LIST_POLY_IT_H 154 | -------------------------------------------------------------------------------- /src/utils/NoCopy.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_NO_COPY_H 2 | #define UTILS_NO_COPY_H 3 | 4 | /*! 5 | * Util class to base other objects off which should never be copied. 6 | * Is needed because C++ has an implicit copy constructor and assign operator when none are defined. 7 | */ 8 | class NoCopy 9 | { 10 | protected: 11 | NoCopy() {} 12 | 13 | private: 14 | /*! 15 | * Private copy constructor. 16 | * Cannot be called because it is private. 17 | */ 18 | NoCopy(const NoCopy&); 19 | 20 | /*! 21 | * Private assign operator. 22 | * Cannot be called because it is private. 23 | */ 24 | NoCopy& operator =(const NoCopy&); 25 | }; 26 | 27 | #endif // UTILS_NO_COPY_H -------------------------------------------------------------------------------- /src/utils/Point3.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #include "Point3.h" //The headers we're implementing. 5 | 6 | namespace visualizer 7 | { 8 | 9 | Point3 Point3::operator +(const Point3& p) const 10 | { 11 | return Point3(x + p.x, y + p.y, z + p.z); 12 | } 13 | 14 | Point3 Point3::operator -(const Point3& p) const 15 | { 16 | return Point3(x - p.x, y - p.y, z - p.z); 17 | } 18 | 19 | Point3 Point3::operator *(const Point3& p) const 20 | { 21 | return Point3(x * p.x, y * p.y, z * p.z); 22 | } 23 | 24 | Point3 Point3::operator /(const Point3& p) const 25 | { 26 | return Point3(x / p.x, y / p.y, z / p.z); 27 | } 28 | 29 | Point3& Point3::operator +=(const Point3& p) 30 | { 31 | x += p.x; 32 | y += p.y; 33 | z += p.z; 34 | return *this; 35 | } 36 | 37 | Point3& Point3::operator -=(const Point3& p) 38 | { 39 | x -= p.x; 40 | y -= p.y; 41 | z -= p.z; 42 | return *this; 43 | } 44 | 45 | Point3& Point3::operator *=(const Point3& p) 46 | { 47 | x *= p.x; 48 | y *= p.y; 49 | z *= p.z; 50 | return *this; 51 | } 52 | 53 | Point3& Point3::operator /=(const Point3& p) 54 | { 55 | x /= p.x; 56 | y /= p.y; 57 | z /= p.z; 58 | return *this; 59 | } 60 | 61 | bool Point3::operator ==(const Point3& p) const 62 | { 63 | return x == p.x && y == p.y && z == p.z; 64 | } 65 | 66 | bool Point3::operator !=(const Point3& p) const 67 | { 68 | return x != p.x || y != p.y || z != p.z; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/utils/Point3.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_POINT3_H 5 | #define UTILS_POINT3_H 6 | 7 | #include //For sqrt. 8 | #include //Auto-serialization. 9 | #include //For numeric_limits::min and max. 10 | #include //For int32_t and int64_t. 11 | #include // for operations on any arithmetic number type 12 | 13 | #include "Coord_t.h" 14 | 15 | 16 | namespace visualizer 17 | { 18 | 19 | class Point3 20 | { 21 | public: 22 | coord_t x,y,z; 23 | Point3() {} 24 | Point3(const coord_t _x, const coord_t _y, const coord_t _z): x(_x), y(_y), z(_z) {} 25 | 26 | Point3 operator +(const Point3& p) const; 27 | Point3 operator -(const Point3& p) const; 28 | Point3 operator *(const Point3& p) const; //!< Element-wise multiplication. For dot product, use .dot()! 29 | Point3 operator /(const Point3& p) const; 30 | template::value, num_t>::type> 31 | Point3 operator *(const num_t i) const 32 | { 33 | return Point3(x * i, y * i, z * i); 34 | } 35 | template::value, num_t>::type> 36 | Point3 operator /(const num_t i) const 37 | { 38 | return Point3(x / i, y / i, z / i); 39 | } 40 | 41 | Point3& operator +=(const Point3& p); 42 | Point3& operator -=(const Point3& p); 43 | Point3& operator *=(const Point3& p); 44 | Point3& operator /=(const Point3& p); 45 | template::value, num_t>::type> 46 | Point3& operator *=(const num_t i) 47 | { 48 | x *= i; 49 | y *= i; 50 | z *= i; 51 | return *this; 52 | } 53 | template::value, num_t>::type> 54 | Point3& operator /=(const num_t i) 55 | { 56 | x /= i; 57 | y /= i; 58 | z /= i; 59 | return *this; 60 | } 61 | 62 | bool operator==(const Point3& p) const; 63 | bool operator!=(const Point3& p) const; 64 | 65 | 66 | template 67 | friend 68 | std::basic_ostream& 69 | operator <<(std::basic_ostream& os, const Point3& p) 70 | { 71 | return os << "(" << p.x << ", " << p.y << ", " << p.z << ")"; 72 | } 73 | 74 | 75 | coord_t max() const 76 | { 77 | if (x > y && x > z) return x; 78 | if (y > z) return y; 79 | return z; 80 | } 81 | 82 | bool testLength(coord_t len) const 83 | { 84 | if (x > len || x < -len) 85 | return false; 86 | if (y > len || y < -len) 87 | return false; 88 | if (z > len || z < -len) 89 | return false; 90 | return vSize2() <= len*len; 91 | } 92 | 93 | coord_t vSize2() const 94 | { 95 | return x * x + y * y + z * z; 96 | } 97 | 98 | coord_t vSize() const 99 | { 100 | return sqrt(vSize2()); 101 | } 102 | 103 | double vSizeMM() const 104 | { 105 | double fx = INT2MM(x); 106 | double fy = INT2MM(y); 107 | double fz = INT2MM(z); 108 | return sqrt(fx*fx+fy*fy+fz*fz); 109 | } 110 | 111 | coord_t dot(const Point3& p) const 112 | { 113 | return x*p.x + y*p.y + z*p.z; 114 | } 115 | 116 | }; 117 | 118 | /*! 119 | * \brief Placeholder coordinate point (3D). 120 | * 121 | * Its value is something that is rarely used. 122 | */ 123 | static Point3 no_point3(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); 124 | 125 | template::value, num_t>::type> 126 | inline Point3 operator*(const num_t i, const Point3& rhs) 127 | { 128 | return rhs * i; 129 | } 130 | 131 | } 132 | 133 | #endif //UTILS_POINT3_H 134 | -------------------------------------------------------------------------------- /src/utils/PolygonsPointIndex.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017 Ultimaker B.V. 2 | 3 | 4 | #include "PolygonsPointIndex.h" 5 | 6 | namespace visualizer 7 | { 8 | 9 | bool PolygonsPointIndex::initialized() const 10 | { 11 | return polygons; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /src/utils/PolygonsPointIndex.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_POLYGONS_POINT_INDEX_H 5 | #define UTILS_POLYGONS_POINT_INDEX_H 6 | 7 | #include 8 | 9 | #include "IntPoint.h" 10 | #include "polygon.h" 11 | 12 | 13 | namespace visualizer 14 | { 15 | 16 | /*! 17 | * A class for iterating over the points in one of the polygons in a \ref Polygons object 18 | */ 19 | class PolygonsPointIndex 20 | { 21 | public: 22 | /*! 23 | * The polygons into which this index is indexing 24 | */ 25 | const Polygons* polygons; // (pointer to const polygons) 26 | unsigned int poly_idx; //!< The index of the polygon in \ref PolygonsPointIndex::polygons 27 | unsigned int point_idx; //!< The index of the point in the polygon in \ref PolygonsPointIndex::polygons 28 | PolygonsPointIndex() 29 | : polygons(nullptr) 30 | , poly_idx(0) 31 | , point_idx(0) 32 | { 33 | } 34 | PolygonsPointIndex(const Polygons* polygons, unsigned int poly_idx, unsigned int point_idx) 35 | : polygons(polygons) 36 | , poly_idx(poly_idx) 37 | , point_idx(point_idx) 38 | { 39 | } 40 | Point p() const 41 | { 42 | if (!polygons) 43 | { 44 | return Point(0, 0); 45 | } 46 | return (*polygons)[poly_idx][point_idx]; 47 | } 48 | 49 | /*! 50 | * \brief Returns whether this point is initialised. 51 | */ 52 | bool initialized() const; 53 | 54 | /*! 55 | * Get the polygon to which this PolygonsPointIndex refers 56 | */ 57 | ConstPolygonRef getPolygon() const 58 | { 59 | return (*polygons)[poly_idx]; 60 | } 61 | /*! 62 | * Test whether two iterators refer to the same polygon in the same polygon list. 63 | * 64 | * \param other The PolygonsPointIndex to test for equality 65 | * \return Wether the right argument refers to the same polygon in the same ListPolygon as the left argument. 66 | */ 67 | bool operator==(const PolygonsPointIndex& other) const 68 | { 69 | return polygons == other.polygons && poly_idx == other.poly_idx && point_idx == other.point_idx; 70 | } 71 | bool operator!=(const PolygonsPointIndex& other) const 72 | { 73 | return !(*this == other); 74 | } 75 | PolygonsPointIndex& operator=(const PolygonsPointIndex& other) 76 | { 77 | polygons = other.polygons; 78 | poly_idx = other.poly_idx; 79 | point_idx = other.point_idx; 80 | return *this; 81 | } 82 | //! move the iterator forward (and wrap around at the end) 83 | PolygonsPointIndex& operator++() 84 | { 85 | point_idx = (point_idx + 1) % (*polygons)[poly_idx].size(); 86 | return *this; 87 | } 88 | //! move the iterator backward (and wrap around at the beginning) 89 | PolygonsPointIndex& operator--() 90 | { 91 | if (point_idx == 0) 92 | { 93 | point_idx = (*polygons)[poly_idx].size(); 94 | } 95 | point_idx--; 96 | return *this; 97 | } 98 | //! move the iterator forward (and wrap around at the end) 99 | PolygonsPointIndex next() const 100 | { 101 | PolygonsPointIndex ret(*this); 102 | ++ret; 103 | return ret; 104 | } 105 | //! move the iterator backward (and wrap around at the beginning) 106 | PolygonsPointIndex prev() const 107 | { 108 | PolygonsPointIndex ret(*this); 109 | --ret; 110 | return ret; 111 | } 112 | }; 113 | 114 | 115 | }//namespace visualizer 116 | 117 | namespace std 118 | { 119 | /*! 120 | * Hash function for \ref PolygonsPointIndex 121 | */ 122 | template <> 123 | struct hash 124 | { 125 | size_t operator()(const visualizer::PolygonsPointIndex& lpi) const 126 | { 127 | return std::hash()(lpi.p()); 128 | } 129 | }; 130 | }//namespace std 131 | 132 | 133 | 134 | #endif//UTILS_POLYGONS_POINT_INDEX_H 135 | -------------------------------------------------------------------------------- /src/utils/PolygonsSegmentIndex.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_POLYGONS_SEGMENT_INDEX_H 5 | #define UTILS_POLYGONS_SEGMENT_INDEX_H 6 | 7 | #include 8 | 9 | #include "PolygonsPointIndex.h" 10 | 11 | namespace visualizer 12 | { 13 | 14 | /*! 15 | * A class for iterating over the points in one of the polygons in a \ref Polygons object 16 | */ 17 | class PolygonsSegmentIndex : public PolygonsPointIndex 18 | { 19 | public: 20 | PolygonsSegmentIndex() 21 | : PolygonsPointIndex() 22 | { 23 | } 24 | PolygonsSegmentIndex(const Polygons* polygons, unsigned int poly_idx, unsigned int point_idx) 25 | : PolygonsPointIndex(polygons, poly_idx, point_idx) 26 | {} 27 | 28 | Point from() const 29 | { 30 | return PolygonsPointIndex::p(); 31 | } 32 | 33 | Point to() const 34 | { 35 | return PolygonsSegmentIndex::next().p(); 36 | } 37 | }; 38 | 39 | 40 | }//namespace visualizer 41 | 42 | 43 | #endif//UTILS_POLYGONS_SEGMENT_INDEX_H 44 | -------------------------------------------------------------------------------- /src/utils/STLwriter.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #include "STLwriter.h" 5 | 6 | namespace visualizer { 7 | 8 | 9 | 10 | STLwriter::STLwriter(std::string filename, double scaler) 11 | : out(filename) 12 | , scaler(scaler) 13 | { 14 | out << "solid STLwriter\n"; 15 | } 16 | 17 | 18 | STLwriter::~STLwriter() 19 | { 20 | out << "endsolid STLwriter\n"; 21 | out.close(); 22 | } 23 | 24 | void STLwriter::writeTriangle(Point3 a, Point3 b, Point3 c) 25 | { 26 | if (a == b || b == c || c == a) return; 27 | a *= scaler; 28 | b *= scaler; 29 | c *= scaler; 30 | out << "facet normal 1 0 0\n"; 31 | out << " outer loop\n"; 32 | out << " vertex " << INT2MM(a.x) << " " << INT2MM(a.y) << " " << INT2MM(a.z) << "\n"; 33 | out << " vertex " << INT2MM(b.x) << " " << INT2MM(b.y) << " " << INT2MM(b.z) << "\n"; 34 | out << " vertex " << INT2MM(c.x) << " " << INT2MM(c.y) << " " << INT2MM(c.z) << "\n"; 35 | out << " endloop\n"; 36 | out << "endfacet\n"; 37 | } 38 | 39 | 40 | void STLwriter::writeQuad(Point3 low_a, Point3 high_a, Point3 low_b, Point3 high_b, coord_t discretization_dist) 41 | { 42 | Point3 a_vec = high_a - low_a; 43 | Point3 b_vec = high_b - low_b; 44 | coord_t a_length = a_vec.vSize(); 45 | coord_t b_length = b_vec.vSize(); 46 | coord_t step_count = std::max(coord_t(1), std::max(a_length, b_length) / discretization_dist); 47 | Point3 a_prev = low_a; 48 | Point3 b_prev = low_b; 49 | for (coord_t step = 0; step < step_count; step++) 50 | { 51 | Point3 a_now = low_a + a_vec * (step + 1) / step_count; 52 | Point3 b_now = low_b + b_vec * (step + 1) / step_count; 53 | writeTriangle(a_prev, b_prev, a_now); 54 | writeTriangle(a_now, b_prev, b_now); 55 | a_prev = a_now; 56 | b_prev = b_now; 57 | } 58 | } 59 | 60 | } // namespace visualizer 61 | -------------------------------------------------------------------------------- /src/utils/STLwriter.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef STL_WRITER_H 5 | #define STL_WRITER_H 6 | 7 | #include // for file output 8 | #include // for file output 9 | 10 | #include "AABB.h" 11 | #include "IntPoint.h" 12 | #include "NoCopy.h" 13 | 14 | namespace visualizer 15 | { 16 | 17 | 18 | class STLwriter : NoCopy 19 | { 20 | public: 21 | 22 | std::ofstream out; // the output file 23 | 24 | double scaler; 25 | 26 | STLwriter(std::string filename, double scaler = 10.0); 27 | ~STLwriter(); 28 | 29 | void writeTriangle(Point3 a, Point3 b, Point3 c); 30 | 31 | void writeQuad(Point3 low_a, Point3 high_a, Point3 low_b, Point3 high_b, coord_t discretization_dist = MM2INT(990.05)); 32 | }; 33 | 34 | } // namespace visualizer 35 | #endif // STL_WRITER_H 36 | -------------------------------------------------------------------------------- /src/utils/SVG.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017 Tim Kuipers 2 | //Copyright (c) 2018 Ultimaker B.V. 3 | 4 | #include 5 | 6 | #include "floatpoint.h" 7 | #include "logoutput.h" 8 | #include "polygon.h" 9 | #include "SVG.h" 10 | 11 | namespace visualizer { 12 | 13 | 14 | 15 | std::string SVG::toString(Color color) 16 | { 17 | switch (color) 18 | { 19 | case SVG::Color::BLACK: return "black"; 20 | case SVG::Color::WHITE: return "white"; 21 | case SVG::Color::GRAY: return "gray"; 22 | case SVG::Color::RED: return "red"; 23 | case SVG::Color::BLUE: return "blue"; 24 | case SVG::Color::GREEN: return "green"; 25 | case SVG::Color::ORANGE: return "orange"; 26 | case SVG::Color::MAGENTA: return "magenta"; 27 | case SVG::Color::YELLOW: return "yellow"; 28 | case SVG::Color::NONE: return "none"; 29 | default: return "black"; 30 | } 31 | } 32 | 33 | std::string SVG::toString(ColorObject& color) 34 | { 35 | if (color.is_enum) return toString(color.color); 36 | else 37 | { 38 | std::ostringstream ss; 39 | ss << "rgb(" << color.r << "," << color.g << "," << color.b << ")"; 40 | return ss.str(); 41 | } 42 | } 43 | 44 | 45 | SVG::SVG(std::string filename, AABB aabb, Point canvas_size, ColorObject background) 46 | : SVG(filename, aabb, std::min(double(canvas_size.X - canvas_size.X / 5 * 2) / (aabb.max.X - aabb.min.X), double(canvas_size.Y - canvas_size.Y / 5) / (aabb.max.Y - aabb.min.Y)), canvas_size, background) 47 | { 48 | } 49 | 50 | SVG::SVG(std::string filename, AABB aabb, double scale, ColorObject background) 51 | : SVG(filename, aabb, scale, (aabb.max - aabb.min) * scale, background) 52 | { 53 | } 54 | 55 | SVG::SVG(std::string filename, AABB aabb, double scale, Point canvas_size, ColorObject background) 56 | : aabb(aabb) 57 | , aabb_size(aabb.max - aabb.min) 58 | , canvas_size(canvas_size) 59 | , scale(scale) 60 | , background(background) 61 | { 62 | output_is_html = strcmp(filename.c_str() + strlen(filename.c_str()) - 4, "html") == 0; 63 | out = fopen(filename.c_str(), "w"); 64 | if(!out) 65 | { 66 | logError("The file %s could not be opened for writing.",filename.c_str()); 67 | } 68 | if (output_is_html) 69 | { 70 | fprintf(out, "\n"); 71 | } 72 | else 73 | { 74 | fprintf(out, "\n"); 75 | } 76 | fprintf(out, "\n"); 87 | fprintf(out," inkscape:export-xdpi=\"100\"\n"); 88 | fprintf(out," inkscape:export-ydpi=\"100\"\n"); 89 | fprintf(out," \n"); 91 | fprintf(out," \n"); 92 | fprintf(out," \n"); 94 | fprintf(out," image/svg+xml\n"); 95 | fprintf(out," \n"); 97 | fprintf(out," \n"); 98 | fprintf(out," \n"); 99 | fprintf(out," \n"); 100 | fprintf(out," \n"); 101 | fprintf(out," \n", layer_nr ); 105 | 106 | if (!background.is_enum || background.color != Color::NONE) 107 | { 108 | fprintf(out, "\n", toString(background).c_str()); 109 | } 110 | 111 | } 112 | 113 | SVG::~SVG() 114 | { 115 | fprintf(out, " \n"); 116 | fprintf(out, "\n"); 117 | if (output_is_html) 118 | { 119 | fprintf(out, ""); 120 | } 121 | fclose(out); 122 | } 123 | 124 | double SVG::getScale() const 125 | { 126 | return scale; 127 | } 128 | 129 | void SVG::nextLayer() 130 | { 131 | fprintf(out, " \n"); 132 | layer_nr++; 133 | fprintf(out," \n", layer_nr ); 137 | } 138 | 139 | Point SVG::transform(const Point& p) 140 | { 141 | return Point((p.X - aabb.min.X) * scale, (p.Y - aabb.min.Y) * scale); 142 | } 143 | 144 | FPoint3 SVG::transformF(const Point& p) 145 | { 146 | return FPoint3((p.X - aabb.min.X) * scale, (p.Y-aabb.min.Y) * scale, 0.0); 147 | } 148 | 149 | void SVG::writeComment(std::string comment) 150 | { 151 | fprintf(out, "\n", comment.c_str()); 152 | } 153 | 154 | void SVG::writeAreas(const Polygons& polygons, ColorObject color, ColorObject outline_color, float stroke_width) 155 | { 156 | auto parts = polygons.splitIntoParts(); 157 | for (auto part_it = parts.rbegin(); part_it != parts.rend(); ++part_it) 158 | { 159 | PolygonsPart& parts = *part_it; 160 | for (unsigned int j = 0; j < parts.size(); j++) 161 | { 162 | fprintf(out, "\n", toString(color).c_str(), toString(outline_color).c_str(), stroke_width); 170 | else 171 | fprintf(out, "\" style=\"fill:white;stroke:%s;stroke-width:%f\" />\n", toString(outline_color).c_str(), stroke_width); 172 | } 173 | } 174 | } 175 | 176 | void SVG::writeAreas(ConstPolygonRef polygon, ColorObject color, ColorObject outline_color, float stroke_width) 177 | { 178 | fprintf(out,"\n"); //The end of the polygon tag. 185 | } 186 | 187 | void SVG::writePoint(const Point& p, bool write_coords, float size, ColorObject color) 188 | { 189 | FPoint3 pf = transformF(p); 190 | fprintf(out, "\n",pf.x, pf.y, size, toString(color).c_str(), toString(color).c_str()); 191 | 192 | if (write_coords) 193 | { 194 | fprintf(out, "%lli,%lli\n",pf.x, pf.y, p.X, p.Y); 195 | } 196 | } 197 | 198 | void SVG::writePoints(ConstPolygonRef poly, bool write_coords, float size, ColorObject color) 199 | { 200 | for (const Point& p : poly) 201 | { 202 | writePoint(p, write_coords, size, color); 203 | } 204 | } 205 | 206 | void SVG::writePoints(Polygons& polygons, bool write_coords, float size, ColorObject color) 207 | { 208 | for (PolygonRef poly : polygons) 209 | { 210 | writePoints(poly, write_coords, size, color); 211 | } 212 | } 213 | 214 | void SVG::writeLines(std::vector polyline, ColorObject color) 215 | { 216 | if(polyline.size() <= 1) //Need at least 2 points. 217 | { 218 | return; 219 | } 220 | 221 | FPoint3 transformed = transformF(polyline[0]); //Element 0 must exist due to the check above. 222 | fprintf(out,"\n"); //Write the end of the tag. 229 | } 230 | 231 | void SVG::writeLine(const Point& a, const Point& b, ColorObject color, float stroke_width) 232 | { 233 | FPoint3 fa = transformF(a); 234 | FPoint3 fb = transformF(b); 235 | fprintf(out, "\n", fa.x, fa.y, fb.x, fb.y, toString(color).c_str(), stroke_width); 236 | } 237 | 238 | void SVG::writeArrow(const Point& a, const Point& b, ColorObject color, float stroke_width, int rel_head_size_divisor, coord_t offset) 239 | { 240 | Point ab = b - a; 241 | Point nd = turn90CCW(ab) / rel_head_size_divisor / 2; 242 | Point n = normal(turn90CCW(ab), offset); 243 | Point d = ab / rel_head_size_divisor / 2; 244 | writeLine(a + n, b + n - d, color, stroke_width); 245 | writeLine(b + n - d, b + n + nd - d * 3, color, stroke_width); 246 | } 247 | 248 | void SVG::writeLineRGB(const Point& from, const Point& to, int r, int g, int b, float stroke_width) 249 | { 250 | FPoint3 fa = transformF(from); 251 | FPoint3 fb = transformF(to); 252 | fprintf(out, "\n", fa.x, fa.y, fb.x, fb.y, r, g, b, stroke_width); 253 | } 254 | 255 | void SVG::writeDashedLine(const Point& a, const Point& b, ColorObject color) 256 | { 257 | FPoint3 fa = transformF(a); 258 | FPoint3 fb = transformF(b); 259 | fprintf(out,"\n",fa.x,fa.y,fb.x,fb.y,toString(color).c_str()); 260 | } 261 | 262 | void SVG::writeText(Point p, std::string txt, ColorObject color, coord_t font_size) 263 | { 264 | FPoint3 pf = transformF(p); 265 | fprintf(out, "%s\n",pf.x, pf.y, font_size, toString(color).c_str(), txt.c_str()); 266 | } 267 | 268 | void SVG::writePolygons(const Polygons& polys, ColorObject color, float stroke_width) 269 | { 270 | for (ConstPolygonRef poly : polys) 271 | { 272 | writePolygon(poly, color, stroke_width); 273 | } 274 | } 275 | 276 | void SVG::writePolygon(ConstPolygonRef poly, ColorObject color, float stroke_width) 277 | { 278 | if (poly.size() == 0) 279 | { 280 | return; 281 | } 282 | int size = poly.size(); 283 | Point p0 = poly.back(); 284 | int i = 0; 285 | for (Point p1 : poly) 286 | { 287 | if (color.color == Color::RAINBOW) 288 | { 289 | int g = (i * 255 * 11 / size) % (255 * 2); 290 | if (g > 255) g = 255 * 2 - g; 291 | int b = (i * 255 * 5 / size) % (255 * 2); 292 | if (b > 255) b = 255 * 2 - b; 293 | writeLineRGB(p0, p1, i * 255 / size, g, b, stroke_width); 294 | } 295 | else 296 | { 297 | writeLine(p0, p1, color, stroke_width); 298 | } 299 | p0 = p1; 300 | i++; 301 | } 302 | } 303 | 304 | 305 | void SVG::writePolylines(const Polygons& polys, ColorObject color, float stroke_width) 306 | { 307 | for (ConstPolygonRef poly : polys) 308 | { 309 | writePolyline(poly, color, stroke_width); 310 | } 311 | } 312 | 313 | void SVG::writePolyline(ConstPolygonRef poly, ColorObject color, float stroke_width) 314 | { 315 | if (poly.size() == 0) 316 | { 317 | return; 318 | } 319 | int size = poly.size(); 320 | Point p0 = poly[0]; 321 | int i = 0; 322 | for (coord_t p_idx = 1; p_idx < poly.size(); p_idx++) 323 | { 324 | Point p1 = poly[p_idx]; 325 | if (color.color == Color::RAINBOW) 326 | { 327 | int g = (i * 255 * 11 / size) % (255 * 2); 328 | if (g > 255) g = 255 * 2 - g; 329 | int b = (i * 255 * 5 / size) % (255 * 2); 330 | if (b > 255) b = 255 * 2 - b; 331 | writeLineRGB(p0, p1, i * 255 / size, g, b, stroke_width); 332 | } 333 | else 334 | { 335 | writeLine(p0, p1, color, stroke_width); 336 | } 337 | p0 = p1; 338 | i++; 339 | } 340 | } 341 | 342 | } // namespace visualizer 343 | -------------------------------------------------------------------------------- /src/utils/SVG.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef SVG_H 5 | #define SVG_H 6 | 7 | #include // for file output 8 | 9 | #include "AABB.h" 10 | #include "IntPoint.h" 11 | #include "NoCopy.h" 12 | 13 | namespace visualizer 14 | { 15 | 16 | class FPoint3; 17 | 18 | class SVG : NoCopy 19 | { 20 | public: 21 | enum class Color { 22 | BLACK, 23 | WHITE, 24 | GRAY, 25 | RED, 26 | BLUE, 27 | GREEN, 28 | ORANGE, 29 | MAGENTA, 30 | YELLOW, 31 | RAINBOW, 32 | NONE 33 | }; 34 | 35 | struct ColorObject 36 | { 37 | bool is_enum; 38 | Color color; 39 | int r, g, b; 40 | ColorObject(Color color) 41 | : is_enum(true) 42 | , color(color) 43 | {} 44 | ColorObject(int r, int g, int b) 45 | : is_enum(false) 46 | , r(r) 47 | , g(g) 48 | , b(b) 49 | {} 50 | }; 51 | private: 52 | 53 | std::string toString(Color color); 54 | std::string toString(ColorObject& color); 55 | 56 | FILE* out; // the output file 57 | const AABB aabb; // the boundary box to display 58 | const Point aabb_size; 59 | const Point canvas_size; 60 | const double scale; 61 | ColorObject background; 62 | size_t layer_nr = 1; 63 | 64 | bool output_is_html; 65 | 66 | public: 67 | SVG(std::string filename, AABB aabb, Point canvas_size = Point(1024, 1024), ColorObject background = Color::NONE); 68 | SVG(std::string filename, AABB aabb, double scale, ColorObject background = Color::NONE); 69 | SVG(std::string filename, AABB aabb, double scale, Point canvas_size, ColorObject background = Color::NONE); 70 | 71 | ~SVG(); 72 | 73 | /*! 74 | * get the scaling factor applied to convert real space to canvas space 75 | */ 76 | double getScale() const; 77 | 78 | void nextLayer(); 79 | 80 | /*! 81 | * transform a point in real space to canvas space 82 | */ 83 | Point transform(const Point& p); 84 | 85 | /*! 86 | * transform a point in real space to canvas space with more precision 87 | */ 88 | FPoint3 transformF(const Point& p); 89 | 90 | void writeComment(std::string comment); 91 | 92 | void writeAreas(const Polygons& polygons, ColorObject color = Color::GRAY, ColorObject outline_color = Color::BLACK, float stroke_width = 1); 93 | 94 | void writeAreas(ConstPolygonRef polygon, ColorObject color = Color::GRAY, ColorObject outline_color = Color::BLACK, float stroke_width = 1); 95 | 96 | void writePoint(const Point& p, bool write_coords=false, float size = 5, ColorObject color = Color::BLACK); 97 | 98 | void writePoints(ConstPolygonRef poly, bool write_coords=false, float size = 5, ColorObject color = Color::BLACK); 99 | 100 | void writePoints(Polygons& polygons, bool write_coords=false, float size = 5, ColorObject color = Color::BLACK); 101 | 102 | /*! 103 | * \brief Draws a polyline on the canvas. 104 | * 105 | * The polyline is the set of line segments between each pair of consecutive 106 | * points in the specified vector. 107 | * 108 | * \param polyline A set of points between which line segments must be 109 | * drawn. 110 | * \param color The colour of the line segments. If this is not specified, 111 | * black will be used. 112 | */ 113 | void writeLines(std::vector polyline, ColorObject color = Color::BLACK); 114 | 115 | void writeLine(const Point& a, const Point& b, ColorObject color = Color::BLACK, float stroke_width = 1); 116 | 117 | void writeArrow(const Point& a, const Point& b, ColorObject color = Color::BLACK, float stroke_width = 1, int rel_head_size_divisor = 20, coord_t offset = 20); 118 | 119 | void writeLineRGB(const Point& from, const Point& to, int r = 0, int g = 0, int b = 0, float stroke_width = 1); 120 | 121 | /*! 122 | * \brief Draws a dashed line on the canvas from point A to point B. 123 | * 124 | * This is useful in the case where multiple lines may overlap each other. 125 | * 126 | * \param a The starting endpoint of the line. 127 | * \param b The ending endpoint of the line. 128 | * \param color The stroke colour of the line. 129 | */ 130 | void writeDashedLine(const Point& a,const Point& b, ColorObject color = Color::BLACK); 131 | 132 | template 133 | void printf(const char* txt, Args&&... args); 134 | 135 | void writeText(Point p, std::string txt, ColorObject color = Color::BLACK, coord_t font_size = 10); 136 | 137 | void writePolygons(const Polygons& polys, ColorObject color = Color::BLACK, float stroke_width = 1); 138 | 139 | void writePolygon(ConstPolygonRef poly, ColorObject color = Color::BLACK, float stroke_width = 1); 140 | 141 | void writePolylines(const Polygons& polys, ColorObject color = Color::BLACK, float stroke_width = 1); 142 | 143 | void writePolyline(ConstPolygonRef poly, ColorObject color = Color::BLACK, float stroke_width = 1); 144 | 145 | }; 146 | 147 | template 148 | void SVG::printf(const char* txt, Args&&... args) 149 | { 150 | fprintf(out, txt, args...); 151 | } 152 | 153 | } // namespace visualizer 154 | #endif // SVG_H 155 | -------------------------------------------------------------------------------- /src/utils/SparseLineGrid.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | 5 | #ifndef UTILS_SPARSE_LINE_GRID_H 6 | #define UTILS_SPARSE_LINE_GRID_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "IntPoint.h" 14 | #include "SparseGrid.h" 15 | #include "SVG.h" // debug 16 | 17 | namespace visualizer { 18 | 19 | /*! \brief Sparse grid which can locate spatially nearby elements efficiently. 20 | * 21 | * \tparam ElemT The element type to store. 22 | * \tparam Locator The functor to get the start and end locations from ElemT. 23 | * must have: std::pair operator()(const ElemT &elem) const 24 | * which returns the location associated with val. 25 | */ 26 | template 27 | class SparseLineGrid : public SparseGrid 28 | { 29 | public: 30 | using Elem = ElemT; 31 | 32 | /*! \brief Constructs a sparse grid with the specified cell size. 33 | * 34 | * \param[in] cell_size The size to use for a cell (square) in the grid. 35 | * Typical values would be around 0.5-2x of expected query radius. 36 | * \param[in] elem_reserve Number of elements to research space for. 37 | * \param[in] max_load_factor Maximum average load factor before rehashing. 38 | */ 39 | SparseLineGrid(coord_t cell_size, size_t elem_reserve = 0U, float max_load_factor = 1.0f); 40 | 41 | /*! \brief Inserts elem into the sparse grid. 42 | * 43 | * \param[in] elem The element to be inserted. 44 | */ 45 | void insert(const Elem &elem); 46 | 47 | void debugHTML(std::string filename); 48 | 49 | static void debugTest(); 50 | protected: 51 | using GridPoint = typename SparseGrid::GridPoint; 52 | using grid_coord_t = typename SparseGrid::grid_coord_t; 53 | 54 | /*! \brief Accessor for getting locations from elements. */ 55 | Locator m_locator; 56 | }; 57 | 58 | 59 | 60 | #define SGI_TEMPLATE template 61 | #define SGI_THIS SparseLineGrid 62 | 63 | SGI_TEMPLATE 64 | SGI_THIS::SparseLineGrid(coord_t cell_size, size_t elem_reserve, float max_load_factor) 65 | : SparseGrid(cell_size, elem_reserve, max_load_factor) 66 | { 67 | } 68 | 69 | SGI_TEMPLATE 70 | void SGI_THIS::insert(const Elem &elem) 71 | { 72 | const std::pair line = m_locator(elem); 73 | using GridMap = std::unordered_multimap; 74 | // below is a workaround for the fact that lambda functions cannot access private or protected members 75 | // first we define a lambda which works on any GridMap and then we bind it to the actual protected GridMap of the parent class 76 | std::function process_cell_func_ = [&elem, this](GridMap* m_grid, const GridPoint grid_loc) 77 | { 78 | m_grid->emplace(grid_loc, elem); 79 | return true; 80 | }; 81 | using namespace std::placeholders; // for _1, _2, _3... 82 | GridMap* m_grid = &(this->m_grid); 83 | std::function process_cell_func(std::bind(process_cell_func_, m_grid, _1)); 84 | 85 | SparseGrid::processLineCells(line, process_cell_func); 86 | } 87 | 88 | SGI_TEMPLATE 89 | void SGI_THIS::debugHTML(std::string filename) 90 | { 91 | AABB aabb; 92 | for (std::pair cell: SparseGrid::m_grid) 93 | { 94 | aabb.include(SparseGrid::toLowerCorner(cell.first)); 95 | aabb.include(SparseGrid::toLowerCorner(cell.first + GridPoint(SparseGrid::nonzero_sign(cell.first.X), SparseGrid::nonzero_sign(cell.first.Y)))); 96 | } 97 | SVG svg(filename.c_str(), aabb); 98 | for (std::pair cell: SparseGrid::m_grid) 99 | { 100 | // doesn't draw cells at x = 0 or y = 0 correctly (should be double size) 101 | Point lb = SparseGrid::toLowerCorner(cell.first); 102 | Point lt = SparseGrid::toLowerCorner(cell.first + GridPoint(0, SparseGrid::nonzero_sign(cell.first.Y))); 103 | Point rt = SparseGrid::toLowerCorner(cell.first + GridPoint(SparseGrid::nonzero_sign(cell.first.X), SparseGrid::nonzero_sign(cell.first.Y))); 104 | Point rb = SparseGrid::toLowerCorner(cell.first + GridPoint(SparseGrid::nonzero_sign(cell.first.X), 0)); 105 | if (lb.X == 0) 106 | { 107 | lb.X = -SparseGrid::m_cell_size; 108 | lt.X = -SparseGrid::m_cell_size; 109 | } 110 | if (lb.Y == 0) 111 | { 112 | lb.Y = -SparseGrid::m_cell_size; 113 | rb.Y = -SparseGrid::m_cell_size; 114 | } 115 | // svg.writePoint(lb, true, 1); 116 | svg.writeLine(lb, lt, SVG::Color::GRAY); 117 | svg.writeLine(lt, rt, SVG::Color::GRAY); 118 | svg.writeLine(rt, rb, SVG::Color::GRAY); 119 | svg.writeLine(rb, lb, SVG::Color::GRAY); 120 | 121 | std::pair line = m_locator(cell.second); 122 | svg.writePoint(line.first, true); 123 | svg.writePoint(line.second, true); 124 | svg.writeLine(line.first, line.second, SVG::Color::BLACK); 125 | } 126 | } 127 | 128 | SGI_TEMPLATE 129 | void SGI_THIS::debugTest() 130 | { 131 | struct PairLocator 132 | { 133 | std::pair operator()(const std::pair& val) const 134 | { 135 | return val; 136 | } 137 | }; 138 | SparseLineGrid, PairLocator> line_grid(10); 139 | 140 | // straight lines 141 | line_grid.insert(std::make_pair(Point(50, 0), Point(50, 70))); 142 | line_grid.insert(std::make_pair(Point(0, 90), Point(50, 90))); 143 | line_grid.insert(std::make_pair(Point(253, 103), Point(253, 173))); 144 | line_grid.insert(std::make_pair(Point(203, 193), Point(253, 193))); 145 | line_grid.insert(std::make_pair(Point(-50, 0), Point(-50, -70))); 146 | line_grid.insert(std::make_pair(Point(0, -90), Point(-50, -90))); 147 | line_grid.insert(std::make_pair(Point(-253, -103), Point(-253, -173))); 148 | line_grid.insert(std::make_pair(Point(-203, -193), Point(-253, -193))); 149 | 150 | // diagonal lines 151 | line_grid.insert(std::make_pair(Point(113, 133), Point(166, 125))); 152 | line_grid.insert(std::make_pair(Point(13, 73), Point(26, 25))); 153 | line_grid.insert(std::make_pair(Point(166, 33), Point(113, 25))); 154 | line_grid.insert(std::make_pair(Point(26, 173), Point(13, 125))); 155 | line_grid.insert(std::make_pair(Point(-24, -18), Point(-19, -64))); 156 | line_grid.insert(std::make_pair(Point(-113, -133), Point(-166, -125))); 157 | line_grid.insert(std::make_pair(Point(-166, -33), Point(-113, -25))); 158 | line_grid.insert(std::make_pair(Point(-26, -173), Point(-13, -125))); 159 | 160 | // diagonal lines exactly crossing cell corners 161 | line_grid.insert(std::make_pair(Point(160, 190), Point(220, 170))); 162 | line_grid.insert(std::make_pair(Point(60, 130), Point(80, 70))); 163 | line_grid.insert(std::make_pair(Point(220, 90), Point(160, 70))); 164 | line_grid.insert(std::make_pair(Point(80, 220), Point(60, 160))); 165 | line_grid.insert(std::make_pair(Point(-160, -190), Point(-220, -170))); 166 | line_grid.insert(std::make_pair(Point(-60, -130), Point(-80, -70))); 167 | line_grid.insert(std::make_pair(Point(-220, -90), Point(-160, -70))); 168 | line_grid.insert(std::make_pair(Point(-80, -220), Point(-60, -160))); 169 | 170 | // single cell 171 | line_grid.insert(std::make_pair(Point(203, 213), Point(203, 213))); 172 | line_grid.insert(std::make_pair(Point(223, 213), Point(223, 215))); 173 | line_grid.insert(std::make_pair(Point(243, 213), Point(245, 213))); 174 | line_grid.insert(std::make_pair(Point(263, 213), Point(265, 215))); 175 | line_grid.insert(std::make_pair(Point(283, 215), Point(285, 213))); 176 | line_grid.insert(std::make_pair(Point(-203, -213), Point(-203, -213))); 177 | 178 | // around origin 179 | line_grid.insert(std::make_pair(Point(20, -20), Point(-20, 20))); 180 | 181 | line_grid.debugHTML("line_grid.html"); 182 | } 183 | 184 | 185 | #undef SGI_TEMPLATE 186 | #undef SGI_THIS 187 | 188 | } // namespace visualizer 189 | 190 | #endif // UTILS_SPARSE_LINE_GRID_H 191 | -------------------------------------------------------------------------------- /src/utils/SparsePointGrid.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2016 Scott Lenser 2 | //Copyright (c) 2018 Ultimaker B.V. 3 | 4 | 5 | #ifndef UTILS_SPARSE_POINT_GRID_H 6 | #define UTILS_SPARSE_POINT_GRID_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "IntPoint.h" 13 | #include "SparseGrid.h" 14 | 15 | namespace visualizer { 16 | 17 | /*! \brief Sparse grid which can locate spatially nearby elements efficiently. 18 | * 19 | * \tparam ElemT The element type to store. 20 | * \tparam Locator The functor to get the location from ElemT. Locator 21 | * must have: Point operator()(const ElemT &elem) const 22 | * which returns the location associated with val. 23 | */ 24 | template 25 | class SparsePointGrid : public SparseGrid 26 | { 27 | public: 28 | using Elem = ElemT; 29 | 30 | /*! \brief Constructs a sparse grid with the specified cell size. 31 | * 32 | * \param[in] cell_size The size to use for a cell (square) in the grid. 33 | * Typical values would be around 0.5-2x of expected query radius. 34 | * \param[in] elem_reserve Number of elements to research space for. 35 | * \param[in] max_load_factor Maximum average load factor before rehashing. 36 | */ 37 | SparsePointGrid(coord_t cell_size, size_t elem_reserve=0U, float max_load_factor=1.0f); 38 | 39 | /*! \brief Inserts elem into the sparse grid. 40 | * 41 | * \param[in] elem The element to be inserted. 42 | */ 43 | void insert(const Elem &elem); 44 | 45 | const ElemT* getAnyNearby(const Point& query_pt, coord_t radius); 46 | 47 | protected: 48 | using GridPoint = typename SparseGrid::GridPoint; 49 | 50 | /*! \brief Accessor for getting locations from elements. */ 51 | Locator m_locator; 52 | }; 53 | 54 | 55 | 56 | #define SGI_TEMPLATE template 57 | #define SGI_THIS SparsePointGrid 58 | 59 | SGI_TEMPLATE 60 | SGI_THIS::SparsePointGrid(coord_t cell_size, size_t elem_reserve, float max_load_factor) 61 | : SparseGrid(cell_size, elem_reserve, max_load_factor) 62 | { 63 | } 64 | 65 | SGI_TEMPLATE 66 | void SGI_THIS::insert(const Elem &elem) 67 | { 68 | Point loc = m_locator(elem); 69 | GridPoint grid_loc = SparseGrid::toGridPoint(loc); 70 | 71 | SparseGrid::m_grid.emplace(grid_loc,elem); 72 | } 73 | 74 | SGI_TEMPLATE 75 | const ElemT* SGI_THIS::getAnyNearby(const Point& query_pt, coord_t radius) 76 | { 77 | const ElemT* ret = nullptr; 78 | const std::function& process_func = [&ret, query_pt, radius, this](const ElemT& maybe_nearby) 79 | { 80 | if (shorterThen(m_locator(maybe_nearby) - query_pt, radius)) 81 | { 82 | ret = &maybe_nearby; 83 | return false; 84 | } 85 | return true; 86 | }; 87 | SparseGrid::processNearby(query_pt, radius, process_func); 88 | 89 | return ret; 90 | } 91 | 92 | 93 | #undef SGI_TEMPLATE 94 | #undef SGI_THIS 95 | 96 | } // namespace visualizer 97 | 98 | #endif // UTILS_SPARSE_POINT_GRID_H 99 | -------------------------------------------------------------------------------- /src/utils/SparsePointGridInclusive.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2016 Scott Lenser 2 | //Copyright (c) 2018 Ultimaker B.V. 3 | 4 | 5 | #ifndef UTILS_SPARSE_POINT_GRID_INCLUSIVE_H 6 | #define UTILS_SPARSE_POINT_GRID_INCLUSIVE_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "IntPoint.h" 13 | #include "SparsePointGrid.h" 14 | 15 | namespace visualizer { 16 | 17 | 18 | namespace SparsePointGridInclusiveImpl { 19 | 20 | template 21 | struct SparsePointGridInclusiveElem 22 | { 23 | SparsePointGridInclusiveElem() 24 | { 25 | } 26 | 27 | SparsePointGridInclusiveElem(const Point &point_, const Val &val_) : 28 | point(point_), 29 | val(val_) 30 | { 31 | } 32 | 33 | Point point; 34 | Val val; 35 | }; 36 | 37 | template 38 | struct Locatoror 39 | { 40 | Point operator()(const SparsePointGridInclusiveElem &elem) 41 | { 42 | return elem.point; 43 | } 44 | }; 45 | 46 | } // namespace SparseGridImpl 47 | 48 | /*! \brief Sparse grid which can locate spatially nearby values efficiently. 49 | * 50 | * \tparam Val The value type to store. 51 | */ 52 | template 53 | class SparsePointGridInclusive : public SparsePointGrid, 54 | SparsePointGridInclusiveImpl::Locatoror > 55 | { 56 | public: 57 | using Base = SparsePointGrid, 58 | SparsePointGridInclusiveImpl::Locatoror >; 59 | 60 | /*! \brief Constructs a sparse grid with the specified cell size. 61 | * 62 | * \param[in] cell_size The size to use for a cell (square) in the grid. 63 | * Typical values would be around 0.5-2x of expected query radius. 64 | * \param[in] elem_reserve Number of elements to research space for. 65 | * \param[in] max_load_factor Maximum average load factor before rehashing. 66 | */ 67 | SparsePointGridInclusive(coord_t cell_size, size_t elem_reserve=0U, float max_load_factor=1.0f); 68 | 69 | /*! \brief Inserts an element with specified point and value into the sparse grid. 70 | * 71 | * This is a convenience wrapper over \ref SparsePointGrid::insert() 72 | * 73 | * \param[in] point The location for the element. 74 | * \param[in] val The value for the element. 75 | */ 76 | void insert(const Point &point, const Val &val); 77 | 78 | /*! \brief Returns all values within radius of query_pt. 79 | * 80 | * Finds all values with location within radius of \p query_pt. May 81 | * return additional values that are beyond radius. 82 | * 83 | * See \ref getNearby(). 84 | * 85 | * \param[in] query_pt The point to search around. 86 | * \param[in] radius The search radius. 87 | * \return Vector of values found 88 | */ 89 | std::vector getNearbyVals(const Point &query_pt, coord_t radius) const; 90 | 91 | }; 92 | 93 | #define SG_TEMPLATE template 94 | #define SG_THIS SparsePointGridInclusive 95 | 96 | SG_TEMPLATE 97 | SG_THIS::SparsePointGridInclusive(coord_t cell_size, size_t elem_reserve, float max_load_factor) : 98 | Base(cell_size,elem_reserve,max_load_factor) 99 | { 100 | } 101 | 102 | SG_TEMPLATE 103 | void SG_THIS::insert(const Point &point, const Val &val) 104 | { 105 | typename SG_THIS::Elem elem(point,val); 106 | Base::insert(elem); 107 | } 108 | 109 | SG_TEMPLATE 110 | std::vector 111 | SG_THIS::getNearbyVals(const Point &query_pt, coord_t radius) const 112 | { 113 | std::vector ret; 114 | std::function&)> process_func = [&ret](const typename SG_THIS::Elem &elem) 115 | { 116 | ret.push_back(elem.val); 117 | return true; 118 | }; 119 | this->processNearby(query_pt, radius, process_func); 120 | return ret; 121 | } 122 | 123 | 124 | #undef SG_TEMPLATE 125 | #undef SG_THIS 126 | 127 | } // namespace visualizer 128 | 129 | #endif // UTILS_SPARSE_POINT_GRID_INCLUSIVE_H 130 | -------------------------------------------------------------------------------- /src/utils/SymmetricPair.h: -------------------------------------------------------------------------------- 1 | /** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */ 2 | #ifndef UTILS_SYMMETRIC_PAIR 3 | #define UTILS_SYMMETRIC_PAIR 4 | 5 | #include // pair 6 | 7 | namespace visualizer 8 | { 9 | 10 | /*! 11 | * A utility class for a pair of which the order between the first and the second doesn't matter. 12 | * 13 | * \tparam A The type of both elements of the pair. 14 | */ 15 | template 16 | class SymmetricPair : public std::pair 17 | { 18 | public: 19 | /*! 20 | * Forwarding std::pair constructor 21 | */ 22 | template 23 | SymmetricPair(const SymmetricPair& pr) 24 | : std::pair(pr) 25 | { 26 | } 27 | /*! 28 | * Forwarding std::pair constructor 29 | */ 30 | template 31 | SymmetricPair(SymmetricPair&& pr) 32 | : std::pair(pr) 33 | { 34 | } 35 | /*! 36 | * Forwarding std::pair constructor 37 | */ 38 | SymmetricPair(const A& first, const A& second) 39 | : std::pair(first, second) 40 | { 41 | } 42 | /*! 43 | * Forwarding std::pair constructor 44 | */ 45 | template 46 | SymmetricPair(U&& first, U&& second) 47 | : std::pair(first, second) 48 | { 49 | } 50 | /*! 51 | * Forwarding std::pair constructor 52 | */ 53 | template 54 | SymmetricPair(std::piecewise_construct_t pwc, std::tuple first_args, std::tuple second_args) 55 | : std::pair(pwc, first_args, second_args) 56 | { 57 | } 58 | 59 | /*! 60 | * Equality operator which checks if two SymmetricPairs are equal regardless of the order between first and second 61 | */ 62 | bool operator==(const SymmetricPair& other) const 63 | { 64 | return (std::pair::first == other.first && std::pair::second == other.second) || (std::pair::first == other.second && std::pair::second == other.first); 65 | } 66 | }; 67 | 68 | }//namespace visualizer 69 | 70 | namespace std 71 | { 72 | 73 | /*! 74 | * Hash operator which creates a hash regardless of the order between first and second 75 | */ 76 | template 77 | struct hash> 78 | { 79 | size_t operator()(const visualizer::SymmetricPair& pr) const 80 | { // has to be symmetric wrt a and b! 81 | return std::hash()(pr.first) + std::hash()(pr.second); 82 | } 83 | }; 84 | }//namespace std 85 | 86 | 87 | #endif // UTILS_SYMMETRIC_PAIR -------------------------------------------------------------------------------- /src/utils/UnionFind.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ruben Dulek, Ultimaker B.V. 2 | 3 | //UnionFind's base structure is released under CC0. See https://github.com/Ghostkeeper/Hattusa 4 | 5 | #ifndef UNIONFIND_H 6 | #define UNIONFIND_H 7 | 8 | #include 9 | #include //To get the max size_t as invalid value for the result of find(). 10 | #include //For size_t. 11 | #include //Holds the main data. 12 | #include //To map the data type to indices for user's convenience. 13 | 14 | namespace visualizer 15 | { 16 | 17 | /*! 18 | * A union-find data structure. 19 | * 20 | * This data structure keeps track of a set of sets. The sets can be combined in 21 | * constant time. Which set an item is part of can be found in amortised 22 | * constant time. 23 | * 24 | * This data structure is not thread-safe. 25 | */ 26 | template > 27 | class UnionFind 28 | { 29 | public: 30 | UnionFind(const Hash& hash = Hash()) 31 | { 32 | element_to_position = std::unordered_map(10, hash); 33 | } 34 | 35 | /*! 36 | * Adds a new item to the union-find data structure. 37 | * 38 | * The item will be placed in a singleton set until it is united with 39 | * another set. 40 | * \param item The item to add. 41 | * \return The handle of the set that the item gets placed in. This can be 42 | * used to combine its set with another set. 43 | */ 44 | size_t add(const E item) 45 | { 46 | items.push_back(item); 47 | size_t handle = parent_index.size(); //Guaranteed to be unique because there has never been any item with this index (can't remove from this data structure!) 48 | element_to_position[item] = handle; 49 | parent_index.push_back(handle); 50 | rank.push_back(1); 51 | return handle; 52 | } 53 | 54 | /*! 55 | * Finds the set that an item is part of. 56 | * \param item The item to find the set of. 57 | * \return The handle of the set that the item is part of. Compare this to 58 | * the handles of the sets that other items are part of to determine if they 59 | * are in the same set. 60 | */ 61 | size_t find(const E& item) 62 | { 63 | const typename std::unordered_map::const_iterator it = element_to_position.find(item); 64 | if (it == element_to_position.end()) 65 | { 66 | return (size_t)-1; 67 | } 68 | const size_t index = it->second; 69 | return findByHandle(index); 70 | } 71 | 72 | /*! 73 | * Finds the set that an item is part of. 74 | * \param item_handle The handle of an item, as returned by the add method. 75 | * \return The handle of the set that the item is part of. Compare this to 76 | * the handles of the sets that other items are part of to determine if they 77 | * are in the same set. 78 | */ 79 | size_t findByHandle(const size_t item_handle) 80 | { 81 | if (item_handle >= parent_index.size()) 82 | { 83 | return (size_t)-1; 84 | } 85 | const size_t parent = parent_index[item_handle]; 86 | if (parent != item_handle) //This is a root. 87 | { 88 | parent_index[item_handle] = findByHandle(parent); 89 | } 90 | return parent_index[item_handle]; 91 | } 92 | 93 | /*! 94 | * Unite two sets to be together in one set. 95 | * \param first One of the sets to combine with the other. 96 | * \param second The other set to combine with the first. 97 | * \return The new handle for the combined set. 98 | */ 99 | size_t unite(const size_t first, const size_t second) 100 | { 101 | const size_t first_root = findByHandle(first); 102 | const size_t second_root = findByHandle(second); 103 | 104 | //The tree with the greatest rank becomes the parent. This creates shallower trees. 105 | if (rank[first_root] < rank[second_root]) 106 | { 107 | parent_index[first] = second; 108 | rank[second_root] += rank[first_root]; 109 | return second; 110 | } 111 | else 112 | { 113 | parent_index[second] = first; 114 | rank[first_root] += rank[second_root]; 115 | return first; 116 | } 117 | } 118 | 119 | //Some aliases to allow people to use UnionFind::iterator and UnionFind::const_iterator without knowing that it's actually a vector iterator. 120 | using iterator = typename std::vector::iterator; 121 | using const_iterator = typename std::vector::iterator; 122 | 123 | /*! 124 | * Beginning of iteration over all items in this Union Find data structure. 125 | * 126 | * The items are iterated over in no particular order. 127 | */ 128 | iterator begin() 129 | { 130 | return items.begin(); 131 | } 132 | 133 | /*! 134 | * Ending of iteration over all items in this Union Find data structure. 135 | * 136 | * The items are iterated over in no particular order. 137 | */ 138 | iterator end() 139 | { 140 | return items.end(); 141 | } 142 | 143 | /*! 144 | * Beginning of iteration over all items in this Union Find data structure. 145 | * 146 | * The items are iterated over in no particular order. 147 | */ 148 | const_iterator begin() const 149 | { 150 | return items.begin(); 151 | } 152 | 153 | /*! 154 | * Ending of iteration over all items in this Union Find data structure. 155 | * 156 | * The items are iterated over in no particular order. 157 | */ 158 | const_iterator end() const 159 | { 160 | return items.end(); 161 | } 162 | 163 | /*! 164 | * Beginning of iteration over all items in this Union Find data structure. 165 | * 166 | * The items are iterated over in no particular order. 167 | */ 168 | const_iterator cbegin() const 169 | { 170 | return items.cbegin(); 171 | } 172 | 173 | /*! 174 | * Ending of iteration over all items in this Union Find data structure. 175 | * 176 | * The items are iterated over in no particular order. 177 | */ 178 | const_iterator cend() const 179 | { 180 | return items.cend(); 181 | } 182 | 183 | private: 184 | /*! 185 | * Holds all items in the entire data structure. 186 | */ 187 | std::vector items; 188 | 189 | /*! 190 | * Tracks where each element is, so that we can find it back when the user 191 | * only specifies an element parameter. 192 | */ 193 | std::unordered_map element_to_position; 194 | 195 | /*! 196 | * For each item, the set handle of the parent item. 197 | * 198 | * Items belong to the set of their most ancient ancestor, so to find if two 199 | * items are in the same set, find if they have any ancestor in common. 200 | * Items with itself as parent are root. There may be multiple roots. 201 | */ 202 | std::vector parent_index; 203 | 204 | /*! 205 | * For each item, a rank that roughly indicates how large the subtree is 206 | * beneath that item. 207 | * 208 | * Items with a higher rank get used as the parent element more often, so 209 | * that the trees become shorter. 210 | */ 211 | std::vector rank; 212 | }; 213 | 214 | } 215 | 216 | #endif /* UNIONFIND_H */ -------------------------------------------------------------------------------- /src/utils/VoronoiUtils.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef UTILS_VORONOI_UTILS_H 5 | #define UTILS_VORONOI_UTILS_H 6 | 7 | #include 8 | 9 | 10 | #include 11 | using boost::polygon::voronoi_diagram; 12 | 13 | #include "SVG.h" 14 | #include "PolygonsSegmentIndex.h" 15 | 16 | 17 | namespace visualizer 18 | { 19 | 20 | /*! 21 | */ 22 | class VoronoiUtils 23 | { 24 | public: 25 | using Segment = PolygonsSegmentIndex; 26 | using voronoi_data_t = double; 27 | using vd_t = voronoi_diagram; 28 | static void debugOutput(std::string filename, vd_t& vd, std::vector& points, std::vector& segments, bool draw_points = false, bool show_coords = true, bool show_parabola_generators = false, bool draw_arrows = false); 29 | static void debugOutput(SVG& svg, vd_t& vd, std::vector& points, std::vector& segments, bool draw_points = false, bool show_coords = false, bool show_parabola_generators = false, bool draw_arrows = false); 30 | 31 | static Point getSourcePoint(const vd_t::cell_type& cell, const std::vector& points, const std::vector& segments); 32 | static const Segment& getSourceSegment(const vd_t::cell_type& cell, const std::vector& points, const std::vector& segments); 33 | static PolygonsPointIndex getSourcePointIndex(const vd_t::cell_type& cell, const std::vector& points, const std::vector& segments); 34 | 35 | static Point p(const vd_t::vertex_type* node); 36 | 37 | static bool isSourcePoint(Point p, const vd_t::cell_type& cell, const std::vector& points, const std::vector& segments, coord_t snap_dist = 10); 38 | 39 | static coord_t getDistance(Point p, const vd_t::cell_type& cell, const std::vector& points, const std::vector& segments); 40 | 41 | /*! 42 | * Discretize a parabola based on (approximate) step size. 43 | * The \p approximate_step_size is measured parallel to the \p source_segment, not along the parabola. 44 | */ 45 | static std::vector discretizeParabola(const Point& source_point, const Segment& source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle); 46 | 47 | protected: 48 | /*! 49 | * Discretize parabola based on max absolute deviation from the parabola. 50 | * 51 | * adapted from boost::polygon::voronoi_visual_utils.cpp 52 | * 53 | * Discretize parabolic Voronoi edge. 54 | * Parabolic Voronoi edges are always formed by one point and one segment 55 | * from the initial input set. 56 | * 57 | * Args: 58 | * point: input point. 59 | * segment: input segment. 60 | * max_dist: maximum discretization distance. 61 | * discretization: point discretization of the given Voronoi edge. 62 | * 63 | * Template arguments: 64 | * InCT: coordinate type of the input geometries (usually integer). 65 | * Point: point type, should model point concept. 66 | * Segment: segment type, should model segment concept. 67 | * 68 | * Important: 69 | * discretization should contain both edge endpoints initially. 70 | */ 71 | static void discretize( 72 | const Point& point, 73 | const Segment& segment, 74 | const coord_t max_dist, 75 | std::vector* discretization); 76 | 77 | /*! 78 | * adapted from boost::polygon::voronoi_visual_utils.cpp 79 | * Compute y(x) = ((x - a) * (x - a) + b * b) / (2 * b). 80 | */ 81 | static coord_t parabola_y(coord_t x, coord_t a, coord_t b); 82 | /*! 83 | * adapted from boost::polygon::voronoi_visual_utils.cpp 84 | * Get normalized length of the distance between: 85 | * 1) point projection onto the segment 86 | * 2) start point of the segment 87 | * Return this length divided by the segment length. This is made to avoid 88 | * sqrt computation during transformation from the initial space to the 89 | * transformed one and vice versa. The assumption is made that projection of 90 | * the point lies between the start-point and endpoint of the segment. 91 | */ 92 | static double get_point_projection(const Point& point, const Segment& segment); 93 | 94 | }; 95 | 96 | 97 | }//namespace visualizer 98 | 99 | 100 | 101 | #endif//UTILS_VORONOI_UTILS_H 102 | -------------------------------------------------------------------------------- /src/utils/floatpoint.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef FLOAT_POINT_H 5 | #define FLOAT_POINT_H 6 | 7 | #include "IntPoint.h" 8 | 9 | #include 10 | #include 11 | 12 | 13 | namespace visualizer 14 | { 15 | 16 | /* 17 | Floating point 3D points are used during model loading as 3D vectors. 18 | They represent millimeters in 3D space. 19 | */ 20 | class FPoint3 21 | { 22 | public: 23 | float x,y,z; 24 | FPoint3() {} 25 | FPoint3(float _x, float _y, float _z): x(_x), y(_y), z(_z) {} 26 | FPoint3(const Point3& p): x(p.x*.001), y(p.y*.001), z(p.z*.001) {} 27 | 28 | FPoint3 operator+(const FPoint3& p) const { return FPoint3(x+p.x, y+p.y, z+p.z); } 29 | FPoint3 operator-(const FPoint3& p) const { return FPoint3(x-p.x, y-p.y, z-p.z); } 30 | FPoint3 operator*(const float f) const { return FPoint3(x*f, y*f, z*f); } 31 | FPoint3 operator/(const float f) const { return FPoint3(x/f, y/f, z/f); } 32 | 33 | FPoint3& operator += (const FPoint3& p) { x += p.x; y += p.y; z += p.z; return *this; } 34 | FPoint3& operator -= (const FPoint3& p) { x -= p.x; y -= p.y; z -= p.z; return *this; } 35 | FPoint3& operator *= (const float f) { x *= f; y *= f; z *= f; return *this; } 36 | 37 | bool operator==(FPoint3& p) const { return x==p.x&&y==p.y&&z==p.z; } 38 | bool operator!=(FPoint3& p) const { return x!=p.x||y!=p.y||z!=p.z; } 39 | 40 | float max() const 41 | { 42 | if (x > y && x > z) return x; 43 | if (y > z) return y; 44 | return z; 45 | } 46 | 47 | bool testLength(float len) const 48 | { 49 | return vSize2() <= len*len; 50 | } 51 | 52 | float vSize2() const 53 | { 54 | return x*x+y*y+z*z; 55 | } 56 | 57 | float vSize() const 58 | { 59 | return sqrt(vSize2()); 60 | } 61 | 62 | inline FPoint3 normalized() const 63 | { 64 | return (*this)/vSize(); 65 | } 66 | 67 | FPoint3 cross(const FPoint3& p) const 68 | { 69 | return FPoint3( 70 | y*p.z-z*p.y, 71 | z*p.x-x*p.z, 72 | x*p.y-y*p.x); 73 | } 74 | 75 | static FPoint3 cross(const Point3& a, const Point3& b) 76 | { 77 | return FPoint3(a).cross(FPoint3(b)); 78 | // FPoint3( 79 | // a.y*b.z-a.z*b.y, 80 | // a.z*b.x-a.x*b.z, 81 | // a.x*b.y-a.y*b.x); 82 | } 83 | 84 | Point3 toPoint3() 85 | { 86 | return Point3(x*1000, y*1000, z*1000); 87 | } 88 | }; 89 | 90 | 91 | //inline FPoint3 operator+(FPoint3 lhs, const FPoint3& rhs) { 92 | // lhs += rhs; 93 | // return lhs; 94 | //} 95 | inline float operator*(FPoint3 lhs, const FPoint3& rhs) { 96 | return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z; 97 | } 98 | //inline FPoint3 operator*(FPoint3 lhs, const float f) { 99 | // lhs *= f; 100 | // return lhs; 101 | //} 102 | 103 | class FMatrix3x3 104 | { 105 | public: 106 | double m[3][3]; 107 | 108 | FMatrix3x3() 109 | { 110 | m[0][0] = 1.0; 111 | m[1][0] = 0.0; 112 | m[2][0] = 0.0; 113 | m[0][1] = 0.0; 114 | m[1][1] = 1.0; 115 | m[2][1] = 0.0; 116 | m[0][2] = 0.0; 117 | m[1][2] = 0.0; 118 | m[2][2] = 1.0; 119 | } 120 | 121 | Point3 apply(const FPoint3& p) const 122 | { 123 | return Point3( 124 | MM2INT(p.x * m[0][0] + p.y * m[1][0] + p.z * m[2][0]), 125 | MM2INT(p.x * m[0][1] + p.y * m[1][1] + p.z * m[2][1]), 126 | MM2INT(p.x * m[0][2] + p.y * m[1][2] + p.z * m[2][2])); 127 | } 128 | }; 129 | 130 | }//namespace visualizer 131 | #endif//INT_POINT_H 132 | -------------------------------------------------------------------------------- /src/utils/gettime.cpp: -------------------------------------------------------------------------------- 1 | /** Copyright (C) 2013 Ultimaker - Released under terms of the AGPLv3 License */ 2 | #include "gettime.h" 3 | 4 | namespace visualizer 5 | { 6 | 7 | TimeKeeper::TimeKeeper() 8 | { 9 | restart(); 10 | } 11 | 12 | double TimeKeeper::restart() 13 | { 14 | double ret = getTime() - startTime; 15 | startTime = getTime(); 16 | return ret; 17 | } 18 | 19 | }//namespace cura 20 | -------------------------------------------------------------------------------- /src/utils/gettime.h: -------------------------------------------------------------------------------- 1 | /** Copyright (C) 2013 Ultimaker - Released under terms of the AGPLv3 License */ 2 | #ifndef GETTIME_H 3 | #define GETTIME_H 4 | 5 | #ifdef _WIN32 6 | #include 7 | #else 8 | #ifdef USE_CPU_TIME 9 | #include 10 | #endif 11 | 12 | #include 13 | #include 14 | #include 15 | #endif 16 | 17 | namespace visualizer 18 | { 19 | static inline double getTime() 20 | { 21 | #ifdef _WIN32 22 | return double(GetTickCount()) / 1000.0; 23 | #else // not __WIN32 24 | #if USE_CPU_TIME // Use cpu usage time if available, otherwise wall clock time 25 | struct rusage usage; 26 | #ifdef DEBUG 27 | int ret = getrusage(RUSAGE_SELF, &usage); 28 | assert(ret == 0); 29 | ((void)ret); 30 | #else 31 | getrusage(RUSAGE_SELF, &usage); 32 | #endif 33 | double user_time = double(usage.ru_utime.tv_sec) + double(usage.ru_utime.tv_usec) / 1000000.0; 34 | double sys_time = double(usage.ru_stime.tv_sec) + double(usage.ru_stime.tv_usec) / 1000000.0; 35 | return user_time + sys_time; 36 | #else // not USE_CPU_TIME 37 | struct timeval tv; 38 | gettimeofday(&tv, nullptr); 39 | return double(tv.tv_sec) + double(tv.tv_usec) / 1000000.0; 40 | #endif // USE_CPU_TIME 41 | #endif // __WIN32 42 | } 43 | 44 | class TimeKeeper 45 | { 46 | private: 47 | double startTime; 48 | public: 49 | TimeKeeper(); 50 | 51 | double restart(); 52 | }; 53 | 54 | }//namespace visualizer 55 | #endif//GETTIME_H 56 | -------------------------------------------------------------------------------- /src/utils/logoutput.cpp: -------------------------------------------------------------------------------- 1 | /** Copyright (C) 2013 Ultimaker - Released under terms of the AGPLv3 License */ 2 | #include 3 | #include 4 | 5 | #ifdef _OPENMP 6 | #include 7 | #endif // _OPENMP 8 | #include "logoutput.h" 9 | 10 | 11 | namespace visualizer { 12 | 13 | static int verbose_level; 14 | static bool progressLogging; 15 | 16 | void increaseVerboseLevel() 17 | { 18 | verbose_level++; 19 | } 20 | 21 | void enableProgressLogging() 22 | { 23 | progressLogging = true; 24 | } 25 | 26 | void logError(const char* fmt, ...) 27 | { 28 | #pragma omp critical 29 | { 30 | va_list args; 31 | va_start(args, fmt); 32 | fprintf(stderr, "[ERROR] "); 33 | vfprintf(stderr, fmt, args); 34 | va_end(args); 35 | fflush(stderr); 36 | } 37 | } 38 | 39 | void logWarning(const char* fmt, ...) 40 | { 41 | #pragma omp critical 42 | { 43 | va_list args; 44 | va_start(args, fmt); 45 | fprintf(stderr, "[WARNING] "); 46 | vfprintf(stderr, fmt, args); 47 | va_end(args); 48 | fflush(stderr); 49 | } 50 | } 51 | 52 | void logAlways(const char* fmt, ...) 53 | { 54 | #pragma omp critical 55 | { 56 | va_list args; 57 | va_start(args, fmt); 58 | vfprintf(stderr, fmt, args); 59 | va_end(args); 60 | fflush(stderr); 61 | } 62 | } 63 | 64 | void log(const char* fmt, ...) 65 | { 66 | if (verbose_level < 1) 67 | return; 68 | 69 | #pragma omp critical 70 | { 71 | va_list args; 72 | va_start(args, fmt); 73 | vfprintf(stderr, fmt, args); 74 | va_end(args); 75 | fflush(stderr); 76 | } 77 | } 78 | 79 | void logDebug(const char* fmt, ...) 80 | { 81 | if (verbose_level < 2) 82 | { 83 | return; 84 | } 85 | #pragma omp critical 86 | { 87 | va_list args; 88 | va_start(args, fmt); 89 | fprintf(stderr, "[DEBUG] "); 90 | vfprintf(stderr, fmt, args); 91 | va_end(args); 92 | fflush(stderr); 93 | } 94 | } 95 | 96 | void logProgress(const char* type, int value, int maxValue, float percent) 97 | { 98 | if (!progressLogging) 99 | return; 100 | 101 | #pragma omp critical 102 | { 103 | fprintf(stderr, "Progress:%s:%i:%i \t%f%%\n", type, value, maxValue, percent); 104 | fflush(stderr); 105 | } 106 | } 107 | 108 | }//namespace visualizer 109 | -------------------------------------------------------------------------------- /src/utils/logoutput.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef LOGOUTPUT_H 5 | #define LOGOUTPUT_H 6 | 7 | namespace visualizer { 8 | 9 | /* 10 | * \brief Increase verbosity level by 1. 11 | */ 12 | void increaseVerboseLevel(); 13 | 14 | /* 15 | * \brief Enable logging the current slicing progress to the log. 16 | */ 17 | void enableProgressLogging(); 18 | 19 | /* 20 | * \brief Report an error message. 21 | * 22 | * This is always reported, regardless of verbosity level. 23 | */ 24 | void logError(const char* fmt, ...); 25 | 26 | /* 27 | * \brief Report a warning message. 28 | * 29 | * Always reported, regardless of verbosity level. 30 | */ 31 | void logWarning(const char* fmt, ...); 32 | 33 | /* 34 | * \brief Report a message if the verbosity level is 1 or higher. 35 | */ 36 | void log(const char* fmt, ...); 37 | 38 | /* 39 | * \brief Log a message, regardless of verbosity level. 40 | */ 41 | void logAlways(const char* fmt, ...); 42 | 43 | /* 44 | * \brief Log a debugging message. 45 | * 46 | * The message is only logged if the verbosity level is 2 or higher. 47 | */ 48 | void logDebug(const char* fmt, ...); 49 | 50 | /* 51 | * \brief Report the progress in the log. 52 | * 53 | * Only works if ``enableProgressLogging()`` has been called. 54 | */ 55 | void logProgress(const char* type, int value, int maxValue, float percent); 56 | 57 | } //namespace visualizer 58 | 59 | #endif //LOGOUTPUT_H -------------------------------------------------------------------------------- /src/utils/macros.h: -------------------------------------------------------------------------------- 1 | /** Copyright (C) 2017 Ultimaker - Released under terms of the AGPLv3 License */ 2 | #ifndef UTILS_MACROS_H 3 | #define UTILS_MACROS_H 4 | 5 | // macro to suppress unused parameter warnings from the compiler 6 | #define UNUSED_PARAM(param) (void)(param) 7 | 8 | // simple function to be used to reduce the number of times an error is logged 9 | // no guarantees in a mutli-threaded context 10 | #define RUN_ONCE(runcode) \ 11 | { \ 12 | static bool code_ran = 0; \ 13 | if(!code_ran){ \ 14 | code_ran = 1; \ 15 | runcode; \ 16 | } \ 17 | } 18 | 19 | 20 | #endif // UTILS_MACROS_H 21 | -------------------------------------------------------------------------------- /src/utils/math.h: -------------------------------------------------------------------------------- 1 | /** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */ 2 | #ifndef UTILS_MATH_H 3 | #define UTILS_MATH_H 4 | 5 | #include 6 | 7 | 8 | //c++11 no longer defines M_PI, so add our own constant. 9 | #ifndef M_PI 10 | #define M_PI 3.14159265358979323846 11 | #endif 12 | 13 | namespace visualizer 14 | { 15 | 16 | static constexpr float sqrt2 = 1.41421356237; 17 | 18 | template inline T square(const T& a) { return a * a; } 19 | 20 | inline unsigned int round_divide(unsigned int dividend, unsigned int divisor) //!< Return dividend divided by divisor rounded to the nearest integer 21 | { 22 | return (dividend + divisor / 2) / divisor; 23 | } 24 | inline unsigned int round_up_divide(unsigned int dividend, unsigned int divisor) //!< Return dividend divided by divisor rounded to the nearest integer 25 | { 26 | return (dividend + divisor - 1) / divisor; 27 | } 28 | 29 | }//namespace visualizer 30 | #endif // UTILS_MATH_H 31 | 32 | -------------------------------------------------------------------------------- /src/utils/types/AngleDegrees.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef ANGLEDEGREES_H 5 | #define ANGLEDEGREES_H 6 | 7 | #include //For fmod. 8 | 9 | namespace visualizer 10 | { 11 | 12 | /* 13 | * \brief Represents an angle in degrees. 14 | * 15 | * This is a facade. It behaves like a double, but this is using clock 16 | * arithmetic which guarantees that the value is always between 0 and 360. 17 | */ 18 | struct AngleDegrees 19 | { 20 | /* 21 | * \brief Default constructor setting the angle to 0. 22 | */ 23 | AngleDegrees() : value(0.0) {}; 24 | 25 | /* 26 | * \brief Casts a double to an AngleDegrees instance. 27 | */ 28 | AngleDegrees(double value) : value(std::fmod(std::fmod(value, 360) + 360, 360)) {}; 29 | 30 | /* 31 | * \brief Casts the AngleDegrees instance to a double. 32 | */ 33 | operator double() const 34 | { 35 | return value; 36 | } 37 | 38 | /* 39 | * Some operators implementing the clock arithmetic. 40 | */ 41 | AngleDegrees operator +(const AngleDegrees& other) const 42 | { 43 | return std::fmod(std::fmod(value + other.value, 360) + 360, 360); 44 | } 45 | AngleDegrees operator +(const int& other) const 46 | { 47 | return operator+(AngleDegrees(other)); 48 | } 49 | AngleDegrees& operator +=(const AngleDegrees& other) 50 | { 51 | value = std::fmod(std::fmod(value + other.value, 360) + 360, 360); 52 | return *this; 53 | } 54 | AngleDegrees operator -(const AngleDegrees& other) const 55 | { 56 | return std::fmod(std::fmod(value - other.value, 360) + 360, 360); 57 | } 58 | AngleDegrees& operator -=(const AngleDegrees& other) 59 | { 60 | value = std::fmod(std::fmod(value - other.value, 360) + 360, 360); 61 | return *this; 62 | } 63 | 64 | /* 65 | * \brief The actual angle, as a double. 66 | * 67 | * This value should always be between 0 and 360. 68 | */ 69 | double value = 0; 70 | }; 71 | 72 | } 73 | 74 | #endif //ANGLEDEGREES_H -------------------------------------------------------------------------------- /src/utils/types/AngleRadians.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef ANGLERADIANS_H 5 | #define ANGLERADIANS_H 6 | 7 | #include //For fmod. 8 | #include "../../utils/math.h" 9 | 10 | #define TAU (2.0 * M_PI) 11 | 12 | namespace visualizer 13 | { 14 | 15 | /* 16 | * \brief Represents an angle in radians. 17 | * 18 | * This is a facade. It behaves like a double, but this is using clock 19 | * arithmetic which guarantees that the value is always between 0 and 2 * pi. 20 | */ 21 | struct AngleRadians 22 | { 23 | /* 24 | * \brief Default constructor setting the angle to 0. 25 | */ 26 | AngleRadians() : value(0.0) {}; 27 | 28 | /* 29 | * \brief Translate the double value in degrees to an AngleRadians instance. 30 | */ 31 | AngleRadians(double value) : value(std::fmod(std::fmod(value, TAU) + TAU, TAU)) {}; 32 | 33 | /* 34 | * \brief Casts the AngleRadians instance to a double. 35 | */ 36 | operator double() const 37 | { 38 | return value; 39 | } 40 | 41 | /* 42 | * Some operators implementing the clock arithmetic. 43 | */ 44 | AngleRadians operator +(const AngleRadians& other) const 45 | { 46 | return std::fmod(std::fmod(value + other.value, TAU) + TAU, TAU); 47 | } 48 | AngleRadians& operator +=(const AngleRadians& other) 49 | { 50 | value = std::fmod(std::fmod(value + other.value, TAU) + TAU, TAU); 51 | return *this; 52 | } 53 | AngleRadians operator -(const AngleRadians& other) const 54 | { 55 | return std::fmod(std::fmod(value - other.value, TAU) + TAU, TAU); 56 | } 57 | AngleRadians& operator -=(const AngleRadians& other) 58 | { 59 | value = std::fmod(std::fmod(value - other.value, TAU) + TAU, TAU); 60 | return *this; 61 | } 62 | 63 | /* 64 | * \brief The actual angle, as a double. 65 | * 66 | * This value should always be between 0 and 2 * pi. 67 | */ 68 | double value = 0; 69 | }; 70 | 71 | } 72 | 73 | #endif //ANGLERADIANS_H -------------------------------------------------------------------------------- /src/utils/types/Duration.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef DURATION_H 5 | #define DURATION_H 6 | 7 | #include 8 | 9 | namespace visualizer 10 | { 11 | 12 | /* 13 | * \brief Represents a duration in seconds. 14 | * 15 | * This is a facade. It behaves like a double, only it can't be negative. 16 | */ 17 | struct Duration 18 | { 19 | /* 20 | * \brief Default constructor setting the duration to 0. 21 | */ 22 | constexpr Duration() : value(0) {}; 23 | 24 | /* 25 | * \brief Casts a double to a Duration instance. 26 | */ 27 | constexpr Duration(double value) : value(value > 0.0 ? value : 0.0) {}; 28 | 29 | /* 30 | * \brief Casts the Duration instance to a double. 31 | */ 32 | constexpr operator double() const 33 | { 34 | return value; 35 | }; 36 | 37 | /* 38 | * Some operators to do arithmetic with Durations. 39 | */ 40 | Duration operator +(const Duration& other) const 41 | { 42 | return Duration(value + other.value); 43 | }; 44 | Duration operator -(const Duration& other) const 45 | { 46 | return Duration(value + other.value); 47 | }; 48 | Duration& operator +=(const Duration& other) 49 | { 50 | value += other.value; 51 | return *this; 52 | } 53 | Duration& operator -=(const Duration& other) 54 | { 55 | value -= other.value; 56 | return *this; 57 | } 58 | 59 | /* 60 | * \brief The actual duration, as a double. 61 | */ 62 | double value = 0; 63 | }; 64 | 65 | constexpr Duration operator "" _s(const long double seconds) 66 | { 67 | return Duration(seconds); 68 | } 69 | 70 | inline std::ostream& operator<< (std::ostream& out, const Duration seconds) 71 | { 72 | double s = seconds; 73 | if (seconds > 60) 74 | { 75 | int min = seconds / 60; 76 | s -= min * 60; 77 | if (min > 60) 78 | { 79 | int hrs = min / 60; 80 | min -= hrs * 60; 81 | out << hrs << "h "; 82 | } 83 | out << min << "min "; 84 | } 85 | out << s << "s"; 86 | return out; 87 | } 88 | 89 | } 90 | 91 | #endif //DURATION_H 92 | -------------------------------------------------------------------------------- /src/utils/types/EnumSettings.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | //CuraEngine is released under the terms of the AGPLv3 or higher. 3 | 4 | #ifndef SETTINGSBASEVIRTUAL_H 5 | #define SETTINGSBASEVIRTUAL_H 6 | 7 | namespace visualizer 8 | { 9 | 10 | /*! 11 | * In Cura different infill methods are available. 12 | * This enum defines which fill patterns are available to get a uniform naming throughout the engine. 13 | * The different methods are used for top/bottom, support and sparse infill. 14 | */ 15 | enum class EFillMethod 16 | { 17 | LINES, 18 | GRID, 19 | CUBIC, 20 | CUBICSUBDIV, 21 | TETRAHEDRAL, 22 | QUARTER_CUBIC, 23 | TRIANGLES, 24 | TRIHEXAGON, 25 | CONCENTRIC, 26 | ZIG_ZAG, 27 | CROSS, 28 | CROSS_3D, 29 | GYROID, 30 | NONE 31 | }; 32 | 33 | /*! 34 | * Type of platform adhesion. 35 | */ 36 | enum class EPlatformAdhesion 37 | { 38 | SKIRT, 39 | BRIM, 40 | RAFT, 41 | NONE 42 | }; 43 | 44 | /*! 45 | * Type of support material to generate 46 | */ 47 | enum class ESupportType 48 | { 49 | NONE, 50 | PLATFORM_ONLY, 51 | EVERYWHERE 52 | }; 53 | 54 | enum class EZSeamType 55 | { 56 | RANDOM, 57 | SHORTEST, 58 | USER_SPECIFIED, 59 | SHARPEST_CORNER 60 | }; 61 | 62 | enum class EZSeamCornerPrefType 63 | { 64 | Z_SEAM_CORNER_PREF_NONE, 65 | Z_SEAM_CORNER_PREF_INNER, 66 | Z_SEAM_CORNER_PREF_OUTER, 67 | Z_SEAM_CORNER_PREF_ANY, 68 | Z_SEAM_CORNER_PREF_WEIGHTED 69 | }; 70 | 71 | enum class ESurfaceMode 72 | { 73 | NORMAL, 74 | SURFACE, 75 | BOTH 76 | }; 77 | 78 | enum class FillPerimeterGapMode 79 | { 80 | NOWHERE, 81 | EVERYWHERE 82 | }; 83 | 84 | enum class BuildPlateShape 85 | { 86 | RECTANGULAR, 87 | ELLIPTIC 88 | }; 89 | 90 | enum class CombingMode 91 | { 92 | OFF, 93 | ALL, 94 | NO_SKIN, 95 | INFILL 96 | }; 97 | 98 | /*! 99 | * How the draft shield height is limited. 100 | */ 101 | enum class DraftShieldHeightLimitation 102 | { 103 | FULL, //Draft shield takes full height of the print. 104 | LIMITED //Draft shield is limited by draft_shield_height setting. 105 | }; 106 | 107 | enum class SupportDistPriority 108 | { 109 | XY_OVERRIDES_Z, 110 | Z_OVERRIDES_XY 111 | }; 112 | 113 | enum class SlicingTolerance 114 | { 115 | MIDDLE, 116 | INCLUSIVE, 117 | EXCLUSIVE 118 | }; 119 | /*! 120 | * Different flavors of GCode. Some machines require different types of GCode. 121 | * The GCode flavor definition handles this as a big setting to make major or minor modifications to the GCode. 122 | */ 123 | enum class EGCodeFlavor 124 | { 125 | /** 126 | * Marlin flavored GCode is Marlin/Sprinter based GCode. 127 | * This is the most commonly used GCode set. 128 | * G0 for moves, G1 for extrusion. 129 | * E values give mm of filament extrusion. 130 | * Retraction is done on E values with G1. Start/end code is added. 131 | * M106 Sxxx and M107 are used to turn the fan on/off. 132 | **/ 133 | MARLIN = 0, 134 | /** 135 | * UltiGCode flavored is Marlin based GCode. 136 | * UltiGCode uses less settings on the slicer and puts more settings in the firmware. This makes for more hardware/material independed GCode. 137 | * G0 for moves, G1 for extrusion. 138 | * E values give mm^3 of filament extrusion. Ignores the filament diameter setting. 139 | * Retraction is done with G10 and G11. Retraction settings are ignored. G10 S1 is used for multi-extruder switch retraction. 140 | * Start/end code is not added. 141 | * M106 Sxxx and M107 are used to turn the fan on/off. 142 | **/ 143 | ULTIGCODE = 1, 144 | /** 145 | * Makerbot flavored GCode. 146 | * Looks a lot like RepRap GCode with a few changes. Requires MakerWare to convert to X3G files. 147 | * Heating needs to be done with M104 Sxxx T0 148 | * No G21 or G90 149 | * Fan ON is M126 T0 (No fan strength control?) 150 | * Fan OFF is M127 T0 151 | * Homing is done with G162 X Y F2000 152 | **/ 153 | MAKERBOT = 2, 154 | 155 | /** 156 | * Bits From Bytes GCode. 157 | * BFB machines use RPM instead of E. Which is coupled to the F instead of independed. (M108 S[deciRPM]) 158 | * Need X,Y,Z,F on every line. 159 | * Needs extruder ON/OFF (M101, M103), has auto-retrection (M227 S[2560*mm] P[2560*mm]) 160 | **/ 161 | BFB = 3, 162 | 163 | /** 164 | * MACH3 GCode 165 | * MACH3 is CNC control software, which expects A/B/C/D for extruders, instead of E. 166 | **/ 167 | MACH3 = 4, 168 | /** 169 | * RepRap volumatric flavored GCode is Marlin based GCode. 170 | * Volumatric uses less settings on the slicer and puts more settings in the firmware. This makes for more hardware/material independed GCode. 171 | * G0 for moves, G1 for extrusion. 172 | * E values give mm^3 of filament extrusion. Ignores the filament diameter setting. 173 | * Retraction is done with G10 and G11. Retraction settings are ignored. G10 S1 is used for multi-extruder switch retraction. 174 | * M106 Sxxx and M107 are used to turn the fan on/off. 175 | **/ 176 | MARLIN_VOLUMATRIC = 5, 177 | /** 178 | * Griffin flavored is Marlin based GCode. 179 | * This is a type of RepRap used for machines with multiple extruder trains. 180 | * G0 for moves, G1 for extrusion. 181 | * E values give mm of filament extrusion. 182 | * E values are stored separately per extruder train. 183 | * Retraction is done on E values with G1. Start/end code is added. 184 | * M227 is used to initialize a single extrusion train. 185 | **/ 186 | GRIFFIN = 6, 187 | 188 | REPETIER = 7, 189 | 190 | /** 191 | * Real RepRap GCode suitable for printers using RepRap firmware (e.g. Duet controllers) 192 | **/ 193 | REPRAP = 8, 194 | }; 195 | 196 | } //Cura namespace. 197 | 198 | #endif //SETTINGSBASEVIRTUAL_H 199 | -------------------------------------------------------------------------------- /src/utils/types/LayerIndex.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef LAYERINDEX_H 5 | #define LAYERINDEX_H 6 | 7 | #include 8 | 9 | namespace visualizer 10 | { 11 | 12 | /* 13 | * \brief Struct behaving like a layer number. 14 | * 15 | * This is a facade. It behaves exactly like an integer but is used to indicate 16 | * that it is a layer number. 17 | */ 18 | struct LayerIndex 19 | { 20 | /* 21 | * \brief Default constructor setting the layer index to 0. 22 | */ 23 | LayerIndex() : value(0) {}; 24 | 25 | /* 26 | * \brief Casts an integer to a LayerIndex instance. 27 | */ 28 | LayerIndex(int value) : value(value) {}; 29 | 30 | /* 31 | * \brief Casts the LayerIndex instance to an integer. 32 | */ 33 | operator int() const 34 | { 35 | return value; 36 | } 37 | 38 | /* 39 | * Some operators to add and subtract layer numbers. 40 | */ 41 | LayerIndex operator +(const LayerIndex& other) const 42 | { 43 | return LayerIndex(value + other.value); 44 | } 45 | template LayerIndex operator +(const E& other) const 46 | { 47 | return LayerIndex(value + other); 48 | } 49 | 50 | LayerIndex operator -(const LayerIndex& other) const 51 | { 52 | return LayerIndex(value - other.value); 53 | } 54 | template LayerIndex operator -(const E& other) const 55 | { 56 | return LayerIndex(value - other); 57 | } 58 | 59 | LayerIndex& operator +=(const LayerIndex& other) 60 | { 61 | value += other.value; 62 | return *this; 63 | } 64 | template LayerIndex& operator +=(const E& other) 65 | { 66 | value += other; 67 | return *this; 68 | } 69 | 70 | LayerIndex& operator -=(const LayerIndex& other) 71 | { 72 | value -= other.value; 73 | return *this; 74 | } 75 | template LayerIndex& operator -=(const E& other) 76 | { 77 | value -= other; 78 | return *this; 79 | } 80 | 81 | LayerIndex& operator ++() 82 | { 83 | value++; 84 | return *this; 85 | } 86 | LayerIndex operator ++(int) //Postfix. 87 | { 88 | LayerIndex original_value(value); 89 | operator++(); //Increment myself. 90 | return original_value; 91 | } 92 | LayerIndex operator --() 93 | { 94 | value--; 95 | return *this; 96 | } 97 | LayerIndex operator --(int) //Postfix. 98 | { 99 | LayerIndex original_value(value); 100 | operator--(); //Decrement myself. 101 | return original_value; 102 | } 103 | 104 | /* 105 | * \brief The actual layer index. 106 | * 107 | * Note that this could be negative for raft layers. 108 | */ 109 | int value = 0; 110 | }; 111 | 112 | } 113 | 114 | namespace std 115 | { 116 | template<> 117 | struct hash 118 | { 119 | size_t operator()(const visualizer::LayerIndex& layer_index) const 120 | { 121 | return hash()(layer_index.value); 122 | } 123 | }; 124 | } 125 | 126 | #endif //LAYERINDEX_H -------------------------------------------------------------------------------- /src/utils/types/Ratio.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef RATIO_H 5 | #define RATIO_H 6 | 7 | namespace visualizer 8 | { 9 | 10 | /* 11 | * \brief Represents a ratio between two numbers. 12 | * 13 | * This is a facade. It behaves like a double. 14 | */ 15 | struct Ratio 16 | { 17 | /* 18 | * \brief Default constructor setting the ratio to 1. 19 | */ 20 | constexpr Ratio() : value(1.0) {}; 21 | 22 | /* 23 | * \brief Casts a double to a Ratio instance. 24 | */ 25 | constexpr Ratio(double value) : value(value) {}; 26 | 27 | /* 28 | * \brief Casts the Ratio instance to a double. 29 | */ 30 | operator double() const 31 | { 32 | return value; 33 | } 34 | 35 | /* 36 | * Some operators for arithmetic on ratios. 37 | */ 38 | Ratio operator *(const Ratio& other) const 39 | { 40 | return Ratio(value * other.value); 41 | } 42 | template Ratio operator *(const E& other) const 43 | { 44 | return Ratio(value * other); 45 | } 46 | Ratio operator /(const Ratio& other) const 47 | { 48 | return Ratio(value / other.value); 49 | } 50 | template Ratio operator /(const E& other) const 51 | { 52 | return Ratio(value / other); 53 | } 54 | Ratio& operator *=(const Ratio& other) 55 | { 56 | value *= other.value; 57 | return *this; 58 | } 59 | template Ratio& operator *=(const E& other) 60 | { 61 | value *= other; 62 | return *this; 63 | } 64 | Ratio& operator /=(const Ratio& other) 65 | { 66 | value /= other.value; 67 | return *this; 68 | } 69 | template Ratio& operator /=(const E& other) 70 | { 71 | value /= other; 72 | return *this; 73 | } 74 | 75 | /* 76 | * \brief The actual ratio, as a double. 77 | */ 78 | double value = 0; 79 | }; 80 | 81 | constexpr Ratio operator "" _r(const long double ratio) 82 | { 83 | return Ratio(ratio); 84 | } 85 | 86 | } 87 | 88 | #endif //RATIO_H -------------------------------------------------------------------------------- /src/utils/types/Temperature.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef TEMPERATURE_H 5 | #define TEMPERATURE_H 6 | 7 | namespace visualizer 8 | { 9 | 10 | /* 11 | * \brief Represents a temperature in degrees Celsius. 12 | * 13 | * This is a facade. It behaves like a double. 14 | */ 15 | struct Temperature 16 | { 17 | /* 18 | * \brief Default constructor setting the temperature to 0. 19 | */ 20 | Temperature() : value(0.0) {}; 21 | 22 | /* 23 | * \brief Casts a double to a Temperature instance. 24 | */ 25 | Temperature(double value) : value(value) {}; 26 | 27 | /* 28 | * \brief Casts the Temperature instance to a double. 29 | */ 30 | operator double() const 31 | { 32 | return value; 33 | } 34 | 35 | /* 36 | * Some operators to do arithmetic with Temperatures. 37 | */ 38 | Temperature operator +(const Temperature& other) const 39 | { 40 | return Temperature(value + other.value); 41 | }; 42 | Temperature operator -(const Temperature& other) const 43 | { 44 | return Temperature(value - other.value); 45 | }; 46 | Temperature& operator +=(const Temperature& other) 47 | { 48 | value += other.value; 49 | return *this; 50 | } 51 | Temperature& operator -=(const Temperature& other) 52 | { 53 | value -= other.value; 54 | return *this; 55 | } 56 | 57 | /* 58 | * \brief The actual temperature, as a double. 59 | */ 60 | double value = 0; 61 | }; 62 | 63 | } 64 | 65 | #endif //TEMPERATURE_H -------------------------------------------------------------------------------- /src/utils/types/Velocity.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2018 Ultimaker B.V. 2 | 3 | 4 | #ifndef VELOCITY_H 5 | #define VELOCITY_H 6 | 7 | namespace visualizer 8 | { 9 | 10 | /* 11 | * Represents a velocity in millimetres per second. 12 | * 13 | * This is a facade. It behaves like a double, only it can't be negative. 14 | */ 15 | struct Velocity 16 | { 17 | /* 18 | * \brief Default constructor setting velocity to 0. 19 | */ 20 | constexpr Velocity() : value(0.0) {}; 21 | 22 | /* 23 | * \brief Casts a double to a Velocity instance. 24 | */ 25 | constexpr Velocity(double value) : value(value) {}; 26 | 27 | /* 28 | * \brief Casts the Temperature instance to a double. 29 | */ 30 | constexpr operator double() const 31 | { 32 | return value; 33 | } 34 | 35 | /* 36 | * Some operators for arithmetic on velocities. 37 | */ 38 | Velocity operator *(const Velocity& other) const 39 | { 40 | return Velocity(value * other.value); 41 | } 42 | template Velocity operator *(const E& other) const 43 | { 44 | return Velocity(value * other); 45 | } 46 | Velocity operator /(const Velocity& other) const 47 | { 48 | return Velocity(value / other.value); 49 | } 50 | template Velocity operator /(const E& other) const 51 | { 52 | return Velocity(value / other); 53 | } 54 | Velocity& operator *=(const Velocity& other) 55 | { 56 | value *= other.value; 57 | return *this; 58 | } 59 | template Velocity& operator *=(const E& other) 60 | { 61 | value *= other; 62 | return *this; 63 | } 64 | Velocity& operator /=(const Velocity& other) 65 | { 66 | value /= other.value; 67 | return *this; 68 | } 69 | template Velocity& operator /=(const E& other) 70 | { 71 | value /= other; 72 | return *this; 73 | } 74 | 75 | /* 76 | * \brief The actual temperature, as a double. 77 | */ 78 | double value = 0; 79 | }; 80 | 81 | using Acceleration = Velocity; //Use the same logic for acceleration variables. 82 | 83 | } 84 | 85 | #endif //VELOCITY_H -------------------------------------------------------------------------------- /src/voronoi_visual_utils.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Boost.Polygon library voronoi_graphic_utils.hpp header file 3 | 4 | // Copyright Andrii Sydorchuk 2010-2012. 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | // See http://www.boost.org for updates, documentation, and revision history. 10 | 11 | #ifndef BOOST_POLYGON_VORONOI_VISUAL_UTILS 12 | #define BOOST_POLYGON_VORONOI_VISUAL_UTILS 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace boost { 23 | namespace polygon { 24 | // Utilities class, that contains set of routines handful for visualization. 25 | template 26 | class voronoi_visual_utils { 27 | public: 28 | // Discretize parabolic Voronoi edge. 29 | // Parabolic Voronoi edges are always formed by one point and one segment 30 | // from the initial input set. 31 | // 32 | // Args: 33 | // point: input point. 34 | // segment: input segment. 35 | // max_dist: maximum discretization distance. 36 | // discretization: point discretization of the given Voronoi edge. 37 | // 38 | // Template arguments: 39 | // InCT: coordinate type of the input geometries (usually integer). 40 | // Point: point type, should model point concept. 41 | // Segment: segment type, should model segment concept. 42 | // 43 | // Important: 44 | // discretization should contain both edge endpoints initially. 45 | template class Point, 47 | template class Segment> 48 | static 49 | typename enable_if< 50 | typename gtl_and< 51 | typename gtl_if< 52 | typename is_point_concept< 53 | typename geometry_concept< Point >::type 54 | >::type 55 | >::type, 56 | typename gtl_if< 57 | typename is_segment_concept< 58 | typename geometry_concept< Segment >::type 59 | >::type 60 | >::type 61 | >::type, 62 | void 63 | >::type discretize( 64 | const Point& point, 65 | const Segment& segment, 66 | const CT max_dist, 67 | std::vector< Point >* discretization) { 68 | // Apply the linear transformation to move start point of the segment to 69 | // the point with coordinates (0, 0) and the direction of the segment to 70 | // coincide the positive direction of the x-axis. 71 | CT segm_vec_x = cast(x(high(segment))) - cast(x(low(segment))); 72 | CT segm_vec_y = cast(y(high(segment))) - cast(y(low(segment))); 73 | CT sqr_segment_length = segm_vec_x * segm_vec_x + segm_vec_y * segm_vec_y; 74 | 75 | // Compute x-coordinates of the endpoints of the edge 76 | // in the transformed space. 77 | CT projection_start = sqr_segment_length * 78 | get_point_projection((*discretization)[0], segment); 79 | CT projection_end = sqr_segment_length * 80 | get_point_projection((*discretization)[1], segment); 81 | 82 | // Compute parabola parameters in the transformed space. 83 | // Parabola has next representation: 84 | // f(x) = ((x-rot_x)^2 + rot_y^2) / (2.0*rot_y). 85 | CT point_vec_x = cast(x(point)) - cast(x(low(segment))); 86 | CT point_vec_y = cast(y(point)) - cast(y(low(segment))); 87 | CT rot_x = segm_vec_x * point_vec_x + segm_vec_y * point_vec_y; 88 | CT rot_y = segm_vec_x * point_vec_y - segm_vec_y * point_vec_x; 89 | 90 | // Save the last point. 91 | Point last_point = (*discretization)[1]; 92 | discretization->pop_back(); 93 | 94 | // Use stack to avoid recursion. 95 | std::stack point_stack; 96 | point_stack.push(projection_end); 97 | CT cur_x = projection_start; 98 | CT cur_y = parabola_y(cur_x, rot_x, rot_y); 99 | 100 | // Adjust max_dist parameter in the transformed space. 101 | const CT max_dist_transformed = max_dist * max_dist * sqr_segment_length; 102 | while (!point_stack.empty()) { 103 | CT new_x = point_stack.top(); 104 | CT new_y = parabola_y(new_x, rot_x, rot_y); 105 | 106 | // Compute coordinates of the point of the parabola that is 107 | // furthest from the current line segment. 108 | CT mid_x = (new_y - cur_y) / (new_x - cur_x) * rot_y + rot_x; 109 | CT mid_y = parabola_y(mid_x, rot_x, rot_y); 110 | 111 | // Compute maximum distance between the given parabolic arc 112 | // and line segment that discretize it. 113 | CT dist = (new_y - cur_y) * (mid_x - cur_x) - 114 | (new_x - cur_x) * (mid_y - cur_y); 115 | dist = dist * dist / ((new_y - cur_y) * (new_y - cur_y) + 116 | (new_x - cur_x) * (new_x - cur_x)); 117 | if (dist <= max_dist_transformed) { 118 | // Distance between parabola and line segment is less than max_dist. 119 | point_stack.pop(); 120 | CT inter_x = (segm_vec_x * new_x - segm_vec_y * new_y) / 121 | sqr_segment_length + cast(x(low(segment))); 122 | CT inter_y = (segm_vec_x * new_y + segm_vec_y * new_x) / 123 | sqr_segment_length + cast(y(low(segment))); 124 | discretization->push_back(Point(inter_x, inter_y)); 125 | cur_x = new_x; 126 | cur_y = new_y; 127 | } else { 128 | point_stack.push(mid_x); 129 | } 130 | } 131 | 132 | // Update last point. 133 | discretization->back() = last_point; 134 | } 135 | 136 | private: 137 | // Compute y(x) = ((x - a) * (x - a) + b * b) / (2 * b). 138 | static CT parabola_y(CT x, CT a, CT b) { 139 | return ((x - a) * (x - a) + b * b) / (b + b); 140 | } 141 | 142 | // Get normalized length of the distance between: 143 | // 1) point projection onto the segment 144 | // 2) start point of the segment 145 | // Return this length divided by the segment length. This is made to avoid 146 | // sqrt computation during transformation from the initial space to the 147 | // transformed one and vice versa. The assumption is made that projection of 148 | // the point lies between the start-point and endpoint of the segment. 149 | template class Point, 151 | template class Segment> 152 | static 153 | typename enable_if< 154 | typename gtl_and< 155 | typename gtl_if< 156 | typename is_point_concept< 157 | typename geometry_concept< Point >::type 158 | >::type 159 | >::type, 160 | typename gtl_if< 161 | typename is_segment_concept< 162 | typename geometry_concept< Segment >::type 163 | >::type 164 | >::type 165 | >::type, 166 | CT 167 | >::type get_point_projection( 168 | const Point& point, const Segment& segment) { 169 | CT segment_vec_x = cast(x(high(segment))) - cast(x(low(segment))); 170 | CT segment_vec_y = cast(y(high(segment))) - cast(y(low(segment))); 171 | CT point_vec_x = x(point) - cast(x(low(segment))); 172 | CT point_vec_y = y(point) - cast(y(low(segment))); 173 | CT sqr_segment_length = 174 | segment_vec_x * segment_vec_x + segment_vec_y * segment_vec_y; 175 | CT vec_dot = segment_vec_x * point_vec_x + segment_vec_y * point_vec_y; 176 | return vec_dot / sqr_segment_length; 177 | } 178 | 179 | template 180 | static CT cast(const InCT& value) { 181 | return static_cast(value); 182 | } 183 | }; 184 | } 185 | } 186 | 187 | #endif // BOOST_POLYGON_VORONOI_VISUAL_UTILS 188 | --------------------------------------------------------------------------------