diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index bddc346b9..02420fed8 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -234,7 +234,7 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) if (_is_multi) list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}) - if (MSVC OR OpenVDB_${COMPONENT}_LIBRARY_DEBUG) + if (OpenVDB_${COMPONENT}_LIBRARY_DEBUG) list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}) endif () diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 3251d2c62..74bf6eb53 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -152,6 +152,11 @@ include(MPFR/MPFR.cmake) include(CGAL/CGAL.cmake) include(wxWidgets/wxWidgets.cmake) +if (ZLIB_PKG) + add_dependencies(dep_blosc ${ZLIB_PKG}) + add_dependencies(dep_openexr ${ZLIB_PKG}) +endif () + if (MSVC) add_custom_target(deps ALL diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 8be9888bc..ac93b4932 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -35,8 +35,6 @@ else () set(DEP_PLATFORM "x64") endif () - - if (${DEP_DEBUG}) set(DEP_BOOST_DEBUG "debug") else () @@ -217,7 +215,6 @@ ExternalProject_Add(dep_blosc #URL_HASH SHA256=7463a1df566704f212263312717ab2c36b45d45cba6cd0dccebf91b2cc4b4da9 GIT_REPOSITORY https://github.com/Blosc/c-blosc.git GIT_TAG e63775855294b50820ef44d1b157f4de1cc38d3e #v1.17.0 - DEPENDS ${ZLIB_PKG} CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS @@ -243,8 +240,7 @@ add_debug_dep(dep_blosc) ExternalProject_Add(dep_openexr EXCLUDE_FROM_ALL 1 GIT_REPOSITORY https://github.com/openexr/openexr.git - GIT_TAG eae0e337c9f5117e78114fd05f7a415819df413a #v2.4.0 - DEPENDS ${ZLIB_PKG} + GIT_TAG eae0e337c9f5117e78114fd05f7a415819df413a #v2.4.0 CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 4f89fe82c..ee8a22c4f 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -19,6 +19,7 @@ prusaslicer_add_cmake_project(wxWidgets -DwxBUILD_PRECOMP=ON ${_wx_toolkit} "-DCMAKE_DEBUG_POSTFIX:STRING=" + -DwxBUILD_DEBUG_LEVEL=0 -DwxUSE_DETECT_SM=OFF -DwxUSE_UNICODE=ON -DwxUSE_OPENGL=ON diff --git a/resources/shaders/gouraud_light.fs b/resources/shaders/gouraud_light.fs new file mode 100644 index 000000000..1a58abc85 --- /dev/null +++ b/resources/shaders/gouraud_light.fs @@ -0,0 +1,11 @@ +#version 110 + +uniform vec4 uniform_color; + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/gouraud_light.vs b/resources/shaders/gouraud_light.vs new file mode 100644 index 000000000..d4f71938a --- /dev/null +++ b/resources/shaders/gouraud_light.vs @@ -0,0 +1,38 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 normal = normalize(gl_NormalMatrix * gl_Normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = ftransform(); +} diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 891554c76..12058c3f6 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -565,10 +565,6 @@ int CLI::run(int argc, char **argv) gui->mainframe->load_config(m_extra_config); }); int result = wxEntry(argc, argv); -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - //FIXME this is a workaround for the PrusaSlicer 2.1 release. - _3DScene::destroy(); -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER return result; #else /* SLIC3R_GUI */ // No GUI support. Just print out a help. diff --git a/src/imgui/README.md b/src/imgui/README.md index 00d78ed01..83f461996 100644 --- a/src/imgui/README.md +++ b/src/imgui/README.md @@ -3,3 +3,8 @@ For more information go to https://github.com/ocornut/imgui THIS DIRECTORY CONTAINS THE imgui-1.75 58b3e02 SOURCE DISTRIBUTION. + +Customized with the following commits: +042880ba2df913916b2cc77f7bb677e07bfd2c58 +67c55c74901f1d337ef08f2090a87cfb4263bb0f +a94c952b40d36b1505fb77b87c0dd739e1034659 \ No newline at end of file diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index c425bbef2..eb47459ea 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -107,6 +107,13 @@ namespace ImGui const char ColorMarkerStart = 0x2; // STX const char ColorMarkerEnd = 0x3; // ETX + // Special ASCII characters are used here as a ikons markers + const char PrintIconMarker = 0x4; + const char PrinterIconMarker = 0x5; + const char PrinterSlaIconMarker = 0x6; + const char FilamentIconMarker = 0x7; + const char MaterialIconMarker = 0x8; + // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/libslic3r/CustomGCode.cpp b/src/libslic3r/CustomGCode.cpp index 824bcdd93..72c5c20de 100644 --- a/src/libslic3r/CustomGCode.cpp +++ b/src/libslic3r/CustomGCode.cpp @@ -1,6 +1,10 @@ #include "CustomGCode.hpp" #include "Config.hpp" +#if ENABLE_GCODE_VIEWER +#include "GCode.hpp" +#else #include "GCode/PreviewData.hpp" +#endif // ENABLE_GCODE_VIEWER #include "GCodeWriter.hpp" namespace Slic3r { @@ -17,8 +21,12 @@ extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrint return; if (info.gcodes.empty() && ! colorprint_heights->values.empty()) { // Convert the old colorprint_heighs only if there is no equivalent data in a new format. - const std::vector& colors = GCodePreviewData::ColorPrintColors(); - const auto& colorprint_values = colorprint_heights->values; +#if ENABLE_GCODE_VIEWER + const std::vector& colors = ColorPrintColors::get(); +#else + const std::vector& colors = GCodePreviewData::ColorPrintColors(); +#endif // ENABLE_GCODE_VIEWER + const auto& colorprint_values = colorprint_heights->values; info.gcodes.clear(); info.gcodes.reserve(colorprint_values.size()); int i = 0; diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index b88520b55..d83249939 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -1361,7 +1361,6 @@ static void traverse_graph_generate_polylines( continue; } - dont_connect: // No way to continue the current polyline. Take the rest of the line up to the outer contour. // This will finish the polyline, starting another polyline at a new point. going_up ? ++ it : -- it; @@ -1442,6 +1441,8 @@ struct MonotonousRegionLink AntPath *next_flipped; }; +// Matrix of paths (AntPath) connecting ends of MontonousRegions. +// AntPath lengths and their derived visibilities refer to the length of the perimeter line if such perimeter segment exists. class AntPathMatrix { public: @@ -1456,6 +1457,12 @@ public: // From end of one region to the start of another region, both flipped or not flipped. m_matrix(regions.size() * regions.size() * 4, AntPath{ -1., -1., initial_pheromone}) {} + void update_inital_pheromone(float initial_pheromone) + { + for (AntPath &ap : m_matrix) + ap.pheromone = initial_pheromone; + } + AntPath& operator()(const MonotonousRegion ®ion_from, bool flipped_from, const MonotonousRegion ®ion_to, bool flipped_to) { int row = 2 * int(®ion_from - m_regions.data()) + flipped_from; @@ -1478,7 +1485,7 @@ public: // Just apply the Eucledian distance of the end points. path.length = unscale(Vec2f(vline_to.pos - vline_from.pos, vline_to.intersections[i_to].pos() - vline_from.intersections[i_from].pos()).norm()); } - path.visibility = 1. / (path.length + EPSILON); + path.visibility = 1.f / (path.length + float(EPSILON)); } return path; } @@ -1497,7 +1504,7 @@ private: const ExPolygonWithOffset &m_poly_with_offset; const std::vector &m_segs; // From end of one region to the start of another region, both flipped or not flipped. - //FIXME one may possibly use sparse representation of the matrix. + //FIXME one may possibly use sparse representation of the matrix, likely using hashing. std::vector m_matrix; }; @@ -1724,7 +1731,98 @@ static std::vector generate_montonous_regions(std::vector ®ions, std::vector &segs) +// Traverse path, calculate length of the draw for the purpose of optimization. +// This function is very similar to polylines_from_paths() in the way how it traverses the path, but +// polylines_from_paths() emits a path, while this function just calculates the path length. +static float montonous_region_path_length(const MonotonousRegion ®ion, bool dir, const ExPolygonWithOffset &poly_with_offset, const std::vector &segs) +{ + // From the initial point (i_vline, i_intersection), follow a path. + int i_intersection = region.left_intersection_point(dir); + int i_vline = region.left.vline; + float total_length = 0.; + bool no_perimeter = false; + Vec2f last_point; + + for (;;) { + const SegmentedIntersectionLine &vline = segs[i_vline]; + const SegmentIntersection *it = &vline.intersections[i_intersection]; + const bool going_up = it->is_low(); + + if (no_perimeter) + total_length += (last_point - Vec2f(vline.pos, (it + (going_up ? - 1 : 1))->pos())).norm(); + + int iright = it->right_horizontal(); + if (going_up) { + // Traverse the complete vertical segment up to the inner contour. + for (;;) { + do { + ++ it; + iright = std::max(iright, it->right_horizontal()); + assert(it->is_inner()); + } while (it->type != SegmentIntersection::INNER_HIGH || (it + 1)->type != SegmentIntersection::OUTER_HIGH); + int inext = it->vertical_up(); + if (inext == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid) + break; + assert(it->iContour == vline.intersections[inext].iContour); + it = vline.intersections.data() + inext; + } + } else { + // Going down. + assert(it->is_high()); + assert(i_intersection > 0); + for (;;) { + do { + -- it; + if (int iright_new = it->right_horizontal(); iright_new != -1) + iright = iright_new; + assert(it->is_inner()); + } while (it->type != SegmentIntersection::INNER_LOW || (it - 1)->type != SegmentIntersection::OUTER_LOW); + int inext = it->vertical_down(); + if (inext == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid) + break; + assert(it->iContour == vline.intersections[inext].iContour); + it = vline.intersections.data() + inext; + } + } + + if (i_vline == region.right.vline) + break; + + int inext = it->right_horizontal(); + if (inext != -1 && it->next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) { + // Summarize length of the connection line along the perimeter. + //FIXME should it be weighted with a lower weight than non-extruding connection line? What weight? + // Taking half of the length. + total_length += 0.5f * float(measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline, it - vline.intersections.data(), inext)); + // Don't add distance to the next vertical line start to the total length. + no_perimeter = false; + i_intersection = inext; + } else { + // Finish the current vertical line, + going_up ? ++ it : -- it; + assert(it->is_outer()); + assert(it->is_high() == going_up); + // Mark the end of this vertical line. + last_point = Vec2f(vline.pos, it->pos()); + // Remember to add distance to the last point. + no_perimeter = true; + if (inext == -1) { + // Find the end of the next overlapping vertical segment. + const SegmentedIntersectionLine &vline_right = segs[i_vline + 1]; + const SegmentIntersection *right = going_up ? + &vertical_run_top(vline_right, vline_right.intersections[iright]) : &vertical_run_bottom(vline_right, vline_right.intersections[iright]); + i_intersection = int(right - vline_right.intersections.data()); + } else + i_intersection = inext; + } + + ++ i_vline; + } + + return unscale(total_length); +} + +static void connect_monotonous_regions(std::vector ®ions, const ExPolygonWithOffset &poly_with_offset, std::vector &segs) { // Map from low intersection to left / right side of a monotonous region. using MapType = std::pair; @@ -1816,6 +1914,20 @@ static void connect_monotonous_regions(std::vector ®ions, s } } #endif /* NDEBUG */ + + // Fill in sum length of connecting lines of a region. This length is used for optimizing the infill path for minimum length. + for (MonotonousRegion ®ion : regions) { + region.len1 = montonous_region_path_length(region, false, poly_with_offset, segs); + region.len2 = montonous_region_path_length(region, true, poly_with_offset, segs); + // Subtract the smaller length from the longer one, so we will optimize just with the positive difference of the two. + if (region.len1 > region.len2) { + region.len1 -= region.len2; + region.len2 = 0; + } else { + region.len2 -= region.len1; + region.len1 = 0; + } + } } // Raad Salman: Algorithms for the Precedence Constrained Generalized Travelling Salesperson Problem @@ -1851,6 +1963,7 @@ inline void print_ant(const std::string& fmt, TArgs&&... args) { } // Find a run through monotonous infill blocks using an 'Ant colony" optimization method. +// http://www.scholarpedia.org/article/Ant_colony_optimization static std::vector chain_monotonous_regions( std::vector ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector &segs, std::mt19937_64 &rng) { @@ -1940,14 +2053,18 @@ static std::vector chain_monotonous_regions( }; #endif /* NDEBUG */ - // How many times to repeat the ant simulation. - constexpr int num_rounds = 10; + // How many times to repeat the ant simulation (number of ant generations). + constexpr int num_rounds = 25; + // After how many rounds without an improvement to exit? + constexpr int num_rounds_no_change_exit = 8; // With how many ants each of the run will be performed? - constexpr int num_ants = 10; - // Base (initial) pheromone level. - constexpr float pheromone_initial_deposit = 0.5f; + const int num_ants = std::min(regions.size(), 10); + // Base (initial) pheromone level. This value will be adjusted based on the length of the first greedy path found. + float pheromone_initial_deposit = 0.5f; // Evaporation rate of pheromones. constexpr float pheromone_evaporation = 0.1f; + // Evaporation rate to diversify paths taken by individual ants. + constexpr float pheromone_diversification = 0.1f; // Probability at which to take the next best path. Otherwise take the the path based on the cost distribution. constexpr float probability_take_best = 0.9f; // Exponents of the cost function. @@ -1956,6 +2073,73 @@ static std::vector chain_monotonous_regions( AntPathMatrix path_matrix(regions, poly_with_offset, segs, pheromone_initial_deposit); + // Find an initial path in a greedy way, set the initial pheromone value to 10% of the cost of the greedy path. + { + // Construct the first path in a greedy way to calculate an initial value of the pheromone value. + queue = queue_initial; + left_neighbors_unprocessed = left_neighbors_unprocessed_initial; + assert(validate_unprocessed()); + // Pick the last of the queue. + MonotonousRegionLink path_end { queue.back(), false }; + queue.pop_back(); + -- left_neighbors_unprocessed[path_end.region - regions.data()]; + + float total_length = path_end.region->length(false); + while (! queue.empty() || ! path_end.region->right_neighbors.empty()) { + // Chain. + MonotonousRegion ®ion = *path_end.region; + bool dir = path_end.flipped; + NextCandidate next_candidate; + next_candidate.probability = 0; + for (MonotonousRegion *next : region.right_neighbors) { + int &unprocessed = left_neighbors_unprocessed[next - regions.data()]; + assert(unprocessed > 1); + if (left_neighbors_unprocessed[next - regions.data()] == 2) { + // Dependencies of the successive blocks are satisfied. + AntPath &path1 = path_matrix(region, dir, *next, false); + AntPath &path2 = path_matrix(region, dir, *next, true); + if (path1.visibility > next_candidate.probability) + next_candidate = { next, &path1, &path1, path1.visibility, false }; + if (path2.visibility > next_candidate.probability) + next_candidate = { next, &path2, &path2, path2.visibility, true }; + } + } + bool from_queue = next_candidate.probability == 0; + if (from_queue) { + for (MonotonousRegion *next : queue) { + AntPath &path1 = path_matrix(region, dir, *next, false); + AntPath &path2 = path_matrix(region, dir, *next, true); + if (path1.visibility > next_candidate.probability) + next_candidate = { next, &path1, &path1, path1.visibility, false }; + if (path2.visibility > next_candidate.probability) + next_candidate = { next, &path2, &path2, path2.visibility, true }; + } + } + // Move the other right neighbors with satisified constraints to the queue. + for (MonotonousRegion *next : region.right_neighbors) + if (-- left_neighbors_unprocessed[next - regions.data()] == 1 && next_candidate.region != next) + queue.emplace_back(next); + if (from_queue) { + // Remove the selected path from the queue. + auto it = std::find(queue.begin(), queue.end(), next_candidate.region); + assert(it != queue.end()); + *it = queue.back(); + queue.pop_back(); + } + // Extend the path. + MonotonousRegion *next_region = next_candidate.region; + bool next_dir = next_candidate.dir; + total_length += next_region->length(next_dir) + path_matrix(*path_end.region, path_end.flipped, *next_region, next_dir).length; + path_end = { next_region, next_dir }; + assert(left_neighbors_unprocessed[next_region - regions.data()] == 1); + left_neighbors_unprocessed[next_region - regions.data()] = 0; + } + + // Set an initial pheromone value to 10% of the greedy path's value. + pheromone_initial_deposit = 0.1 / total_length; + path_matrix.update_inital_pheromone(pheromone_initial_deposit); + } + // Probability (unnormalized) of traversing a link between two monotonous regions. auto path_probability = [pheromone_alpha, pheromone_beta](AntPath &path) { return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta); @@ -1966,8 +2150,10 @@ static std::vector chain_monotonous_regions( ++ irun; #endif /* SLIC3R_DEBUG_ANTS */ - for (int round = 0; round < num_rounds; ++ round) + int num_rounds_no_change = 0; + for (int round = 0; round < num_rounds && num_rounds_no_change < num_rounds_no_change_exit; ++ round) { + bool improved = false; for (int ant = 0; ant < num_ants; ++ ant) { // Find a new path following the pheromones deposited by the previous ants. @@ -1977,6 +2163,8 @@ static std::vector chain_monotonous_regions( left_neighbors_unprocessed = left_neighbors_unprocessed_initial; assert(validate_unprocessed()); // Pick randomly the first from the queue at random orientation. + //FIXME picking the 1st monotonous region should likely be done based on accumulated pheromone level as well, + // but the inefficiency caused by the random pick of the 1st monotonous region is likely insignificant. int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng); path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 }); *(queue.begin() + first_idx) = std::move(queue.back()); @@ -2051,7 +2239,7 @@ static std::vector chain_monotonous_regions( for (std::vector::iterator it_next_candidate = next_candidates.begin(); it_next_candidate != next_candidates.begin() + num_direct_neighbors; ++ it_next_candidate) if ((queue.empty() || it_next_candidate->region != queue.back()) && it_next_candidate->region != take_path->region) queue.emplace_back(it_next_candidate->region); - if (take_path - next_candidates.begin() >= num_direct_neighbors) { + if (size_t(take_path - next_candidates.begin()) >= num_direct_neighbors) { // Remove the selected path from the queue. auto it = std::find(queue.begin(), queue.end(), take_path->region); assert(it != queue.end()); @@ -2083,8 +2271,10 @@ static std::vector chain_monotonous_regions( path.back().flipped == path.back().region->flips ? path.back().region->right.high : path.back().region->right.low, path.back().flipped == path.back().region->flips ? path.back().region->right.low : path.back().region->right.high); - // Update pheromones along this link. - take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit; + // Update pheromones along this link, see Ant Colony System (ACS) update rule. + // http://www.scholarpedia.org/article/Ant_colony_optimization + // The goal here is to lower the pheromone trace for paths taken to diversify the next path picked in the same batch of ants. + take_path->link->pheromone = (1.f - pheromone_diversification) * take_path->link->pheromone + pheromone_diversification * pheromone_initial_deposit; assert(validate_unprocessed()); } @@ -2104,18 +2294,33 @@ static std::vector chain_monotonous_regions( if (path_length < best_path_length) { best_path_length = path_length; std::swap(best_path, path); +#if 0 // #if ! defined(SLIC3R_DEBUG_ANTS) && ! defined(ndebug) + if (round == 0 && ant == 0) + std::cout << std::endl; + std::cout << Slic3r::format("round %1% ant %2% path length %3%", round, ant, path_length) << std::endl; +#endif + if (path_length == 0) + // Perfect path found. + goto end; + improved = true; } } - // Reinforce the path feromones with the best path. - float total_cost = best_path_length + EPSILON; + // Reinforce the path pheromones with the best path. + float total_cost = best_path_length + float(EPSILON); for (size_t i = 0; i + 1 < path.size(); ++ i) { MonotonousRegionLink &link = path[i]; link.next->pheromone = (1.f - pheromone_evaporation) * link.next->pheromone + pheromone_evaporation / total_cost; } + + if (improved) + num_rounds_no_change = 0; + else + ++ num_rounds_no_change; } - return best_path; +end: + return best_path; } // Traverse path, produce polylines. @@ -2195,7 +2400,6 @@ static void polylines_from_paths(const std::vector &path, int inext = it->vertical_up(); if (inext == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid) break; - const Polygon &poly = poly_with_offset.contour(it->iContour); assert(it->iContour == vline.intersections[inext].iContour); emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, it->has_left_vertical_up()); it = vline.intersections.data() + inext; @@ -2215,7 +2419,6 @@ static void polylines_from_paths(const std::vector &path, int inext = it->vertical_down(); if (inext == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid) break; - const Polygon &poly = poly_with_offset.contour(it->iContour); assert(it->iContour == vline.intersections[inext].iContour); emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, it->has_right_vertical_down()); it = vline.intersections.data() + inext; @@ -2285,8 +2488,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP ExPolygonWithOffset poly_with_offset( surface->expolygon, - rotate_vector.first, - scale_(this->overlap - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing), - scale_(this->overlap - 0.5 * this->spacing)); + float(scale_(this->overlap - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing)), + float(scale_(this->overlap - 0.5 * this->spacing))); if (poly_with_offset.n_contours_inner == 0) { // Not a single infill line fits. //FIXME maybe one shall trigger the gap fill here? @@ -2317,7 +2520,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP size_t n_vlines = (bounding_box.max(0) - bounding_box.min(0) + line_spacing - 1) / line_spacing; coord_t x0 = bounding_box.min(0); if (params.full_infill()) - x0 += (line_spacing + SCALED_EPSILON) / 2; + x0 += (line_spacing + coord_t(SCALED_EPSILON)) / 2; #ifdef SLIC3R_DEBUG static int iRun = 0; @@ -2359,7 +2562,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP bool monotonous_infill = params.monotonous; // || params.density > 0.99; if (monotonous_infill) { std::vector regions = generate_montonous_regions(segs); - connect_monotonous_regions(regions, segs); + connect_monotonous_regions(regions, poly_with_offset, segs); if (! regions.empty()) { std::mt19937_64 rng; std::vector path = chain_monotonous_regions(regions, poly_with_offset, segs, rng); @@ -2478,10 +2681,10 @@ Polylines FillCubic::fill_surface(const Surface *surface, const FillParams ¶ params3.dont_connect = true; Polylines polylines_out; coordf_t dx = sqrt(0.5) * z; - if (! fill_surface_by_lines(surface, params2, 0.f, dx, polylines_out) || - ! fill_surface_by_lines(surface, params2, float(M_PI / 3.), - dx, polylines_out) || + if (! fill_surface_by_lines(surface, params2, 0.f, float(dx), polylines_out) || + ! fill_surface_by_lines(surface, params2, float(M_PI / 3.), - float(dx), polylines_out) || // Rotated by PI*2/3 + PI to achieve reverse sloping wall. - ! fill_surface_by_lines(surface, params3, float(M_PI * 2. / 3.), dx, polylines_out)) { + ! fill_surface_by_lines(surface, params3, float(M_PI * 2. / 3.), float(dx), polylines_out)) { printf("FillCubic::fill_surface() failed to fill a region.\n"); } return polylines_out; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 10636082b..584d1a1e3 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -572,6 +572,10 @@ std::string WipeTowerIntegration::finalize(GCode &gcodegen) return gcode; } +#if ENABLE_GCODE_VIEWER +const std::vector ColorPrintColors::Colors = { "#C0392B", "#E67E22", "#F1C40F", "#27AE60", "#1ABC9C", "#2980B9", "#9B59B6" }; +#endif // ENABLE_GCODE_VIEWER + #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) // Collect pairs of object_layer + support_layer sorted by print_z. @@ -699,7 +703,7 @@ std::vector>> GCode::collec } #if ENABLE_GCODE_VIEWER -void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) +void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) #else void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) #endif // ENABLE_GCODE_VIEWER @@ -724,7 +728,9 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ if (file == nullptr) throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); +#if !ENABLE_GCODE_VIEWER m_enable_analyzer = preview_data != nullptr; +#endif // !ENABLE_GCODE_VIEWER try { m_placeholder_parser_failed_templates.clear(); @@ -778,16 +784,14 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_ m_silent_time_estimator.reset(); } +#if !ENABLE_GCODE_VIEWER // starts analyzer calculations if (m_enable_analyzer) { -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - m_analyzer.close_debug_output_file(); -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data" << log_memory_info(); m_analyzer.calc_gcode_preview_data(*preview_data, [print]() { print->throw_if_canceled(); }); m_analyzer.reset(); } +#endif // !ENABLE_GCODE_VIEWER if (rename_file(path_tmp, path)) throw std::runtime_error( @@ -877,7 +881,14 @@ namespace DoExport { } } - static void init_gcode_analyzer(const PrintConfig &config, GCodeAnalyzer &analyzer) +#if ENABLE_GCODE_VIEWER + static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor) + { + processor.reset(); + processor.apply_config(config); + } +#else + static void init_gcode_analyzer(const PrintConfig &config, GCodeAnalyzer &analyzer) { // resets analyzer analyzer.reset(); @@ -901,17 +912,6 @@ namespace DoExport { // tell analyzer about the gcode flavor analyzer.set_gcode_flavor(config.gcode_flavor); - -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - analyzer.open_debug_output_file(); -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - } - -#if ENABLE_GCODE_VIEWER - static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor) - { - processor.reset(); - processor.apply_config(config); } #endif // ENABLE_GCODE_VIEWER @@ -1145,15 +1145,22 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu DoExport::init_time_estimators(print.config(), // modifies the following: m_normal_time_estimator, m_silent_time_estimator, m_silent_time_estimator_enabled); - DoExport::init_gcode_analyzer(print.config(), m_analyzer); #if ENABLE_GCODE_VIEWER DoExport::init_gcode_processor(print.config(), m_processor); +#else + DoExport::init_gcode_analyzer(print.config(), m_analyzer); #endif // ENABLE_GCODE_VIEWER // resets analyzer's tracking data +#if ENABLE_GCODE_VIEWER + m_last_mm3_per_mm = 0.0f; + m_last_width = 0.0f; + m_last_height = 0.0f; +#else m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; m_last_width = GCodeAnalyzer::Default_Width; m_last_height = GCodeAnalyzer::Default_Height; +#endif // ENABLE_GCODE_VIEWER // How many times will be change_layer() called? // change_layer() in turn increments the progress bar status. @@ -1333,13 +1340,13 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // Set extruder(s) temperature before and after start G-code. this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false); - if (m_enable_analyzer) - // adds tag for analyzer - _write_format(file, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); - #if ENABLE_GCODE_VIEWER // adds tag for processor _write_format(file, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), erCustom); +#else + if (m_enable_analyzer) + // adds tag for analyzer + _write_format(file, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); #endif // ENABLE_GCODE_VIEWER // Write the custom start G-code @@ -1491,13 +1498,13 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu _write(file, this->retract()); _write(file, m_writer.set_fan(false)); - if (m_enable_analyzer) - // adds tag for analyzer - _write_format(file, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); - #if ENABLE_GCODE_VIEWER // adds tag for processor _write_format(file, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), erCustom); +#else + if (m_enable_analyzer) + // adds tag for analyzer + _write_format(file, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); #endif // ENABLE_GCODE_VIEWER // Process filament-specific gcode in extruder order. @@ -1821,11 +1828,12 @@ namespace ProcessLayer { assert(m600_extruder_before_layer >= 0); // Color Change or Tool Change as Color Change. - // add tag for analyzer - gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; #if ENABLE_GCODE_VIEWER // add tag for processor gcode += "; " + GCodeProcessor::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; +#else + // add tag for analyzer + gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; #endif // ENABLE_GCODE_VIEWER // add tag for time estimator gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; @@ -1846,11 +1854,12 @@ namespace ProcessLayer { if (custom_code == PausePrintCode) // Pause print { - // add tag for analyzer - gcode += "; " + GCodeAnalyzer::Pause_Print_Tag + "\n"; #if ENABLE_GCODE_VIEWER // add tag for processor gcode += "; " + GCodeProcessor::Pause_Print_Tag + "\n"; +#else + // add tag for analyzer + gcode += "; " + GCodeAnalyzer::Pause_Print_Tag + "\n"; #endif // ENABLE_GCODE_VIEWER //! FIXME_in_fw show message during print pause if (!pause_print_msg.empty()) @@ -1860,11 +1869,12 @@ namespace ProcessLayer } else // custom Gcode { - // add tag for analyzer - gcode += "; " + GCodeAnalyzer::Custom_Code_Tag + "\n"; #if ENABLE_GCODE_VIEWER // add tag for processor gcode += "; " + GCodeProcessor::Custom_Code_Tag + "\n"; +#else + // add tag for analyzer + gcode += "; " + GCodeAnalyzer::Custom_Code_Tag + "\n"; #endif // ENABLE_GCODE_VIEWER // add tag for time estimator //gcode += "; " + GCodeTimeEstimator::Custom_Code_Tag + "\n"; @@ -2218,9 +2228,15 @@ void GCode::process_layer( m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) : this->set_extruder(extruder_id, print_z); +#if ENABLE_GCODE_VIEWER + // let analyzer tag generator aware of a role type change + if (layer_tools.has_wipe_tower && m_wipe_tower) + m_last_processor_extrusion_role = erWipeTower; +#else // let analyzer tag generator aware of a role type change if (m_enable_analyzer && layer_tools.has_wipe_tower && m_wipe_tower) m_last_analyzer_extrusion_role = erWipeTower; +#endif // ENABLE_GCODE_VIEWER if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) { const std::pair loops = loops_it->second; @@ -2324,11 +2340,13 @@ void GCode::process_layer( if (m_cooling_buffer) gcode = m_cooling_buffer->process_layer(gcode, layer.id()); +#if !ENABLE_GCODE_VIEWER // add tag for analyzer if (gcode.find(GCodeAnalyzer::Pause_Print_Tag) != gcode.npos) gcode += "\n; " + GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag + "\n"; else if (gcode.find(GCodeAnalyzer::Custom_Code_Tag) != gcode.npos) gcode += "\n; " + GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag + "\n"; +#endif // !ENABLE_GCODE_VIEWER #ifdef HAS_PRESSURE_EQUALIZER // Apply pressure equalization if enabled; @@ -2342,9 +2360,11 @@ void GCode::process_layer( BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << ", time estimator memory: " << format_memsize_MB(m_normal_time_estimator.memory_used() + (m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0)) << - ", analyzer memory: " << +#if !ENABLE_GCODE_VIEWER + ", analyzer memory: " << format_memsize_MB(m_analyzer.memory_used()) << - log_memory_info(); +#endif // !ENABLE_GCODE_VIEWER + log_memory_info(); } void GCode::apply_print_config(const PrintConfig &print_config) @@ -2984,8 +3004,12 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill void GCode::_write(FILE* file, const char *what) { if (what != nullptr) { +#if ENABLE_GCODE_VIEWER + const char* gcode = what; +#else // apply analyzer, if enabled const char* gcode = m_enable_analyzer ? m_analyzer.process_gcode(what).c_str() : what; +#endif // !ENABLE_GCODE_VIEWER // writes string to file fwrite(gcode, 1, ::strlen(gcode), file); @@ -3132,57 +3156,73 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } // adds analyzer tags and updates analyzer's tracking data +#if !ENABLE_GCODE_VIEWER if (m_enable_analyzer) { +#endif // !ENABLE_GCODE_VIEWER // PrusaMultiMaterial::Writer may generate GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines without updating m_last_height and m_last_width // so, if the last role was erWipeTower we force export of GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines +#if ENABLE_GCODE_VIEWER + bool last_was_wipe_tower = (m_last_processor_extrusion_role == erWipeTower); +#else bool last_was_wipe_tower = (m_last_analyzer_extrusion_role == erWipeTower); +#endif // ENABLE_GCODE_VIEWER char buf[64]; +#if ENABLE_GCODE_VIEWER + if (path.role() != m_last_processor_extrusion_role) + { + m_last_processor_extrusion_role = path.role(); + sprintf(buf, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), int(m_last_processor_extrusion_role)); + gcode += buf; + } +#else if (path.role() != m_last_analyzer_extrusion_role) { m_last_analyzer_extrusion_role = path.role(); sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), int(m_last_analyzer_extrusion_role)); -#if ENABLE_GCODE_VIEWER - gcode += buf; - sprintf(buf, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), int(m_last_analyzer_extrusion_role)); -#endif // ENABLE_GCODE_VIEWER gcode += buf; } +#endif // ENABLE_GCODE_VIEWER if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm)) { m_last_mm3_per_mm = path.mm3_per_mm; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); - gcode += buf; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); gcode += buf; +#else + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm); + gcode += buf; #endif // ENABLE_GCODE_VIEWER } if (last_was_wipe_tower || (m_last_width != path.width)) { m_last_width = path.width; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), m_last_width); - gcode += buf; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), m_last_width); gcode += buf; +#else + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), m_last_width); + gcode += buf; #endif // ENABLE_GCODE_VIEWER } if (last_was_wipe_tower || (m_last_height != path.height)) { m_last_height = path.height; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_last_height); - gcode += buf; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_last_height); gcode += buf; +#else + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_last_height); + gcode += buf; #endif // ENABLE_GCODE_VIEWER } +#if !ENABLE_GCODE_VIEWER } +#endif // !ENABLE_GCODE_VIEWER std::string comment; if (m_enable_cooling_markers) { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index f6668ad3d..546c42575 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -16,10 +16,11 @@ #include "GCode/WipeTower.hpp" #if ENABLE_GCODE_VIEWER #include "GCode/GCodeProcessor.hpp" +#else +#include "GCode/Analyzer.hpp" #endif // ENABLE_GCODE_VIEWER #include "GCodeTimeEstimator.hpp" #include "EdgeGrid.hpp" -#include "GCode/Analyzer.hpp" #include "GCode/ThumbnailData.hpp" #include @@ -33,7 +34,9 @@ namespace Slic3r { // Forward declarations. class GCode; +#if !ENABLE_GCODE_VIEWER class GCodePreviewData; +#endif // !ENABLE_GCODE_VIEWER class AvoidCrossingPerimeters { public: @@ -138,6 +141,15 @@ private: double m_last_wipe_tower_print_z = 0.f; }; +#if ENABLE_GCODE_VIEWER +class ColorPrintColors +{ + static const std::vector Colors; +public: + static const std::vector& get() { return Colors; } +}; +#endif // ENABLE_GCODE_VIEWER + class GCode { public: GCode() : @@ -145,17 +157,27 @@ public: m_enable_loop_clipping(true), m_enable_cooling_markers(false), m_enable_extrusion_role_markers(false), +#if ENABLE_GCODE_VIEWER + m_last_processor_extrusion_role(erNone), +#else m_enable_analyzer(false), m_last_analyzer_extrusion_role(erNone), +#endif // ENABLE_GCODE_VIEWER m_layer_count(0), m_layer_index(-1), m_layer(nullptr), m_volumetric_speed(0), m_last_pos_defined(false), m_last_extrusion_role(erNone), +#if ENABLE_GCODE_VIEWER + m_last_mm3_per_mm(0.0f), + m_last_width(0.0f), + m_last_height(0.0f), +#else m_last_mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm), m_last_width(GCodeAnalyzer::Default_Width), m_last_height(GCodeAnalyzer::Default_Height), +#endif // ENABLE_GCODE_VIEWER m_brim_done(false), m_second_layer_things_done(false), m_normal_time_estimator(GCodeTimeEstimator::Normal), @@ -168,7 +190,7 @@ public: // throws std::runtime_exception on error, // throws CanceledException through print->throw_if_canceled(). #if ENABLE_GCODE_VIEWER - void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, GCodeProcessor::Result* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); + void do_export(Print* print, const char* path, GCodeProcessor::Result* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #else void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #endif // ENABLE_GCODE_VIEWER @@ -331,11 +353,16 @@ private: // Markers for the Pressure Equalizer to recognize the extrusion type. // The Pressure Equalizer removes the markers from the final G-code. bool m_enable_extrusion_role_markers; +#if ENABLE_GCODE_VIEWER + // Keeps track of the last extrusion role passed to the processor + ExtrusionRole m_last_processor_extrusion_role; +#else // Enableds the G-code Analyzer. // Extended markers will be added during G-code generation. // The G-code Analyzer will remove these comments from the final G-code. bool m_enable_analyzer; ExtrusionRole m_last_analyzer_extrusion_role; +#endif // ENABLE_GCODE_VIEWER // How many times will change_layer() be called? // change_layer() will update the progress bar. unsigned int m_layer_count; @@ -377,12 +404,12 @@ private: GCodeTimeEstimator m_silent_time_estimator; bool m_silent_time_estimator_enabled; - // Analyzer - GCodeAnalyzer m_analyzer; - #if ENABLE_GCODE_VIEWER // Processor GCodeProcessor m_processor; +#else + // Analyzer + GCodeAnalyzer m_analyzer; #endif // ENABLE_GCODE_VIEWER // Write a string into a file. diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index 974176dbd..d022b3798 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -8,19 +8,11 @@ #include "Print.hpp" #include -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT -#include -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT #include "Analyzer.hpp" #include "PreviewData.hpp" -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT -#include - -// don't worry, this is just temporary -static boost::nowide::ofstream g_debug_output; -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT +#if !ENABLE_GCODE_VIEWER static const std::string AXIS_STR = "XYZE"; static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; @@ -184,19 +176,6 @@ bool GCodeAnalyzer::is_valid_extrusion_role(ExtrusionRole role) return ((erPerimeter <= role) && (role < erMixed)); } -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT -void GCodeAnalyzer::open_debug_output_file() -{ - boost::filesystem::path path("d:/analyzer.output"); - g_debug_output.open(path.string()); -} - -void GCodeAnalyzer::close_debug_output_file() -{ - g_debug_output.close(); -} -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line) { // processes 'special' comments contained in line @@ -945,23 +924,6 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) Vec3f start_position = _get_start_position() + extruder_offset; Vec3f end_position = _get_end_position() + extruder_offset; it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_fan_speed(), _get_cp_color_id()); - -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - if (g_debug_output.good()) - { - g_debug_output << std::to_string(static_cast(type)); - g_debug_output << ", " << std::to_string(static_cast(_get_extrusion_role())); - g_debug_output << ", " << Slic3r::to_string(static_cast(end_position.cast())); - g_debug_output << ", " << std::to_string(extruder_id); - g_debug_output << ", " << std::to_string(_get_cp_color_id()); - g_debug_output << ", " << std::to_string(_get_feedrate()); - g_debug_output << ", " << std::to_string(_get_width()); - g_debug_output << ", " << std::to_string(_get_height()); - g_debug_output << ", " << std::to_string(_get_mm3_per_mm()); - g_debug_output << ", " << std::to_string(_get_fan_speed()); - g_debug_output << "\n"; - } -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT } bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const @@ -1231,3 +1193,5 @@ size_t GCodeAnalyzer::memory_used() const } } // namespace Slic3r + +#endif // !ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp index 9d16ab494..37d907259 100644 --- a/src/libslic3r/GCode/Analyzer.hpp +++ b/src/libslic3r/GCode/Analyzer.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_GCode_Analyzer_hpp_ #define slic3r_GCode_Analyzer_hpp_ +#if !ENABLE_GCODE_VIEWER + #include "../libslic3r.h" #include "../PrintConfig.hpp" #include "../ExtrusionEntity.hpp" @@ -147,11 +149,6 @@ public: static bool is_valid_extrusion_role(ExtrusionRole role); -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - void open_debug_output_file(); - void close_debug_output_file(); -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - private: // Processes the given gcode line void _process_gcode_line(GCodeReader& reader, const GCodeReader::GCodeLine& line); @@ -307,4 +304,6 @@ private: } // namespace Slic3r +#endif // !ENABLE_GCODE_VIEWER + #endif /* slic3r_GCode_Analyzer_hpp_ */ diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index 3aae15748..8aec327db 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -5,6 +5,8 @@ #include +#if !ENABLE_GCODE_VIEWER + //! macro used to mark string used at localization, #define L(s) (s) @@ -117,7 +119,8 @@ const Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[erCount] Color(1.0f, 1.0f, 0.0f, 1.0f), // erInternalInfill Color(1.0f, 0.0f, 1.0f, 1.0f), // erSolidInfill Color(0.0f, 1.0f, 1.0f, 1.0f), // erTopSolidInfill - Color(0.0f, 1.0f, 1.0f, 1.0f), // erIroning +// Color(1.0f, 0.7f, 0.61f, 1.0f), // erIroning + Color(1.0f, 0.55f, 0.41f, 1.0f), // erIroning Color(0.5f, 0.5f, 0.5f, 1.0f), // erBridgeInfill Color(1.0f, 1.0f, 1.0f, 1.0f), // erGapFill Color(0.5f, 0.0f, 0.0f, 1.0f), // erSkirt @@ -515,3 +518,5 @@ Color operator * (float f, const Color& color) } } // namespace Slic3r + +#endif // !ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp index c0f768088..930c1659e 100644 --- a/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_GCode_PreviewData_hpp_ #define slic3r_GCode_PreviewData_hpp_ +#if !ENABLE_GCODE_VIEWER + #include "../libslic3r.h" #include "../ExtrusionEntity.hpp" #include "../Point.hpp" @@ -391,4 +393,6 @@ public: } // namespace Slic3r +#endif // !ENABLE_GCODE_VIEWER + #endif /* slic3r_GCode_PreviewData_hpp_ */ diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index d5d060f77..3b5c2a159 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -21,9 +21,10 @@ TODO LIST #include #include -#include "Analyzer.hpp" #if ENABLE_GCODE_VIEWER #include "GCodeProcessor.hpp" +#else +#include "Analyzer.hpp" #endif // ENABLE_GCODE_VIEWER #include "BoundingBox.hpp" @@ -56,16 +57,16 @@ public: { // adds tag for analyzer: char buf[64]; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming - m_gcode += buf; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming - m_gcode += buf; +#else + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming #endif // ENABLE_GCODE_VIEWER - sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower); -#if ENABLE_GCODE_VIEWER m_gcode += buf; +#if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%d\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), erWipeTower); +#else + sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower); #endif // ENABLE_GCODE_VIEWER m_gcode += buf; change_analyzer_line_width(line_width); @@ -74,12 +75,12 @@ public: WipeTowerWriter& change_analyzer_line_width(float line_width) { // adds tag for analyzer: char buf[64]; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); - m_gcode += buf; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width); - m_gcode += buf; +#else + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); #endif // ENABLE_GCODE_VIEWER + m_gcode += buf; return *this; } @@ -88,12 +89,12 @@ public: float mm3_per_mm = (len == 0.f ? 0.f : area * e / len); // adds tag for analyzer: char buf[64]; - sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); - m_gcode += buf; #if ENABLE_GCODE_VIEWER sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); - m_gcode += buf; +#else + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); #endif // ENABLE_GCODE_VIEWER + m_gcode += buf; return *this; } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 98f595d91..90c9d0357 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -20,7 +20,9 @@ #include "SVG.hpp" #include #include "GCodeWriter.hpp" +#if !ENABLE_GCODE_VIEWER #include "GCode/PreviewData.hpp" +#endif // !ENABLE_GCODE_VIEWER namespace Slic3r { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index cd2c4957d..7b56030c7 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -411,6 +411,11 @@ public: return timestamp == other.get_timestamp(); } + template void serialize(Archive &ar) + { + ar(m_data); + } + private: std::map m_data; @@ -613,7 +618,8 @@ private: } template void load(Archive &ar) { bool has_convex_hull; - ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + ar(name, source, m_mesh, m_type, m_material_id, m_transformation, + m_is_splittable, has_convex_hull, m_supported_facets); cereal::load_by_value(ar, config); assert(m_mesh); if (has_convex_hull) { @@ -626,7 +632,8 @@ private: } template void save(Archive &ar) const { bool has_convex_hull = m_convex_hull.get() != nullptr; - ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + ar(name, source, m_mesh, m_type, m_material_id, m_transformation, + m_is_splittable, has_convex_hull, m_supported_facets); cereal::save_by_value(ar, config); if (has_convex_hull) cereal::save_optional(ar, m_convex_hull); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 6bc049412..0ffb5472c 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1628,7 +1628,7 @@ void Print::process() // write error into the G-code, cannot execute post-processing scripts). // It is up to the caller to show an error message. #if ENABLE_GCODE_VIEWER -std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) +std::string Print::export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb) #else std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb) #endif // ENABLE_GCODE_VIEWER @@ -1637,7 +1637,11 @@ std::string Print::export_gcode(const std::string& path_template, GCodePreviewDa // The following call may die if the output_filename_format template substitution fails. std::string path = this->output_filepath(path_template); std::string message; +#if ENABLE_GCODE_VIEWER + if (!path.empty() && result == nullptr) { +#else if (! path.empty() && preview_data == nullptr) { +#endif // ENABLE_GCODE_VIEWER // Only show the path if preview_data is not set -> running from command line. message = L("Exporting G-code"); message += " to "; @@ -1649,7 +1653,7 @@ std::string Print::export_gcode(const std::string& path_template, GCodePreviewDa // The following line may die for multiple reasons. GCode gcode; #if ENABLE_GCODE_VIEWER - gcode.do_export(this, path.c_str(), preview_data, result, thumbnail_cb); + gcode.do_export(this, path.c_str(), result, thumbnail_cb); #else gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb); #endif // ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 9ae0f1394..c358cd918 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -372,7 +372,7 @@ public: // Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file. // If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r). #if ENABLE_GCODE_VIEWER - std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); + std::string export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #else std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); #endif // ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 5d60d7513..29ad6057f 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -1292,7 +1292,7 @@ void SupportTreeBuildsteps::routing_headless() m_thr(); const auto R = double(m_support_pts[i].head_front_radius); - const double HWIDTH_MM = m_cfg.head_penetration_mm; + const double HWIDTH_MM = std::min(R, m_cfg.head_penetration_mm); // Exact support position Vec3d sph = m_support_pts[i].pos.cast(); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 255afc631..c22e504df 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -53,13 +53,8 @@ // Enable rendering of objects colored by facets' slope #define ENABLE_SLOPE_RENDERING (1 && ENABLE_2_3_0_ALPHA1) -// Moves GLCanvas3DManager from being a static member of _3DScene to be a normal member of GUI_App -#define ENABLE_NON_STATIC_CANVAS_MANAGER (1 && ENABLE_2_3_0_ALPHA1) - // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) -#define ENABLE_GCODE_VIEWER_DEBUG_OUTPUT (0 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) - #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index c33a0af59..b085fad45 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -27,8 +27,8 @@ set(SLIC3R_GUI_SOURCES GUI/GLShader.hpp GUI/GLCanvas3D.hpp GUI/GLCanvas3D.cpp - GUI/GLCanvas3DManager.hpp - GUI/GLCanvas3DManager.cpp + GUI/OpenGLManager.hpp + GUI/OpenGLManager.cpp GUI/Selection.hpp GUI/Selection.cpp GUI/Gizmos/GLGizmosManager.cpp @@ -55,6 +55,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoHollow.hpp GUI/GLSelectionRectangle.cpp GUI/GLSelectionRectangle.hpp + GUI/GLModel.hpp + GUI/GLModel.cpp GUI/GLTexture.hpp GUI/GLTexture.cpp GUI/GLToolbar.hpp @@ -218,6 +220,12 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES}) endif() +if (SLIC3R_STATIC) + # FIXME: This was previously exported by wx-config but the wxWidgets + # cmake build forgets this and the build fails in debug mode (or on raspberry release) + target_compile_definitions(libslic3r_gui PUBLIC -DwxDEBUG_LEVEL=0) +endif() + if(APPLE) target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY}) endif() diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 2cc9de38c..4ef867960 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -5,15 +5,24 @@ #include "libslic3r/Polygon.hpp" #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/BoundingBox.hpp" +#if ENABLE_GCODE_VIEWER +#include "libslic3r/Geometry.hpp" +#endif // ENABLE_GCODE_VIEWER #include "GUI_App.hpp" #include "PresetBundle.hpp" #include "GLCanvas3D.hpp" +#if ENABLE_GCODE_VIEWER +#include "3DScene.hpp" +#endif // ENABLE_GCODE_VIEWER #include #include #include +#if ENABLE_GCODE_VIEWER +#include +#endif // ENABLE_GCODE_VIEWER static const float GROUND_Z = -0.02f; @@ -119,13 +128,25 @@ const float* GeometryBuffer::get_vertices_data() const return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr; } +#if ENABLE_GCODE_VIEWER +const float Bed3D::Axes::DefaultStemRadius = 0.5f; +const float Bed3D::Axes::DefaultStemLength = 25.0f; +const float Bed3D::Axes::DefaultTipRadius = 2.5f * Bed3D::Axes::DefaultStemRadius; +const float Bed3D::Axes::DefaultTipLength = 5.0f; +#else const double Bed3D::Axes::Radius = 0.5; const double Bed3D::Axes::ArrowBaseRadius = 2.5 * Bed3D::Axes::Radius; const double Bed3D::Axes::ArrowLength = 5.0; +#endif // ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER +void Bed3D::Axes::set_stem_length(float length) +{ + m_stem_length = length; + m_arrow.reset(); +} +#else Bed3D::Axes::Axes() -: origin(Vec3d::Zero()) -, length(25.0 * Vec3d::Ones()) { m_quadric = ::gluNewQuadric(); if (m_quadric != nullptr) @@ -137,9 +158,46 @@ Bed3D::Axes::~Axes() if (m_quadric != nullptr) ::gluDeleteQuadric(m_quadric); } +#endif // ENABLE_GCODE_VIEWER void Bed3D::Axes::render() const { +#if ENABLE_GCODE_VIEWER + auto render_axis = [this](const Transform3f& transform, GLint color_id, const std::array& color) { + if (color_id >= 0) + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color.data())); + + glsafe(::glPushMatrix()); + glsafe(::glMultMatrixf(transform.data())); + m_arrow.render(); + glsafe(::glPopMatrix()); + }; + + m_arrow.init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); + if (!m_shader.init("gouraud_light.vs", "gouraud_light.fs")) + BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; + + if (!m_shader.is_initialized()) + return; + + glsafe(::glEnable(GL_DEPTH_TEST)); + + m_shader.start_using(); + GLint color_id = ::glGetUniformLocation(m_shader.get_shader_program_id(), "uniform_color"); + + // x axis + render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0f }).cast(), color_id, { 0.75f, 0.0f, 0.0f, 1.0f }); + + // y axis + render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0f }).cast(), color_id, { 0.0f, 0.75f, 0.0f, 1.0f }); + + // z axis + render_axis(Geometry::assemble_transform(m_origin).cast(), color_id, { 0.0f, 0.0f, 0.75f, 1.0f }); + + m_shader.stop_using(); + + glsafe(::glDisable(GL_DEPTH_TEST)); +#else if (m_quadric == nullptr) return; @@ -171,8 +229,10 @@ void Bed3D::Axes::render() const glsafe(::glDisable(GL_LIGHTING)); glsafe(::glDisable(GL_DEPTH_TEST)); +#endif // !ENABLE_GCODE_VIEWER } +#if !ENABLE_GCODE_VIEWER void Bed3D::Axes::render_axis(double length) const { ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); @@ -185,6 +245,7 @@ void Bed3D::Axes::render_axis(double length) const ::gluQuadricOrientation(m_quadric, GLU_INSIDE); ::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1); } +#endif // !ENABLE_GCODE_VIEWER Bed3D::Bed3D() : m_type(Custom) @@ -242,8 +303,13 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c m_model.reset(); // Set the origin and size for rendering the coordinate system axes. +#if ENABLE_GCODE_VIEWER + m_axes.set_origin({ 0.0, 0.0, static_cast(GROUND_Z) }); + m_axes.set_stem_length(0.1f * static_cast(m_bounding_box.max_size())); +#else m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); m_axes.length = 0.1 * m_bounding_box.max_size() * Vec3d::Ones(); +#endif // ENABLE_GCODE_VIEWER // Let the calee to update the UI. return true; @@ -290,7 +356,11 @@ void Bed3D::calc_bounding_boxes() const m_extended_bounding_box = m_bounding_box; // extend to contain axes +#if ENABLE_GCODE_VIEWER + m_extended_bounding_box.merge(m_axes.get_total_length() * Vec3d::Ones()); +#else m_extended_bounding_box.merge(m_axes.length + Axes::ArrowLength * Vec3d::Ones()); +#endif // ENABLE_GCODE_VIEWER // extend to contain model, if any if (!m_model.get_filename().empty()) @@ -410,7 +480,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const if (boost::algorithm::iends_with(m_texture_filename, ".svg")) { // use higher resolution images if graphic card and opengl version allow - GLint max_tex_size = GLCanvas3DManager::get_gl_info().get_max_tex_size(); + GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size(); if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename)) { // generate a temporary lower resolution texture to show while no main texture levels have been compressed diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index abdfca1fe..440468233 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -4,11 +4,16 @@ #include "GLTexture.hpp" #include "3DScene.hpp" #include "GLShader.hpp" +#if ENABLE_GCODE_VIEWER +#include "GLModel.hpp" +#endif // ENABLE_GCODE_VIEWER #include +#if !ENABLE_GCODE_VIEWER class GLUquadric; typedef class GLUquadric GLUquadricObj; +#endif // !ENABLE_GCODE_VIEWER namespace Slic3r { namespace GUI { @@ -45,22 +50,50 @@ public: class Bed3D { +#if ENABLE_GCODE_VIEWER + class Axes + { + static const float DefaultStemRadius; + static const float DefaultStemLength; + static const float DefaultTipRadius; + static const float DefaultTipLength; +#else struct Axes { static const double Radius; static const double ArrowBaseRadius; static const double ArrowLength; +#endif // ENABLE_GCODE_VIEWER + +#if ENABLE_GCODE_VIEWER + Vec3d m_origin{ Vec3d::Zero() }; + float m_stem_length{ DefaultStemLength }; + mutable GL_Model m_arrow; + mutable Shader m_shader; + + public: +#else Vec3d origin; Vec3d length; GLUquadricObj* m_quadric; +#endif // ENABLE_GCODE_VIEWER +#if !ENABLE_GCODE_VIEWER Axes(); ~Axes(); +#endif // !ENABLE_GCODE_VIEWER +#if ENABLE_GCODE_VIEWER + void set_origin(const Vec3d& origin) { m_origin = origin; } + void set_stem_length(float length); + float get_total_length() const { return m_stem_length + DefaultTipLength; } +#endif // ENABLE_GCODE_VIEWER void render() const; +#if !ENABLE_GCODE_VIEWER private: void render_axis(double length) const; +#endif // !ENABLE_GCODE_VIEWER }; public: diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index cac0910e2..29a4b84a3 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -5,11 +5,15 @@ #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp" #include "libslic3r/Geometry.hpp" +#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/PreviewData.hpp" +#endif // !ENABLE_GCODE_VIEWER #include "libslic3r/Print.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Slicing.hpp" +#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/Analyzer.hpp" +#endif // !ENABLE_GCODE_VIEWER #include "slic3r/GUI/BitmapCache.hpp" #include "libslic3r/Format/STL.hpp" #include "libslic3r/Utils.hpp" @@ -1853,10 +1857,6 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height thick_point_to_verts(point, width, height, volume); } -#if !ENABLE_NON_STATIC_CANVAS_MANAGER -GUI::GLCanvas3DManager _3DScene::s_canvas_mgr; -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - GLModel::GLModel() : m_filename("") { @@ -1944,6 +1944,7 @@ void GLModel::render() const glsafe(::glDisable(GL_BLEND)); } +#if !ENABLE_GCODE_VIEWER bool GLArrow::on_init() { Pointf3s vertices; @@ -2115,6 +2116,7 @@ bool GLCurvedArrow::on_init() m_volume.indexed_vertex_array.finalize_geometry(true); return true; } +#endif // !ENABLE_GCODE_VIEWER bool GLBed::on_init_from_file(const std::string& filename) { @@ -2147,41 +2149,4 @@ bool GLBed::on_init_from_file(const std::string& filename) return true; } -#if !ENABLE_NON_STATIC_CANVAS_MANAGER -std::string _3DScene::get_gl_info(bool format_as_html, bool extensions) -{ - return Slic3r::GUI::GLCanvas3DManager::get_gl_info().to_string(format_as_html, extensions); -} - -bool _3DScene::add_canvas(wxGLCanvas* canvas, GUI::Bed3D& bed, GUI::Camera& camera, GUI::GLToolbar& view_toolbar) -{ - return s_canvas_mgr.add(canvas, bed, camera, view_toolbar); -} - -bool _3DScene::remove_canvas(wxGLCanvas* canvas) -{ - return s_canvas_mgr.remove(canvas); -} - -void _3DScene::remove_all_canvases() -{ - s_canvas_mgr.remove_all(); -} - -bool _3DScene::init(wxGLCanvas* canvas) -{ - return s_canvas_mgr.init(canvas); -} - -void _3DScene::destroy() -{ - s_canvas_mgr.destroy(); -} - -GUI::GLCanvas3D* _3DScene::get_canvas(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_canvas(canvas); -} -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - } // namespace Slic3r diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 70d6fb016..86072754d 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -7,9 +7,6 @@ #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" -#if !ENABLE_NON_STATIC_CANVAS_MANAGER -#include "slic3r/GUI/GLCanvas3DManager.hpp" -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER #include @@ -645,6 +642,7 @@ protected: virtual bool on_init_from_file(const std::string& filename) { return false; } }; +#if !ENABLE_GCODE_VIEWER class GLArrow : public GLModel { protected: @@ -661,6 +659,7 @@ public: protected: bool on_init() override; }; +#endif // !ENABLE_GCODE_VIEWER class GLBed : public GLModel { @@ -668,30 +667,8 @@ protected: bool on_init_from_file(const std::string& filename) override; }; -#if ENABLE_NON_STATIC_CANVAS_MANAGER struct _3DScene -#else -class _3DScene -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER { -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - static GUI::GLCanvas3DManager s_canvas_mgr; -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - -#if !ENABLE_NON_STATIC_CANVAS_MANAGER -public: - static std::string get_gl_info(bool format_as_html, bool extensions); - - static bool add_canvas(wxGLCanvas* canvas, GUI::Bed3D& bed, GUI::Camera& camera, GUI::GLToolbar& view_toolbar); - static bool remove_canvas(wxGLCanvas* canvas); - static void remove_all_canvases(); - - static bool init(wxGLCanvas* canvas); - static void destroy(); - - static GUI::GLCanvas3D* get_canvas(wxGLCanvas* canvas); -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - static void thick_lines_to_verts(const Lines& lines, const std::vector& widths, const std::vector& heights, bool closed, double top_z, GLVolume& volume); static void thick_lines_to_verts(const Lines3& lines, const std::vector& widths, const std::vector& heights, bool closed, GLVolume& volume); static void extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume); diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 291949ce9..0f603c265 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -84,6 +84,9 @@ void AppConfig::set_defaults() if (get("custom_toolbar_size").empty()) set("custom_toolbar_size", "100"); + if (get("auto_toolbar_size").empty()) + set("auto_toolbar_size", "100"); + if (get("use_perspective_camera").empty()) set("use_perspective_camera", "1"); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index c8c344caa..32b7b8365 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -18,7 +18,9 @@ #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/GCode/PostProcessor.hpp" +#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/PreviewData.hpp" +#endif // !ENABLE_GCODE_VIEWER #include "libslic3r/Format/SL1.hpp" #include "libslic3r/libslic3r.h" @@ -89,7 +91,7 @@ void BackgroundSlicingProcess::process_fff() m_print->process(); wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id)); #if ENABLE_GCODE_VIEWER - m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_gcode_result, m_thumbnail_cb); + m_fff_print->export_gcode(m_temp_output_path, m_gcode_result, m_thumbnail_cb); #else m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb); #endif // ENABLE_GCODE_VIEWER @@ -385,9 +387,7 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn // Some FFF status was invalidated, and the G-code was not exported yet. // Let the G-code preview UI know that the final G-code preview is not valid. // In addition, this early memory deallocation reduces memory footprint. - if (m_gcode_preview_data != nullptr) - m_gcode_preview_data->reset(); - else if (m_gcode_result != nullptr) + if (m_gcode_result != nullptr) m_gcode_result->reset(); } #else diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index d3fca88fc..91ebc1372 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -50,10 +50,11 @@ public: void set_fff_print(Print *print) { m_fff_print = print; } void set_sla_print(SLAPrint *print) { m_sla_print = print; m_sla_print->set_printer(&m_sla_archive); } - void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } - void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; } + void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; } #if ENABLE_GCODE_VIEWER void set_gcode_result(GCodeProcessor::Result* result) { m_gcode_result = result; } +#else + void set_gcode_preview_data(GCodePreviewData* gpd) { m_gcode_preview_data = gpd; } #endif // ENABLE_GCODE_VIEWER // The following wxCommandEvent will be sent to the UI thread / Plater window, when the slicing is finished @@ -156,15 +157,17 @@ private: // Non-owned pointers to Print instances. Print *m_fff_print = nullptr; SLAPrint *m_sla_print = nullptr; +#if ENABLE_GCODE_VIEWER + // Data structure, to which the G-code export writes its annotations. + GCodeProcessor::Result *m_gcode_result = nullptr; +#else // Data structure, to which the G-code export writes its annotations. GCodePreviewData *m_gcode_preview_data = nullptr; - // Callback function, used to write thumbnails into gcode. +#endif // ENABLE_GCODE_VIEWER + // Callback function, used to write thumbnails into gcode. ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr; SL1Archive m_sla_archive; -#if ENABLE_GCODE_VIEWER - GCodeProcessor::Result* m_gcode_result = nullptr; -#endif // ENABLE_GCODE_VIEWER - // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID. + // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID. std::string m_temp_output_path; // Output path provided by the user. The output path may be set even if the slicing is running, // but once set, it cannot be re-set. diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 4c4c1aa8d..4732ff261 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1,5 +1,11 @@ +#include "libslic3r/libslic3r.h" +#if ENABLE_GCODE_VIEWER +#include "DoubleSlider.hpp" +#include "libslic3r/GCode.hpp" +#else #include "wxExtensions.hpp" #include "libslic3r/GCode/PreviewData.hpp" +#endif // ENABLE_GCODE_VIEWER #include "GUI.hpp" #include "GUI_App.hpp" #include "I18N.hpp" @@ -1945,7 +1951,11 @@ std::string TickCodeInfo::get_color_for_tick(TickCode tick, const std::string& c { if (mode == t_mode::SingleExtruder && code == ColorChangeCode && m_use_default_colors) { +#if ENABLE_GCODE_VIEWER + const std::vector& colors = ColorPrintColors::get(); +#else const std::vector& colors = GCodePreviewData::ColorPrintColors(); +#endif // ENABLE_GCODE_VIEWER if (ticks.empty()) return colors[0]; m_default_color_idx++; diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index bf8f54d6c..36bff17e9 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -4,7 +4,9 @@ #include "libslic3r/CustomGCode.hpp" #include "wxExtensions.hpp" +#if !ENABLE_GCODE_VIEWER #include +#endif // !ENABLE_GCODE_VIEWER #include #include #include diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 16d3f4123..59ea01314 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #ifdef __WXOSX__ @@ -79,6 +80,29 @@ void Field::PostInitialize() m_em_unit = em_unit(m_parent); BUILD(); + + // For the mode, when settings are in non-modal dialog, neither dialog nor tabpanel doesn't receive wxEVT_KEY_UP event, when some field is selected. + // So, like a workaround check wxEVT_KEY_UP event for the Filed and switch between tabs if Ctrl+(1-4) was pressed + if (getWindow()) + getWindow()->Bind(wxEVT_KEY_UP, [](wxKeyEvent& evt) { + if ((evt.GetModifiers() & wxMOD_CONTROL) != 0) { + int tab_id = -1; + switch (evt.GetKeyCode()) { + case '1': { tab_id = 0; break; } + case '2': { tab_id = 1; break; } + case '3': { tab_id = 2; break; } + case '4': { tab_id = 3; break; } + default: break; + } + if (tab_id >= 0) + wxGetApp().mainframe->select_tab(tab_id); + if (tab_id > 0) + // tab panel should be focused for correct navigation between tabs + wxGetApp().tab_panel()->SetFocus(); + } + + evt.Skip(); + }); } // Values of width to alignments of fields @@ -397,7 +421,7 @@ void TextCtrl::BUILD() { bKilledFocus = false; #endif // __WXOSX__ }), temp->GetId()); - +/* // select all text using Ctrl+A temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event) { @@ -405,7 +429,7 @@ void TextCtrl::BUILD() { temp->SetSelection(-1, -1); //select all event.Skip(); })); - +*/ // recast as a wxWindow to fit the calling convention window = dynamic_cast(temp); } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 44fe361c2..aa188cae3 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -3,6 +3,7 @@ #if ENABLE_GCODE_VIEWER #include "libslic3r/Print.hpp" +#include "libslic3r/Geometry.hpp" #include "GUI_App.hpp" #include "PresetBundle.hpp" #include "Camera.hpp" @@ -100,8 +101,7 @@ void GCodeViewer::IBuffer::reset() } // release cpu memory - data = std::vector(); - data_size = 0; + indices_count = 0; paths = std::vector(); render_paths = std::vector(); } @@ -116,9 +116,9 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con return true; } -void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int v_id) +void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id) { - Path::Endpoint endpoint = { static_cast(data.size()), v_id, move.position }; + Path::Endpoint endpoint = { i_id, s_id, move.position }; paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); } @@ -145,7 +145,44 @@ GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) con return ret; } -const std::vector GCodeViewer::Extrusion_Role_Colors{ { +void GCodeViewer::SequentialView::Marker::init() +{ + m_model.init_from(stilized_arrow(16, 2.0f, 4.0f, 1.0f, 8.0f)); + init_shader(); +} + +void GCodeViewer::SequentialView::Marker::render() const +{ + if (!m_visible || !m_shader.is_initialized()) + return; + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + m_shader.start_using(); + GLint color_id = ::glGetUniformLocation(m_shader.get_shader_program_id(), "uniform_color"); + if (color_id >= 0) + glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)m_color.data())); + + glsafe(::glPushMatrix()); + glsafe(::glMultMatrixf(m_world_transform.data())); + + m_model.render(); + + glsafe(::glPopMatrix()); + + m_shader.stop_using(); + + glsafe(::glDisable(GL_BLEND)); +} + +void GCodeViewer::SequentialView::Marker::init_shader() +{ + if (!m_shader.init("gouraud_light.vs", "gouraud_light.fs")) + BOOST_LOG_TRIVIAL(error) << "Unable to initialize gouraud_light shader: please, check that the files gouraud_light.vs and gouraud_light.fs are available"; +} + +const std::vector GCodeViewer::Extrusion_Role_Colors {{ { 0.50f, 0.50f, 0.50f }, // erNone { 1.00f, 1.00f, 0.40f }, // erPerimeter { 1.00f, 0.65f, 0.00f }, // erExternalPerimeter @@ -153,7 +190,7 @@ const std::vector GCodeViewer::Extrusion_Role_Colors{ { { 0.69f, 0.19f, 0.16f }, // erInternalInfill { 0.84f, 0.20f, 0.84f }, // erSolidInfill { 1.00f, 0.10f, 0.10f }, // erTopSolidInfill - { 0.00f, 1.00f, 1.00f }, // erIroning + { 1.00f, 0.55f, 0.41f }, // erIroning { 0.60f, 0.60f, 1.00f }, // erBridgeInfill { 1.00f, 1.00f, 1.00f }, // erGapFill { 0.52f, 0.48f, 0.13f }, // erSkirt @@ -164,13 +201,13 @@ const std::vector GCodeViewer::Extrusion_Role_Colors{ { { 0.00f, 0.00f, 0.00f } // erMixed }}; -const std::vector GCodeViewer::Travel_Colors{ { +const std::vector GCodeViewer::Travel_Colors {{ { 0.0f, 0.0f, 0.5f }, // Move { 0.0f, 0.5f, 0.0f }, // Extrude { 0.5f, 0.0f, 0.0f } // Retract }}; -const std::vector GCodeViewer::Range_Colors{ { +const std::vector GCodeViewer::Range_Colors {{ { 0.043f, 0.173f, 0.478f }, // bluish { 0.075f, 0.349f, 0.522f }, { 0.110f, 0.533f, 0.569f }, @@ -278,11 +315,16 @@ void GCodeViewer::render() const m_statistics.reset_opengl(); #endif // ENABLE_GCODE_VIEWER_STATISTICS + if (m_roles.empty()) + return; + glsafe(::glEnable(GL_DEPTH_TEST)); render_toolpaths(); + m_sequential_view.marker.set_world_transform(Geometry::assemble_transform(m_sequential_view.current_position.cast() + (0.5 + 12.0) * Vec3d::UnitZ(), { M_PI, 0.0, 0.0 }).cast()); + m_sequential_view.marker.render(); render_shells(); render_legend(); - render_sequential_dlg(); + render_sequential_bar(); #if ENABLE_GCODE_VIEWER_STATISTICS render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -316,7 +358,8 @@ unsigned int GCodeViewer::get_options_visibility_flags() const flags = set_flag(flags, 5, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print)); flags = set_flag(flags, 6, is_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode)); flags = set_flag(flags, 7, m_shells.visible); - flags = set_flag(flags, 8, is_legend_enabled()); + flags = set_flag(flags, 8, m_sequential_view.marker.is_visible()); + flags = set_flag(flags, 9, is_legend_enabled()); return flags; } @@ -334,7 +377,8 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Pause_Print, is_flag_set(5)); set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Custom_GCode, is_flag_set(6)); m_shells.visible = is_flag_set(7); - enable_legend(is_flag_set(8)); + m_sequential_view.marker.set_visible(is_flag_set(8)); + enable_legend(is_flag_set(9)); } bool GCodeViewer::init_shaders() @@ -388,10 +432,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) std::vector vertices_data(m_vertices.vertices_count * 3); for (size_t i = 0; i < m_vertices.vertices_count; ++i) { const GCodeProcessor::MoveVertex& move = gcode_result.moves[i]; - m_bounding_box.merge(move.position.cast()); + if (move.type == GCodeProcessor::EMoveType::Extrude) + m_bounding_box.merge(move.position.cast()); ::memcpy(static_cast(&vertices_data[i * 3]), static_cast(move.position.data()), 3 * sizeof(float)); } + m_bounding_box.merge(m_bounding_box.max + m_sequential_view.marker.get_bounding_box().max[2] * Vec3d::UnitZ()); + #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.vertices_size = SLIC3R_STDVEC_MEMSIZE(vertices_data, float); m_statistics.vertices_gpu_size = vertices_data.size() * sizeof(float); @@ -403,10 +450,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices_data.size() * sizeof(float), vertices_data.data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - // vertex data -> free ram + // vertex data -> no more needed, free ram vertices_data = std::vector(); // indices data -> extract from result + std::vector> indices(m_buffers.size()); for (size_t i = 0; i < m_vertices.vertices_count; ++i) { // skip first vertex @@ -416,7 +464,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1]; const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; - IBuffer& buffer = m_buffers[buffer_id(curr.type)]; + unsigned char id = buffer_id(curr.type); + IBuffer& buffer = m_buffers[id]; + std::vector& buffer_indices = indices[id]; switch (curr.type) { @@ -427,23 +477,23 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case GCodeProcessor::EMoveType::Retract: case GCodeProcessor::EMoveType::Unretract: { - buffer.add_path(curr, static_cast(i)); - buffer.data.push_back(static_cast(i)); + buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(i)); + buffer_indices.push_back(static_cast(i)); break; } case GCodeProcessor::EMoveType::Extrude: case GCodeProcessor::EMoveType::Travel: { if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { - buffer.add_path(curr, static_cast(i)); + buffer.add_path(curr, static_cast(buffer_indices.size()), static_cast(i)); Path& last_path = buffer.paths.back(); last_path.first.position = prev.position; last_path.first.s_id = static_cast(i - 1); - buffer.data.push_back(static_cast(i - 1)); + buffer_indices.push_back(static_cast(i - 1)); } - buffer.paths.back().last = { static_cast(buffer.data.size()), static_cast(i), curr.position }; - buffer.data.push_back(static_cast(i)); + buffer.paths.back().last = { static_cast(buffer_indices.size()), static_cast(i), curr.position }; + buffer_indices.push_back(static_cast(i)); break; } default: @@ -461,22 +511,21 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) #endif // ENABLE_GCODE_VIEWER_STATISTICS // indices data -> send data to gpu - for (IBuffer& buffer : m_buffers) + for (size_t i = 0; i < m_buffers.size(); ++i) { - buffer.data_size = buffer.data.size(); + IBuffer& buffer = m_buffers[i]; + std::vector& buffer_indices = indices[i]; + buffer.indices_count = buffer_indices.size(); #if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.indices_size += SLIC3R_STDVEC_MEMSIZE(buffer.data, unsigned int); - m_statistics.indices_gpu_size += buffer.data_size * sizeof(unsigned int); + m_statistics.indices_size += SLIC3R_STDVEC_MEMSIZE(buffer_indices, unsigned int); + m_statistics.indices_gpu_size += buffer.indices_count * sizeof(unsigned int); #endif // ENABLE_GCODE_VIEWER_STATISTICS - if (buffer.data_size > 0) { + if (buffer.indices_count > 0) { glsafe(::glGenBuffers(1, &buffer.ibo_id)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); - glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer.data_size * sizeof(unsigned int), buffer.data.data(), GL_STATIC_DRAW)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer.indices_count * sizeof(unsigned int), buffer_indices.data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - - // indices data -> free ram - buffer.data = std::vector(); } } @@ -641,6 +690,10 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current) const // update current sequential position m_sequential_view.current = keep_sequential_current ? std::clamp(m_sequential_view.current, m_sequential_view.first, m_sequential_view.last) : m_sequential_view.last; + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vertices.vbo_id)); + size_t v_size = VBuffer::vertex_size_bytes(); + glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(m_sequential_view.current * v_size), static_cast(v_size), static_cast(m_sequential_view.current_position.data()))); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); // second pass: filter paths by sequential data for (auto&& [buffer, id] : paths) { @@ -683,7 +736,7 @@ void GCodeViewer::render_toolpaths() const { auto set_color = [](GLint current_program_id, const Color& color) { if (current_program_id > 0) { - GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; + GLint color_id = ::glGetUniformLocation(current_program_id, "uniform_color"); if (color_id >= 0) { glsafe(::glUniform3fv(color_id, 1, (const GLfloat*)color.data())); return; @@ -714,10 +767,7 @@ void GCodeViewer::render_toolpaths() const GCodeProcessor::EMoveType type = buffer_type(i); buffer.shader.start_using(); - - GLint current_program_id; - glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); - + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.ibo_id)); switch (type) @@ -725,7 +775,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Tool_change: { Color color = { 1.0f, 1.0f, 1.0f }; - set_color(current_program_id, color); + set_color(static_cast(buffer.shader.get_shader_program_id()), color); for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -741,7 +791,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Color_change: { Color color = { 1.0f, 0.0f, 0.0f }; - set_color(current_program_id, color); + set_color(static_cast(buffer.shader.get_shader_program_id()), color); for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -757,7 +807,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Pause_Print: { Color color = { 0.0f, 1.0f, 0.0f }; - set_color(current_program_id, color); + set_color(static_cast(buffer.shader.get_shader_program_id()), color); for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -773,7 +823,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Custom_GCode: { Color color = { 0.0f, 0.0f, 1.0f }; - set_color(current_program_id, color); + set_color(static_cast(buffer.shader.get_shader_program_id()), color); for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -789,7 +839,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Retract: { Color color = { 1.0f, 0.0f, 1.0f }; - set_color(current_program_id, color); + set_color(static_cast(buffer.shader.get_shader_program_id()), color); for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -805,7 +855,7 @@ void GCodeViewer::render_toolpaths() const case GCodeProcessor::EMoveType::Unretract: { Color color = { 0.0f, 1.0f, 1.0f }; - set_color(current_program_id, color); + set_color(static_cast(buffer.shader.get_shader_program_id()), color); for (const RenderPath& path : buffer.render_paths) { glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); @@ -822,7 +872,7 @@ void GCodeViewer::render_toolpaths() const { for (const RenderPath& path : buffer.render_paths) { - set_color(current_program_id, path.color); + set_color(static_cast(buffer.shader.get_shader_program_id()), path.color); glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; @@ -835,7 +885,7 @@ void GCodeViewer::render_toolpaths() const { for (const RenderPath& path : buffer.render_paths) { - set_color(current_program_id, path.color); + set_color(static_cast(buffer.shader.get_shader_program_id()), path.color); glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_line_strip_calls_count; @@ -874,7 +924,7 @@ void GCodeViewer::render_legend() const static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); static const ImU32 ICON_BORDER_COLOR = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); - if (!m_legend_enabled || m_roles.empty()) + if (!m_legend_enabled) return; ImGuiWrapper& imgui = *wxGetApp().imgui(); @@ -928,14 +978,14 @@ void GCodeViewer::render_legend() const ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); switch (m_view_type) { - case EViewType::FeatureType: { imgui.text(I18N::translate_utf8(L("Feature type"))); break; } - case EViewType::Height: { imgui.text(I18N::translate_utf8(L("Height (mm)"))); break; } - case EViewType::Width: { imgui.text(I18N::translate_utf8(L("Width (mm)"))); break; } - case EViewType::Feedrate: { imgui.text(I18N::translate_utf8(L("Speed (mm/s)"))); break; } - case EViewType::FanSpeed: { imgui.text(I18N::translate_utf8(L("Fan Speed (%%)"))); break; } - case EViewType::VolumetricRate: { imgui.text(I18N::translate_utf8(L("Volumetric flow rate (mm³/s)"))); break; } - case EViewType::Tool: { imgui.text(I18N::translate_utf8(L("Tool"))); break; } - case EViewType::ColorPrint: { imgui.text(I18N::translate_utf8(L("Color Print"))); break; } + case EViewType::FeatureType: { imgui.text(_u8L("Feature type")); break; } + case EViewType::Height: { imgui.text(_u8L("Height (mm)")); break; } + case EViewType::Width: { imgui.text(_u8L("Width (mm)")); break; } + case EViewType::Feedrate: { imgui.text(_u8L("Speed (mm/s)")); break; } + case EViewType::FanSpeed: { imgui.text(_u8L("Fan Speed (%%)")); break; } + case EViewType::VolumetricRate: { imgui.text(_u8L("Volumetric flow rate (mm³/s)")); break; } + case EViewType::Tool: { imgui.text(_u8L("Tool")); break; } + case EViewType::ColorPrint: { imgui.text(_u8L("Color Print")); break; } default: { break; } } ImGui::PopStyleColor(); @@ -951,7 +1001,7 @@ void GCodeViewer::render_legend() const if (!visible) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); - add_item(Extrusion_Role_Colors[static_cast(role)], I18N::translate_utf8(ExtrusionEntity::role_to_string(role)), [this, role]() { + add_item(Extrusion_Role_Colors[static_cast(role)], _u8L(ExtrusionEntity::role_to_string(role)), [this, role]() { if (role < erCount) { m_extrusions.role_visibility_flags = is_visible(role) ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); @@ -981,7 +1031,7 @@ void GCodeViewer::render_legend() const if (it == m_extruder_ids.end()) continue; - add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str()); + add_item(m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); } break; } @@ -992,7 +1042,7 @@ void GCodeViewer::render_legend() const if (extruders_count == 1) { // single extruder use case if (custom_gcode_per_print_z.empty()) // no data to show - add_item(m_tool_colors.front(), I18N::translate_utf8(L("Default print color"))); + add_item(m_tool_colors.front(), _u8L("Default print color")); else { std::vector> cp_values; cp_values.reserve(custom_gcode_per_print_z.size()); @@ -1016,7 +1066,7 @@ void GCodeViewer::render_legend() const const int items_cnt = static_cast(cp_values.size()); if (items_cnt == 0) { // There is no one color change, but there are some pause print or custom Gcode - add_item(m_tool_colors.front(), I18N::translate_utf8(L("Default print color"))); + add_item(m_tool_colors.front(), _u8L("Default print color")); } else { for (int i = items_cnt; i >= 0; --i) { @@ -1024,14 +1074,14 @@ void GCodeViewer::render_legend() const std::string id_str = " (" + std::to_string(i + 1) + ")"; if (i == 0) { - add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values.front().first).str() + id_str); + add_item(m_tool_colors[i], (boost::format(_u8L("up to %.2f mm")) % cp_values.front().first).str() + id_str); break; } else if (i == items_cnt) { - add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str() + id_str); + add_item(m_tool_colors[i], (boost::format(_u8L("above %.2f mm")) % cp_values[i - 1].second).str() + id_str); continue; } - add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str() + id_str); + add_item(m_tool_colors[i], (boost::format(_u8L("%.2f - %.2f mm")) % cp_values[i - 1].second% cp_values[i].first).str() + id_str); } } } @@ -1040,7 +1090,7 @@ void GCodeViewer::render_legend() const { // extruders for (unsigned int i = 0; i < (unsigned int)extruders_count; ++i) { - add_item(m_tool_colors[i], (boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str()); + add_item(m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str()); } // color changes @@ -1052,7 +1102,7 @@ void GCodeViewer::render_legend() const std::string id_str = " (" + std::to_string(color_change_idx--) + ")"; add_item(m_tool_colors[last_color_id--], - (boost::format(I18N::translate_utf8(L("Color change for Extruder %d at %.2f mm"))) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str() + id_str); + (boost::format(_u8L("Color change for Extruder %d at %.2f mm")) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str() + id_str); } } } @@ -1079,14 +1129,14 @@ void GCodeViewer::render_legend() const ImGui::Spacing(); ImGui::Spacing(); ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(I18N::translate_utf8(L("Travel"))); + imgui.text(_u8L("Travel")); ImGui::PopStyleColor(); ImGui::Separator(); // items - add_item(Travel_Colors[0], I18N::translate_utf8(L("Movement"))); - add_item(Travel_Colors[1], I18N::translate_utf8(L("Extrusion"))); - add_item(Travel_Colors[2], I18N::translate_utf8(L("Retraction"))); + add_item(Travel_Colors[0], _u8L("Movement")); + add_item(Travel_Colors[1], _u8L("Extrusion")); + add_item(Travel_Colors[2], _u8L("Retraction")); break; } @@ -1097,7 +1147,7 @@ void GCodeViewer::render_legend() const ImGui::PopStyleVar(); } -void GCodeViewer::render_sequential_dlg() const +void GCodeViewer::render_sequential_bar() const { static const float MARGIN = 125.0f; static const float BUTTON_W = 50.0f; @@ -1107,9 +1157,6 @@ void GCodeViewer::render_sequential_dlg() const refresh_render_paths(true); }; - if (m_roles.empty()) - return; - if (m_sequential_view.last <= m_sequential_view.first) return; @@ -1191,9 +1238,6 @@ void GCodeViewer::render_statistics() const static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); static const float offset = 250.0f; - if (m_roles.empty()) - return; - ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.set_next_window_pos(0.5f * wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_width(), 0.0f, ImGuiCond_Once, 0.5f, 0.0f); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 3e73cb5d6..6f3ca47db 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -5,11 +5,15 @@ #include "GLShader.hpp" #include "3DScene.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" +#include "GLModel.hpp" #include namespace Slic3r { + class Print; +class TriangleMesh; + namespace GUI { class GCodeViewer @@ -73,19 +77,18 @@ class GCodeViewer struct IBuffer { unsigned int ibo_id{ 0 }; + size_t indices_count{ 0 }; Shader shader; - std::vector data; - size_t data_size{ 0 }; std::vector paths; std::vector render_paths; bool visible{ false }; void reset(); bool init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src); - void add_path(const GCodeProcessor::MoveVertex& move, unsigned int s_id); + void add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id); }; - + // helper to render shells struct Shells { GLVolumeCollection volumes; @@ -148,9 +151,36 @@ class GCodeViewer struct SequentialView { + class Marker + { + GL_Model m_model; + Transform3f m_world_transform; + std::array m_color{ 1.0f, 1.0f, 1.0f, 1.0f }; + bool m_visible{ false }; + Shader m_shader; + + public: + void init(); + + const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); } + + void set_world_transform(const Transform3f& transform) { m_world_transform = transform; } + void set_color(const std::array& color) { m_color = color; } + + bool is_visible() const { return m_visible; } + void set_visible(bool visible) { m_visible = visible; } + + void render() const; + + private: + void init_shader(); + }; + unsigned int first{ 0 }; unsigned int last{ 0 }; unsigned int current{ 0 }; + Vec3f current_position{ Vec3f::Zero() }; + Marker marker; }; #if ENABLE_GCODE_VIEWER_STATISTICS @@ -237,6 +267,7 @@ public: bool init() { set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true); + m_sequential_view.marker.init(); return init_shaders(); } @@ -285,7 +316,7 @@ private: void render_toolpaths() const; void render_shells() const; void render_legend() const; - void render_sequential_dlg() const; + void render_sequential_bar() const; #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics() const; #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 875e4fd52..073a9cd35 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5,7 +5,9 @@ #include "polypartition.h" #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/PrintConfig.hpp" +#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/PreviewData.hpp" +#endif // !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/ThumbnailData.hpp" #include "libslic3r/Geometry.hpp" #include "libslic3r/ExtrusionEntity.hpp" @@ -19,9 +21,7 @@ #include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/Tab.hpp" #include "slic3r/GUI/GUI_Preview.hpp" -#if ENABLE_NON_STATIC_CANVAS_MANAGER -#include "slic3r/GUI/GLCanvas3DManager.hpp" -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER +#include "slic3r/GUI/OpenGLManager.hpp" #include "slic3r/GUI/3DBed.hpp" #include "slic3r/GUI/Camera.hpp" @@ -58,10 +58,6 @@ #include #include -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT -#include -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - #include #include #include @@ -364,13 +360,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) const Size& cnv_size = canvas.get_canvas_size(); float half_w = 0.5f * (float)cnv_size.get_width(); float half_h = 0.5f * (float)cnv_size.get_height(); - -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)canvas.get_camera().get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); } @@ -868,11 +858,7 @@ void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0)) { const Size& cnv_size = canvas.get_canvas_size(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)canvas.get_camera().get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float left = (-0.5f * (float)m_original_width) * inv_zoom; float top = (-0.5f * (float)cnv_size.get_height() + (float)m_original_height + 2.0f) * inv_zoom; float right = left + (float)m_original_width * inv_zoom; @@ -1240,11 +1226,7 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0)) { const Size& cnv_size = canvas.get_canvas_size(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)canvas.get_camera().get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom; float top = (0.5f * (float)cnv_size.get_height()) * inv_zoom; float right = left + (float)m_original_width * inv_zoom; @@ -1271,11 +1253,7 @@ void GLCanvas3D::Labels::render(const std::vector& sorted_ if (!m_enabled || !is_shown()) return; -#if ENABLE_NON_STATIC_CANVAS_MANAGER const Camera& camera = wxGetApp().plater()->get_camera(); -#else - const Camera& camera = m_canvas.get_camera(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER const Model* model = m_canvas.get_model(); if (model == nullptr) return; @@ -1460,7 +1438,7 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas #if ENABLE_SLOPE_RENDERING void GLCanvas3D::Slope::render() const { - if (is_shown()) + if (m_dialog_shown) { const std::array& z_range = m_volumes.get_slope_z_range(); std::array angle_range = { Geometry::rad2deg(::acos(z_range[0])) - 90.0f, Geometry::rad2deg(::acos(z_range[1])) - 90.0f }; @@ -1471,29 +1449,41 @@ void GLCanvas3D::Slope::render() const imgui.set_next_window_pos((float)cnv_size.get_width(), (float)cnv_size.get_height(), ImGuiCond_Always, 1.0f, 1.0f); imgui.begin(_(L("Slope visualization")), nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - imgui.text(_(L("Facets' normal angle range (degrees)")) + ":"); + imgui.text(_(L("Facets' slope range (degrees)")) + ":"); - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.75f, 0.75f, 0.0f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(1.0f, 1.0f, 0.0f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.85f, 0.85f, 0.0f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.25f, 0.25f, 0.0f, 1.0f)); - if (ImGui::SliderFloat("##yellow", &angle_range[0], 0.0f, 90.0f, "%.1f")) - { - modified = true; - if (angle_range[1] < angle_range[0]) - angle_range[1] = angle_range[0]; - } - ImGui::PopStyleColor(4); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.75f, 0.0f, 0.0f, 0.5f)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(1.0f, 0.0f, 0.0f, 0.5f)); ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.85f, 0.0f, 0.0f, 0.5f)); ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.25f, 0.0f, 0.0f, 1.0f)); - if (ImGui::SliderFloat("##red", &angle_range[1], 0.0f, 90.0f, "%.1f")) + + // angle_range is range of normal angle, GUI should + // show facet slope angle + float slope_bound = 90.f - angle_range[1]; + bool mod = ImGui::SliderFloat("##red", &slope_bound, 0.0f, 90.0f, "%.1f"); + angle_range[1] = 90.f - slope_bound; + if (mod) { modified = true; if (angle_range[0] > angle_range[1]) angle_range[0] = angle_range[1]; } + + ImGui::PopStyleColor(4); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.75f, 0.75f, 0.0f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(1.0f, 1.0f, 0.0f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.85f, 0.85f, 0.0f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.25f, 0.25f, 0.0f, 1.0f)); + + slope_bound = 90.f - angle_range[0]; + mod = ImGui::SliderFloat("##yellow", &slope_bound, 0.0f, 90.0f, "%.1f"); + angle_range[0] = 90.f - slope_bound; + if (mod) + { + modified = true; + if (angle_range[1] < angle_range[0]) + angle_range[1] = angle_range[0]; + } + ImGui::PopStyleColor(4); ImGui::Separator(); @@ -1508,7 +1498,7 @@ void GLCanvas3D::Slope::render() const imgui.end(); if (modified) - m_volumes.set_slope_z_range({ -::cos(Geometry::deg2rad(90.0f - angle_range[0])), -::cos(Geometry::deg2rad(90.0f - angle_range[1])) }); + set_range(angle_range); } } #endif // ENABLE_SLOPE_RENDERING @@ -1545,22 +1535,13 @@ wxDEFINE_EVENT(EVT_GLCANVAS_RELOAD_FROM_DISK, SimpleEvent); const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25; -#if ENABLE_NON_STATIC_CANVAS_MANAGER GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) -#else -GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar) -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER : m_canvas(canvas) , m_context(nullptr) #if ENABLE_RETINA_GL , m_retina_helper(nullptr) #endif , m_in_render(false) -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - , m_bed(bed) - , m_camera(camera) - , m_view_toolbar(view_toolbar) -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER , m_main_toolbar(GLToolbar::Normal, "Top") , m_undoredo_toolbar(GLToolbar::Normal, "Top") , m_collapse_toolbar(GLToolbar::Normal, "Top") @@ -1599,10 +1580,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar m_timer.SetOwner(m_canvas); #if ENABLE_RETINA_GL m_retina_helper.reset(new RetinaHelper(canvas)); -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - // set default view_toolbar icons size equal to GLGizmosManager::Default_Icons_Size - m_view_toolbar.set_icons_size(GLGizmosManager::Default_Icons_Size); -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER #endif // ENABLE_RETINA_GL } @@ -1724,7 +1701,6 @@ void GLCanvas3D::reset_volumes() if (!m_initialized) return; -#if ENABLE_NON_STATIC_CANVAS_MANAGER if (m_volumes.empty()) return; @@ -1735,18 +1711,6 @@ void GLCanvas3D::reset_volumes() m_dirty = true; _set_warning_texture(WarningTexture::ObjectOutside, false); -#else - _set_current(); - - if (!m_volumes.empty()) - { - m_selection.clear(); - m_volumes.clear(); - m_dirty = true; - } - - _set_warning_texture(WarningTexture::ObjectOutside, false); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER } int GLCanvas3D::check_volumes_outside_state() const @@ -1835,11 +1799,7 @@ void GLCanvas3D::set_model(Model* model) void GLCanvas3D::bed_shape_changed() { refresh_camera_scene_box(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER wxGetApp().plater()->get_camera().requires_zoom_to_bed = true; -#else - m_camera.requires_zoom_to_bed = true; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER m_dirty = true; } @@ -1848,12 +1808,10 @@ void GLCanvas3D::set_color_by(const std::string& value) m_color_by = value; } -#if ENABLE_NON_STATIC_CANVAS_MANAGER void GLCanvas3D::refresh_camera_scene_box() { wxGetApp().plater()->get_camera().set_scene_box(scene_bounding_box()); } -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const { @@ -1869,12 +1827,7 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER bb.merge(wxGetApp().plater()->get_bed().get_bounding_box(true)); -#else - bb.merge(m_bed.get_bounding_box(true)); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - if (m_config != nullptr) { double h = m_config->opt_float("max_print_height"); @@ -1927,8 +1880,8 @@ bool GLCanvas3D::is_reload_delayed() const void GLCanvas3D::enable_layers_editing(bool enable) { #if ENABLE_SLOPE_RENDERING - if (enable && m_slope.is_shown()) - m_slope.show(false); + if (enable && m_slope.is_dialog_shown()) + m_slope.show_dialog(false); #endif // ENABLE_SLOPE_RENDERING m_layers_editing.set_enabled(enable); @@ -2000,11 +1953,7 @@ void GLCanvas3D::allow_multisample(bool allow) void GLCanvas3D::zoom_to_bed() { -#if ENABLE_NON_STATIC_CANVAS_MANAGER _zoom_to_box(wxGetApp().plater()->get_bed().get_bounding_box(false)); -#else - _zoom_to_box(m_bed.get_bounding_box(false)); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER } void GLCanvas3D::zoom_to_volumes() @@ -2022,11 +1971,7 @@ void GLCanvas3D::zoom_to_selection() void GLCanvas3D::select_view(const std::string& direction) { -#if ENABLE_NON_STATIC_CANVAS_MANAGER wxGetApp().plater()->get_camera().select_view(direction); -#else - m_camera.select_view(direction); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER if (m_canvas != nullptr) m_canvas->Refresh(); } @@ -2054,26 +1999,17 @@ void GLCanvas3D::render() return; // ensures this canvas is current and initialized -#if ENABLE_NON_STATIC_CANVAS_MANAGER if (!_is_shown_on_screen() || !_set_current() || !wxGetApp().init_opengl()) return; if (!is_initialized() && !init()) return; -#else - if (! _is_shown_on_screen() || !_set_current() || !_3DScene::init(m_canvas)) - return; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_RENDER_STATISTICS auto start_time = std::chrono::high_resolution_clock::now(); #endif // ENABLE_RENDER_STATISTICS -#if ENABLE_NON_STATIC_CANVAS_MANAGER if (wxGetApp().plater()->get_bed().get_shape().empty()) -#else - if (m_bed.get_shape().empty()) -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER { // this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE post_event(SimpleEvent(EVT_GLCANVAS_UPDATE_BED_SHAPE)); @@ -2085,7 +2021,6 @@ void GLCanvas3D::render() // to preview, this was called before canvas had its final size. It reported zero width // and the viewport was set incorrectly, leading to tripping glAsserts further down // the road (in apply_projection). That's why the minimum size is forced to 10. -#if ENABLE_NON_STATIC_CANVAS_MANAGER Camera& camera = wxGetApp().plater()->get_camera(); camera.apply_viewport(0, 0, std::max(10u, (unsigned int)cnv_size.get_width()), std::max(10u, (unsigned int)cnv_size.get_height())); @@ -2098,19 +2033,6 @@ void GLCanvas3D::render() camera.apply_view_matrix(); camera.apply_projection(_max_bounding_box(true, true)); -#else - m_camera.apply_viewport(0, 0, std::max(10u, (unsigned int)cnv_size.get_width()), std::max(10u, (unsigned int)cnv_size.get_height())); - - if (m_camera.requires_zoom_to_bed) - { - zoom_to_bed(); - _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); - m_camera.requires_zoom_to_bed = false; - } - - m_camera.apply_view_matrix(); - m_camera.apply_projection(_max_bounding_box(true, true)); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER GLfloat position_cam[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; glsafe(::glLightfv(GL_LIGHT1, GL_POSITION, position_cam)); @@ -2144,11 +2066,7 @@ void GLCanvas3D::render() #endif // ENABLE_GCODE_VIEWER _render_sla_slices(); _render_selection(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER _render_bed(!camera.is_looking_downward(), true); -#else - _render_bed(!m_camera.is_looking_downward(), true); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_RENDER_SELECTION_CENTER _render_selection_center(); @@ -2189,10 +2107,10 @@ void GLCanvas3D::render() ImGui::Separator(); imgui.text("Compressed textures: "); ImGui::SameLine(); - imgui.text(GLCanvas3DManager::are_compressed_textures_supported() ? "supported" : "not supported"); + imgui.text(OpenGLManager::are_compressed_textures_supported() ? "supported" : "not supported"); imgui.text("Max texture size: "); ImGui::SameLine(); - imgui.text(std::to_string(GLCanvas3DManager::get_gl_info().get_max_tex_size())); + imgui.text(std::to_string(OpenGLManager::get_gl_info().get_max_tex_size())); imgui.end(); #endif // ENABLE_RENDER_STATISTICS @@ -2223,16 +2141,13 @@ void GLCanvas3D::render() tooltip = m_collapse_toolbar.get_tooltip(); if (tooltip.empty()) -#if ENABLE_NON_STATIC_CANVAS_MANAGER tooltip = wxGetApp().plater()->get_view_toolbar().get_tooltip(); -#else - tooltip = m_view_toolbar.get_tooltip(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER } set_tooltip(tooltip); - m_tooltip.render(m_mouse.position, *this); + if (m_tooltip_enabled) + m_tooltip.render(m_mouse.position, *this); #endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this); @@ -2273,15 +2188,10 @@ void GLCanvas3D::render() void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const { - switch (GLCanvas3DManager::get_framebuffers_type()) + switch (OpenGLManager::get_framebuffers_type()) { -#if ENABLE_NON_STATIC_CANVAS_MANAGER - case GLCanvas3DManager::EFramebufferType::Arb: { _render_thumbnail_framebuffer(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; } - case GLCanvas3DManager::EFramebufferType::Ext: { _render_thumbnail_framebuffer_ext(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; } -#else - case GLCanvas3DManager::FB_Arb: { _render_thumbnail_framebuffer(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; } - case GLCanvas3DManager::FB_Ext: { _render_thumbnail_framebuffer_ext(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; } -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER + case OpenGLManager::EFramebufferType::Arb: { _render_thumbnail_framebuffer(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; } + case OpenGLManager::EFramebufferType::Ext: { _render_thumbnail_framebuffer_ext(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; } default: { _render_thumbnail_legacy(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; } } } @@ -2421,15 +2331,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr)) return; -#if ENABLE_NON_STATIC_CANVAS_MANAGER if (!m_initialized) return; _set_current(); -#else - if (m_initialized) - _set_current(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER struct ModelVolumeState { ModelVolumeState(const GLVolume* volume) : @@ -2853,22 +2758,8 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio #if ENABLE_GCODE_VIEWER void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result) { -#if ENABLE_GCODE_VIEWER_DEBUG_OUTPUT - static unsigned int last_result_id = 0; - if (last_result_id != gcode_result.id) - { - last_result_id = gcode_result.id; - boost::filesystem::path path("d:/processor.output"); - boost::nowide::ofstream out; - out.open(path.string()); - for (const GCodeProcessor::MoveVertex& v : gcode_result.moves) - { - out << v.to_string() << "\n"; - } - out.close(); - } -#endif // ENABLE_GCODE_VIEWER_DEBUG_OUTPUT m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); + _show_warning_texture_if_needed(WarningTexture::ToolpathOutside); } void GLCanvas3D::refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector& str_tool_colors) @@ -2877,9 +2768,7 @@ void GLCanvas3D::refresh_gcode_preview(const GCodeProcessor::Result& gcode_resul set_as_dirty(); request_extra_frame(); } -#endif // ENABLE_GCODE_VIEWER - -#if !ENABLE_GCODE_VIEWER +#else void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors) { const Print *print = this->fff_print(); @@ -2948,7 +2837,7 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const _generate_legend_texture(preview_data, tool_colors); } } -#endif // !ENABLE_GCODE_VIEWER +#endif // ENABLE_GCODE_VIEWER void GLCanvas3D::load_sla_preview() { @@ -3018,6 +2907,9 @@ void GLCanvas3D::bind_event_handlers() m_canvas->Bind(wxEVT_MIDDLE_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Bind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Bind(wxEVT_PAINT, &GLCanvas3D::on_paint, this); +#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI + m_canvas->Bind(wxEVT_SET_FOCUS, &GLCanvas3D::on_set_focus, this); +#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI } } @@ -3045,6 +2937,9 @@ void GLCanvas3D::unbind_event_handlers() m_canvas->Unbind(wxEVT_MIDDLE_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Unbind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Unbind(wxEVT_PAINT, &GLCanvas3D::on_paint, this); +#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI + m_canvas->Unbind(wxEVT_SET_FOCUS, &GLCanvas3D::on_set_focus, this); +#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI } } @@ -3061,13 +2956,8 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) m_dirty |= m_main_toolbar.update_items_state(); m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_collapse_toolbar.update_items_state(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state(); bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera()); -#else - m_dirty |= m_view_toolbar.update_items_state(); - bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER m_dirty |= mouse3d_controller_applied; if (!m_dirty) @@ -3220,7 +3110,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'd': { if (!is_layers_editing_enabled()) { - m_slope.show(!m_slope.is_shown()); + m_slope.show_dialog(!m_slope.is_dialog_shown()); m_dirty = true; } break; @@ -3231,11 +3121,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'I': case 'i': { _update_camera_zoom(1.0); break; } case 'K': -#if ENABLE_NON_STATIC_CANVAS_MANAGER case 'k': { wxGetApp().plater()->get_camera().select_next_type(); m_dirty = true; break; } -#else - case 'k': { m_camera.select_next_type(); m_dirty = true; break; } -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_GCODE_VIEWER case 'L': case 'l': { @@ -3259,7 +3145,24 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) } #endif // ENABLE_RENDER_PICKING_PASS case 'Z': +#if ENABLE_GCODE_VIEWER + case 'z': + { + if (!m_selection.is_empty()) + zoom_to_selection(); + else + { + if (!m_volumes.empty()) + zoom_to_volumes(); + else + _zoom_to_box(m_gcode_viewer.get_bounding_box()); + } + + break; + } +#else case 'z': { m_selection.is_empty() ? zoom_to_volumes() : zoom_to_selection(); break; } +#endif // ENABLE_GCODE_VIEWER default: { evt.Skip(); break; } } } @@ -3374,11 +3277,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) Vec3d displacement; if (camera_space) { -#if ENABLE_NON_STATIC_CANVAS_MANAGER Eigen::Matrix inv_view_3x3 = wxGetApp().plater()->get_camera().get_view_matrix().inverse().matrix().block(0, 0, 3, 3); -#else - Eigen::Matrix inv_view_3x3 = m_camera.get_view_matrix().inverse().matrix().block(0, 0, 3, 3); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER displacement = multiplier * (inv_view_3x3 * direction); displacement(2) = 0.0; } @@ -3721,11 +3620,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) return; } -#if ENABLE_NON_STATIC_CANVAS_MANAGER if (wxGetApp().plater()->get_view_toolbar().on_mouse(evt, *this)) -#else - if (m_view_toolbar.on_mouse(evt, *this)) -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER { if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) mouse_up_cleanup(); @@ -3773,12 +3668,18 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (top_level_wnd && top_level_wnd->IsActive()) m_canvas->SetFocus(); m_mouse.position = pos.cast(); +#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI + m_tooltip_enabled = false; +#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI // 1) forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while // the context menu is shown, ensuring it to disappear if the mouse is outside any volume and to // change the volume hover state if any is under the mouse // 2) when switching between 3d view and preview the size of the canvas changes if the side panels are visible, // so forces a resize to avoid multiple renders with different sizes (seen as flickering) _refresh_if_shown_on_screen(); +#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI + m_tooltip_enabled = true; +#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI } m_mouse.set_start_position_2D_as_invalid(); //#endif @@ -3786,7 +3687,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.Leaving()) { _deactivate_undo_redo_toolbar_items(); - _deactivate_search_toolbar_item(); // to remove hover on objects when the mouse goes out of this canvas m_mouse.position = Vec2d(-1.0, -1.0); @@ -3809,7 +3709,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) { - if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports) + if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports + && m_gizmos.get_current_type() != GLGizmosManager::FdmSupports) { m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); m_dirty = true; @@ -3889,12 +3790,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag if (m_selection.contains_volume(get_first_hover_volume_idx())) { -#if ENABLE_NON_STATIC_CANVAS_MANAGER const Camera& camera = wxGetApp().plater()->get_camera(); if (std::abs(camera.get_dir_forward()(2)) < EPSILON) -#else - if (std::abs(m_camera.get_dir_forward()(2)) < EPSILON) -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER { // side view -> move selected volumes orthogonally to camera view direction Linef3 ray = mouse_ray(pos); @@ -3907,13 +3804,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // vector from the starting position to the found intersection Vec3d inters_vec = inters - m_mouse.drag.start_position_3D; -#if ENABLE_NON_STATIC_CANVAS_MANAGER Vec3d camera_right = camera.get_dir_right(); Vec3d camera_up = camera.get_dir_up(); -#else - Vec3d camera_right = m_camera.get_dir_right(); - Vec3d camera_up = m_camera.get_dir_up(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER // finds projection of the vector along the camera axes double projection_x = inters_vec.dot(camera_right); @@ -3963,25 +3855,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.); if (wxGetApp().app_config->get("use_free_camera") == "1") // Virtual track ball (similar to the 3DConnexion mouse). -#if ENABLE_NON_STATIC_CANVAS_MANAGER wxGetApp().plater()->get_camera().rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.)); -#else - m_camera.rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.)); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER else { // Forces camera right vector to be parallel to XY plane in case it has been misaligned using the 3D mouse free rotation. // It is cheaper to call this function right away instead of testing wxGetApp().plater()->get_mouse3d_controller().connected(), // which checks an atomics (flushes CPU caches). // See GH issue #3816. -#if ENABLE_NON_STATIC_CANVAS_MANAGER Camera& camera = wxGetApp().plater()->get_camera(); camera.recover_from_free_camera(); camera.rotate_on_sphere(rot.x(), rot.y(), wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); -#else - m_camera.recover_from_free_camera(); - m_camera.rotate_on_sphere(rot.x(), rot.y(), wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER } m_dirty = true; @@ -3997,23 +3880,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) float z = 0.0f; const Vec3d& cur_pos = _mouse_to_3d(pos, &z); Vec3d orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z); -#if ENABLE_NON_STATIC_CANVAS_MANAGER Camera& camera = wxGetApp().plater()->get_camera(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER if (wxGetApp().app_config->get("use_free_camera") != "1") // Forces camera right vector to be parallel to XY plane in case it has been misaligned using the 3D mouse free rotation. // It is cheaper to call this function right away instead of testing wxGetApp().plater()->get_mouse3d_controller().connected(), // which checks an atomics (flushes CPU caches). // See GH issue #3816. -#if ENABLE_NON_STATIC_CANVAS_MANAGER camera.recover_from_free_camera(); camera.set_target(camera.get_target() + orig - cur_pos); -#else - m_camera.recover_from_free_camera(); - - m_camera.set_target(m_camera.get_target() + orig - cur_pos); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER m_dirty = true; } @@ -4122,6 +3997,15 @@ void GLCanvas3D::on_paint(wxPaintEvent& evt) this->render(); } +#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI +void GLCanvas3D::on_set_focus(wxFocusEvent& evt) +{ + m_tooltip_enabled = false; + _refresh_if_shown_on_screen(); + m_tooltip_enabled = true; +} +#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI + Size GLCanvas3D::get_canvas_size() const { int w = 0; @@ -4468,15 +4352,14 @@ void GLCanvas3D::update_ui_from_settings() if (new_scaling != orig_scaling) { BOOST_LOG_TRIVIAL(debug) << "GLCanvas3D: Scaling factor: " << new_scaling; -#if ENABLE_NON_STATIC_CANVAS_MANAGER Camera& camera = wxGetApp().plater()->get_camera(); camera.set_zoom(camera.get_zoom() * new_scaling / orig_scaling); -#else - m_camera.set_zoom(m_camera.get_zoom() * new_scaling / orig_scaling); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER _refresh_if_shown_on_screen(); } -#endif +#endif // ENABLE_RETINA_GL + + bool enable_collapse = wxGetApp().app_config->get("show_collapse_button") == "1"; + enable_collapse_toolbar(enable_collapse); } @@ -4508,11 +4391,7 @@ Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos) double GLCanvas3D::get_size_proportional_to_max_bed_size(double factor) const { -#if ENABLE_NON_STATIC_CANVAS_MANAGER return factor * wxGetApp().plater()->get_bed().get_bounding_box(false).max_size(); -#else - return factor * m_bed.get_bounding_box(false).max_size(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER } void GLCanvas3D::set_cursor(ECursorType type) @@ -4534,6 +4413,16 @@ void GLCanvas3D::msw_rescale() m_warning_texture.msw_rescale(*this); } +void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar() +{ + std::string new_tooltip = _u8L("Switch to Settings") + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (m_process->current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ; + + m_main_toolbar.set_tooltip(get_main_toolbar_item_id("settings"), new_tooltip); +} + bool GLCanvas3D::has_toolpaths_to_export() const { return m_volumes.has_toolpaths_to_export(); @@ -4575,11 +4464,7 @@ bool GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) const ImGuiWrapper* imgui = wxGetApp().imgui(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER const float x = pos_x * (float)wxGetApp().plater()->get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width(); -#else - const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER imgui->set_next_window_pos(x, m_undoredo_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f); std::string title = is_undo ? L("Undo History") : L("Redo History"); imgui->begin(_(title), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); @@ -4620,22 +4505,17 @@ bool GLCanvas3D::_render_search_list(float pos_x) const bool action_taken = false; ImGuiWrapper* imgui = wxGetApp().imgui(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER - const float x = pos_x * (float)wxGetApp().plater()->get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width(); -#else - const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width(); -#endif + const float x = /*pos_x * (float)wxGetApp().plater()->get_camera().get_zoom() + */0.5f * (float)get_canvas_size().get_width(); imgui->set_next_window_pos(x, m_main_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f); std::string title = L("Search"); imgui->begin(_(title), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); int selected = -1; bool edited = false; - bool check_changed = false; float em = static_cast(wxGetApp().em_unit()); #if ENABLE_RETINA_GL em *= m_retina_helper->get_scale_factor(); -#endif +#endif // ENABLE_RETINA_GL Sidebar& sidebar = wxGetApp().sidebar(); @@ -4645,7 +4525,7 @@ bool GLCanvas3D::_render_search_list(float pos_x) const imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s, sidebar.get_searcher().view_params, - selected, edited, m_mouse_wheel); + selected, edited, m_mouse_wheel, wxGetApp().is_localized()); search_line = s; delete [] s; @@ -4655,11 +4535,12 @@ bool GLCanvas3D::_render_search_list(float pos_x) const if (edited) sidebar.search(); - if (selected != size_t(-1)) { + if (selected >= 0) { // selected == 9999 means that Esc kye was pressed - if (selected != 9999) + if (selected == 9999) + action_taken = true; + else sidebar.jump_to_option(selected); - action_taken = true; } imgui->end(); @@ -4737,11 +4618,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool // extends the near and far z of the frustrum to avoid the bed being clipped // box in eye space -#if ENABLE_NON_STATIC_CANVAS_MANAGER BoundingBoxf3 t_bed_box = wxGetApp().plater()->get_bed().get_bounding_box(true).transformed(camera.get_view_matrix()); -#else - BoundingBoxf3 t_bed_box = m_bed.get_bounding_box(true).transformed(camera.get_view_matrix()); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER near_z = -t_bed_box.max(2); far_z = -t_bed_box.min(2); } @@ -5018,11 +4895,7 @@ void GLCanvas3D::_render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigne #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT // restore the default framebuffer size to avoid flickering on the 3D scene -#if ENABLE_NON_STATIC_CANVAS_MANAGER wxGetApp().plater()->get_camera().apply_viewport(0, 0, cnv_size.get_width(), cnv_size.get_height()); -#else - m_camera.apply_viewport(0, 0, cnv_size.get_width(), cnv_size.get_height()); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER } bool GLCanvas3D::_init_toolbars() @@ -5177,10 +5050,26 @@ bool GLCanvas3D::_init_main_toolbar() if (!m_main_toolbar.add_separator()) return false; + item.name = "settings"; + item.icon_filename = "cog.svg"; + item.tooltip = _u8L("Switch to Settings") + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (m_process->current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ; + item.sprite_id = 10; + item.enabling_callback = GLToolbarItem::Default_Enabling_Callback; + item.visibility_callback = [this]() { return (wxGetApp().app_config->get("new_settings_layout_mode") == "1" || + wxGetApp().app_config->get("dlg_settings_layout_mode") == "1"); }; + item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(); }; + if (!m_main_toolbar.add_item(item)) + return false; + + if (!m_main_toolbar.add_separator()) + return false; + item.name = "layersediting"; item.icon_filename = "layers_white.svg"; item.tooltip = _utf8(L("Variable layer height")); - item.sprite_id = 10; + item.sprite_id = 11; item.left.toggable = true; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; item.visibility_callback = [this]()->bool @@ -5202,7 +5091,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "search"; item.icon_filename = "search_.svg"; item.tooltip = _utf8(L("Search")) + " [" + GUI::shortkey_ctrl_prefix() + "F]"; - item.sprite_id = 11; + item.sprite_id = 12; item.left.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) { @@ -5338,7 +5227,7 @@ bool GLCanvas3D::_init_view_toolbar() bool GLCanvas3D::_init_collapse_toolbar() { - if (!m_collapse_toolbar.is_enabled()) + if (!m_collapse_toolbar.is_enabled() && m_collapse_toolbar.get_items_count() > 0) return true; BackgroundTexture::Metadata background_data; @@ -5379,61 +5268,10 @@ bool GLCanvas3D::_init_collapse_toolbar() wxGetApp().plater()->collapse_sidebar(!wxGetApp().plater()->is_sidebar_collapsed()); }; - if (!m_collapse_toolbar.add_item(item)) - return false; - - if (!m_collapse_toolbar.add_separator()) - return false; - - item.name = "print"; - item.icon_filename = "cog.svg"; - item.tooltip = _utf8(L("Switch to Print Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "2]"; - item.sprite_id = 1; - item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*0*/1); }; - - if (!m_collapse_toolbar.add_item(item)) - return false; - - item.name = "filament"; - item.icon_filename = "spool.svg"; - item.tooltip = _utf8(L("Switch to Filament Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]"; - item.sprite_id = 2; - item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*1*/2); }; - item.visibility_callback = [this]() { return wxGetApp().plater()->printer_technology() == ptFFF; }; - - if (!m_collapse_toolbar.add_item(item)) - return false; - - item.name = "printer"; - item.icon_filename = "printer.svg"; - item.tooltip = _utf8(L("Switch to Printer Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "4]"; - item.sprite_id = 3; - item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*2*/3); }; - - if (!m_collapse_toolbar.add_item(item)) - return false; - - item.name = "resin"; - item.icon_filename = "resin.svg"; - item.tooltip = _utf8(L("Switch to SLA Material Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]"; - item.sprite_id = 4; - item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*1*/2); }; - item.visibility_callback = [this]() { return m_process->current_printer_technology() == ptSLA; }; - - if (!m_collapse_toolbar.add_item(item)) - return false; - - item.name = "sla_printer"; - item.icon_filename = "sla_printer.svg"; - item.tooltip = _utf8(L("Switch to Printer Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "4]"; - item.sprite_id = 5; - item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*2*/3); }; - if (!m_collapse_toolbar.add_item(item)) return false; return true; - } bool GLCanvas3D::_set_current() @@ -5473,11 +5311,7 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be bb.merge(BoundingBoxf3(sel_bb_center - extend_by, sel_bb_center + extend_by)); } -#if ENABLE_NON_STATIC_CANVAS_MANAGER bb.merge(wxGetApp().plater()->get_bed().get_bounding_box(include_bed_model)); -#else - bb.merge(m_bed.get_bounding_box(include_bed_model)); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_GCODE_VIEWER if (!m_main_toolbar.is_enabled()) @@ -5489,21 +5323,13 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box, double margin_factor) { -#if ENABLE_NON_STATIC_CANVAS_MANAGER wxGetApp().plater()->get_camera().zoom_to_box(box, margin_factor); -#else - m_camera.zoom_to_box(box, margin_factor); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER m_dirty = true; } void GLCanvas3D::_update_camera_zoom(double zoom) { -#if ENABLE_NON_STATIC_CANVAS_MANAGER wxGetApp().plater()->get_camera().update_zoom(zoom); -#else - m_camera.update_zoom(zoom); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER m_dirty = true; } @@ -5652,8 +5478,39 @@ void GLCanvas3D::_rectangular_selection_picking_pass() const _update_volumes_hover_state(); } +#if ENABLE_GCODE_VIEWER +static BoundingBoxf3 print_volume(const DynamicPrintConfig& config) +{ + // tolerance to avoid false detection at bed edges + const double tolerance_x = 0.05; + const double tolerance_y = 0.05; + + BoundingBoxf3 ret; + const ConfigOptionPoints* opt = dynamic_cast(config.option("bed_shape")); + if (opt != nullptr) + { + BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); + ret = BoundingBoxf3(Vec3d(unscale(bed_box_2D.min(0)) - tolerance_x, unscale(bed_box_2D.min(1)) - tolerance_y, 0.0), Vec3d(unscale(bed_box_2D.max(0)) + tolerance_x, unscale(bed_box_2D.max(1)) + tolerance_y, config.opt_float("max_print_height"))); + // Allow the objects to protrude below the print bed + ret.min(2) = -1e10; + } + return ret; +} +#endif // ENABLE_GCODE_VIEWER + void GLCanvas3D::_render_background() const { +#if ENABLE_GCODE_VIEWER + bool use_error_color = m_dynamic_background_enabled; + if (!m_volumes.empty()) + use_error_color &= _is_any_volume_outside(); + else + { + BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); + use_error_color &= (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_bounding_box()) : false; + } +#endif // ENABLE_GCODE_VIEWER + glsafe(::glPushMatrix()); glsafe(::glLoadIdentity()); glsafe(::glMatrixMode(GL_PROJECTION)); @@ -5664,7 +5521,11 @@ void GLCanvas3D::_render_background() const glsafe(::glDisable(GL_DEPTH_TEST)); ::glBegin(GL_QUADS); +#if ENABLE_GCODE_VIEWER + if (use_error_color) +#else if (m_dynamic_background_enabled && _is_any_volume_outside()) +#endif // ENABLE_GCODE_VIEWER ::glColor3fv(ERROR_BG_DARK_COLOR); else ::glColor3fv(DEFAULT_BG_DARK_COLOR); @@ -5672,8 +5533,12 @@ void GLCanvas3D::_render_background() const ::glVertex2f(-1.0f, -1.0f); ::glVertex2f(1.0f, -1.0f); +#if ENABLE_GCODE_VIEWER + if (use_error_color) +#else if (m_dynamic_background_enabled && _is_any_volume_outside()) - ::glColor3fv(ERROR_BG_LIGHT_COLOR); +#endif // ENABLE_GCODE_VIEWER +::glColor3fv(ERROR_BG_LIGHT_COLOR); else ::glColor3fv(DEFAULT_BG_LIGHT_COLOR); @@ -5699,11 +5564,7 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes) const (m_gizmos.get_current_type() != GLGizmosManager::FdmSupports && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports); -#if ENABLE_NON_STATIC_CANVAS_MANAGER wxGetApp().plater()->get_bed().render(const_cast(*this), bottom, scale_factor, show_axes, show_texture); -#else - m_bed.render(const_cast(*this), theta, scale_factor, show_axes); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER } void GLCanvas3D::_render_objects() const @@ -5722,11 +5583,7 @@ void GLCanvas3D::_render_objects() const if (m_config != nullptr) { -#if ENABLE_NON_STATIC_CANVAS_MANAGER const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false); -#else - const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(false); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); m_volumes.check_outside_state(m_config, nullptr); } @@ -5742,37 +5599,20 @@ void GLCanvas3D::_render_objects() const m_shader.start_using(); if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) { int object_id = m_layers_editing.last_object_id; -#if ENABLE_NON_STATIC_CANVAS_MANAGER m_volumes.render(GLVolumeCollection::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) { // Which volume to paint without the layer height profile shader? return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); }); -#else - m_volumes.render(GLVolumeCollection::Opaque, false, m_camera.get_view_matrix(), [object_id](const GLVolume& volume) { - // Which volume to paint without the layer height profile shader? - return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); - }); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER // Let LayersEditing handle rendering of the active object using the layer height profile shader. m_layers_editing.render_volumes(*this, this->m_volumes); } else { // do not cull backfaces to show broken geometry, if any -#if ENABLE_NON_STATIC_CANVAS_MANAGER m_volumes.render(GLVolumeCollection::Opaque, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) { return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); }); -#else - m_volumes.render(GLVolumeCollection::Opaque, m_picking_enabled, m_camera.get_view_matrix(), [this](const GLVolume& volume) { - return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); - }); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER } -#if ENABLE_NON_STATIC_CANVAS_MANAGER m_volumes.render(GLVolumeCollection::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix()); -#else - m_volumes.render(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix()); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER m_shader.stop_using(); m_camera_clipping_plane = ClippingPlane::ClipsNothing(); @@ -5790,7 +5630,7 @@ void GLCanvas3D::_render_selection() const float scale_factor = 1.0; #if ENABLE_RETINA_GL scale_factor = m_retina_helper->get_scale_factor(); -#endif +#endif // ENABLE_RETINA_GL if (!m_gizmos.is_running()) m_selection.render(scale_factor); @@ -5803,24 +5643,58 @@ void GLCanvas3D::_render_selection_center() const } #endif // ENABLE_RENDER_SELECTION_CENTER +void GLCanvas3D::_check_and_update_toolbar_icon_scale() const +{ + float scale = wxGetApp().toolbar_icon_scale(); + Size cnv_size = get_canvas_size(); + + float size = GLToolbar::Default_Icons_Size * scale; + + // Set current size for all top toolbars. It will be used for next calculations +#if ENABLE_RETINA_GL + const float sc = m_retina_helper->get_scale_factor() * scale; + m_main_toolbar.set_scale(sc); + m_undoredo_toolbar.set_scale(sc); + m_collapse_toolbar.set_scale(sc); +#else + m_main_toolbar.set_icons_size(size); + m_undoredo_toolbar.set_icons_size(size); + m_collapse_toolbar.set_icons_size(size); +#endif // ENABLE_RETINA_GL + + float top_tb_width = m_main_toolbar.get_width() + m_undoredo_toolbar.get_width() + m_collapse_toolbar.get_width(); + int items_cnt = m_main_toolbar.get_visible_items_cnt() + m_undoredo_toolbar.get_visible_items_cnt() + m_collapse_toolbar.get_visible_items_cnt(); + float noitems_width = top_tb_width - size * items_cnt; // width of separators and borders in top toolbars + + // calculate scale needed for items in all top toolbars + float new_h_scale = (cnv_size.get_width() - noitems_width) / (items_cnt * GLToolbar::Default_Icons_Size); + + items_cnt = m_gizmos.get_selectable_icons_cnt() + 3; // +3 means a place for top and view toolbars and separators in gizmos toolbar + + // calculate scale needed for items in the gizmos toolbar + float new_v_scale = cnv_size.get_height() / (items_cnt * GLGizmosManager::Default_Icons_Size); + + // set minimum scale as a auto scale for the toolbars + float new_scale = std::min(new_h_scale, new_v_scale); + if (fabs(new_scale - scale) > EPSILON) + wxGetApp().set_auto_toolbar_icon_scale(new_scale); +} + + void GLCanvas3D::_render_overlays() const { glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glPushMatrix()); glsafe(::glLoadIdentity()); // ensure that the textures are renderered inside the frustrum -#if ENABLE_NON_STATIC_CANVAS_MANAGER const Camera& camera = wxGetApp().plater()->get_camera(); glsafe(::glTranslated(0.0, 0.0, -(camera.get_near_z() + 0.005))); // ensure that the overlay fits the frustrum near z plane double gui_scale = camera.get_gui_scale(); -#else - glsafe(::glTranslated(0.0, 0.0, -(m_camera.get_near_z() + 0.005))); - // ensure that the overlay fits the frustrum near z plane - double gui_scale = m_camera.get_gui_scale(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER glsafe(::glScaled(gui_scale, gui_scale, 1.0)); + _check_and_update_toolbar_icon_scale(); + _render_gizmos_overlay(); _render_warning_texture(); #if !ENABLE_GCODE_VIEWER @@ -5830,12 +5704,12 @@ void GLCanvas3D::_render_overlays() const // main toolbar and undoredo toolbar need to be both updated before rendering because both their sizes are needed // to correctly place them #if ENABLE_RETINA_GL - const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(true); + const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(/*true*/); m_main_toolbar.set_scale(scale); m_undoredo_toolbar.set_scale(scale); m_collapse_toolbar.set_scale(scale); #else - const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(true)); + const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(/*true*/)); m_main_toolbar.set_icons_size(size); m_undoredo_toolbar.set_icons_size(size); m_collapse_toolbar.set_icons_size(size); @@ -5892,11 +5766,7 @@ void GLCanvas3D::_render_volumes_for_picking() const glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); -#if ENABLE_NON_STATIC_CANVAS_MANAGER const Transform3d& view_matrix = wxGetApp().plater()->get_camera().get_view_matrix(); -#else - const Transform3d& view_matrix = m_camera.get_view_matrix(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER for (size_t type = 0; type < 2; ++ type) { GLVolumeWithIdAndZList to_render = volumes_to_render(m_volumes.volumes, (type == 0) ? GLVolumeCollection::Opaque : GLVolumeCollection::Transparent, view_matrix); for (const GLVolumeWithIdAndZ& volume : to_render) @@ -5945,14 +5815,11 @@ void GLCanvas3D::_render_main_toolbar() const return; Size cnv_size = get_canvas_size(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)m_camera.get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - float left = -0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width()) * inv_zoom; + float collapse_toolbar_width = m_collapse_toolbar.is_enabled() ? m_collapse_toolbar.get_width() : 0.0f; + float left = -0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width() + collapse_toolbar_width) * inv_zoom; m_main_toolbar.set_position(top, left); m_main_toolbar.render(*this); @@ -5964,14 +5831,11 @@ void GLCanvas3D::_render_undoredo_toolbar() const return; Size cnv_size = get_canvas_size(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)m_camera.get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - float left = (m_main_toolbar.get_width() - 0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width())) * inv_zoom; + float collapse_toolbar_width = m_collapse_toolbar.is_enabled() ? m_collapse_toolbar.get_width() : 0.0f; + float left = (m_main_toolbar.get_width() - 0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width() + collapse_toolbar_width)) * inv_zoom; m_undoredo_toolbar.set_position(top, left); m_undoredo_toolbar.render(*this); } @@ -5982,11 +5846,7 @@ void GLCanvas3D::_render_collapse_toolbar() const return; Size cnv_size = get_canvas_size(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)m_camera.get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float band = m_layers_editing.is_enabled() ? (wxGetApp().imgui()->get_style_scaling() * LayersEditing::THICKNESS_BAR_WIDTH) : 0.0; @@ -5999,50 +5859,27 @@ void GLCanvas3D::_render_collapse_toolbar() const void GLCanvas3D::_render_view_toolbar() const { -#if ENABLE_NON_STATIC_CANVAS_MANAGER GLToolbar& view_toolbar = wxGetApp().plater()->get_view_toolbar(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_RETINA_GL // m_view_toolbar.set_scale(m_retina_helper->get_scale_factor()); const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER view_toolbar.set_scale(scale); //! #ys_FIXME_experiment -#else - m_view_toolbar.set_scale(scale); //! #ys_FIXME_experiment -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #else // m_view_toolbar.set_scale(m_canvas->GetContentScaleFactor()); // m_view_toolbar.set_scale(wxGetApp().em_unit()*0.1f); const float size = int(GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale()); -#if ENABLE_NON_STATIC_CANVAS_MANAGER view_toolbar.set_icons_size(size); //! #ys_FIXME_experiment -#else - m_view_toolbar.set_icons_size(size); //! #ys_FIXME_experiment -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #endif // ENABLE_RETINA_GL Size cnv_size = get_canvas_size(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)m_camera.get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER // places the toolbar on the bottom-left corner of the 3d scene -#if ENABLE_NON_STATIC_CANVAS_MANAGER float top = (-0.5f * (float)cnv_size.get_height() + view_toolbar.get_height()) * inv_zoom; -#else - float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float left = -0.5f * (float)cnv_size.get_width() * inv_zoom; -#if ENABLE_NON_STATIC_CANVAS_MANAGER view_toolbar.set_position(top, left); view_toolbar.render(*this); -#else - m_view_toolbar.set_position(top, left); - m_view_toolbar.render(*this); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER } #if ENABLE_SHOW_CAMERA_TARGET @@ -6194,7 +6031,11 @@ void GLCanvas3D::_render_sla_slices() const void GLCanvas3D::_render_selection_sidebar_hints() const { +#if ENABLE_GCODE_VIEWER + m_selection.render_sidebar_hints(m_sidebar_field); +#else m_selection.render_sidebar_hints(m_sidebar_field, m_shader); +#endif // ENABLE_GCODE_VIEWER } void GLCanvas3D::_update_volumes_hover_state() const @@ -6321,16 +6162,10 @@ Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) if (m_canvas == nullptr) return Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); -#if ENABLE_NON_STATIC_CANVAS_MANAGER const Camera& camera = wxGetApp().plater()->get_camera(); const std::array& viewport = camera.get_viewport(); const Transform3d& modelview_matrix = camera.get_view_matrix(); const Transform3d& projection_matrix = camera.get_projection_matrix(); -#else - const std::array& viewport = m_camera.get_viewport(); - const Transform3d& modelview_matrix = m_camera.get_view_matrix(); - const Transform3d& projection_matrix = m_camera.get_projection_matrix(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER GLint y = viewport[3] - (GLint)mouse_pos(1); GLfloat mouse_z; @@ -7252,6 +7087,7 @@ void GLCanvas3D::_load_sla_shells() update_volumes_colors_by_extruder(); } +#if !ENABLE_GCODE_VIEWER void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data) { unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size(); @@ -7309,9 +7145,13 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe } } } +#endif // !ENABLE_GCODE_VIEWER void GLCanvas3D::_update_toolpath_volumes_outside_state() { +#if ENABLE_GCODE_VIEWER + BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); +#else // tolerance to avoid false detection at bed edges static const double tolerance_x = 0.05; static const double tolerance_y = 0.05; @@ -7328,15 +7168,23 @@ void GLCanvas3D::_update_toolpath_volumes_outside_state() print_volume.min(2) = -1e10; } } +#endif // ENABLE_GCODE_VIEWER for (GLVolume* volume : m_volumes.volumes) { +#if ENABLE_GCODE_VIEWER + volume->is_outside = ((test_volume.radius() > 0.0) && volume->is_extrusion_path) ? !test_volume.contains(volume->bounding_box()) : false; +#else volume->is_outside = ((print_volume.radius() > 0.0) && volume->is_extrusion_path) ? !print_volume.contains(volume->bounding_box()) : false; +#endif // ENABLE_GCODE_VIEWER } } void GLCanvas3D::_update_sla_shells_outside_state() { +#if ENABLE_GCODE_VIEWER + BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); +#else // tolerance to avoid false detection at bed edges static const double tolerance_x = 0.05; static const double tolerance_y = 0.05; @@ -7353,17 +7201,34 @@ void GLCanvas3D::_update_sla_shells_outside_state() print_volume.min(2) = -1e10; } } +#endif // ENABLE_GCODE_VIEWER for (GLVolume* volume : m_volumes.volumes) { +#if ENABLE_GCODE_VIEWER + volume->is_outside = ((test_volume.radius() > 0.0) && volume->shader_outside_printer_detection_enabled) ? !test_volume.contains(volume->transformed_convex_hull_bounding_box()) : false; +#else volume->is_outside = ((print_volume.radius() > 0.0) && volume->shader_outside_printer_detection_enabled) ? !print_volume.contains(volume->transformed_convex_hull_bounding_box()) : false; +#endif // ENABLE_GCODE_VIEWER } } void GLCanvas3D::_show_warning_texture_if_needed(WarningTexture::Warning warning) { _set_current(); +#if ENABLE_GCODE_VIEWER + bool show = false; + if (!m_volumes.empty()) + show = _is_any_volume_outside(); + else + { + BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); + show = (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_bounding_box()) : false; + } + _set_warning_texture(warning, show); +#else _set_warning_texture(warning, _is_any_volume_outside()); +#endif // ENABLE_GCODE_VIEWER } std::vector GLCanvas3D::_parse_colors(const std::vector& colors) @@ -7528,9 +7393,14 @@ bool GLCanvas3D::_deactivate_undo_redo_toolbar_items() return false; } +bool GLCanvas3D::is_search_pressed() const +{ + return m_main_toolbar.is_item_pressed("search"); +} + bool GLCanvas3D::_deactivate_search_toolbar_item() { - if (m_main_toolbar.is_item_pressed("search")) + if (is_search_pressed()) { m_main_toolbar.force_left_action(m_main_toolbar.get_item_id("search"), *this); return true; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index be10e79a1..3e7296e13 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -16,9 +16,6 @@ #include "GUI_ObjectLayers.hpp" #include "GLSelectionRectangle.hpp" #include "MeshUtils.hpp" -#if !ENABLE_NON_STATIC_CANVAS_MANAGER -#include "Camera.hpp" -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_GCODE_VIEWER #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GCodeViewer.hpp" @@ -35,9 +32,7 @@ class wxMouseEvent; class wxTimerEvent; class wxPaintEvent; class wxGLCanvas; -#if ENABLE_NON_STATIC_CANVAS_MANAGER class wxGLContext; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER // Support for Retina OpenGL on Mac OS #define ENABLE_RETINA_GL __APPLE__ @@ -420,6 +415,7 @@ private: class Slope { bool m_enabled{ false }; + bool m_dialog_shown{ false }; GLCanvas3D& m_canvas; GLVolumeCollection& m_volumes; @@ -428,9 +424,14 @@ private: void enable(bool enable) { m_enabled = enable; } bool is_enabled() const { return m_enabled; } - void show(bool show) { m_volumes.set_slope_active(m_enabled ? show : false); } - bool is_shown() const { return m_volumes.is_slope_active(); } + void use(bool use) { m_volumes.set_slope_active(m_enabled ? use : false); } + bool is_used() const { return m_volumes.is_slope_active(); } + void show_dialog(bool show) { if (show && is_used()) return; use(show); m_dialog_shown = show; } + bool is_dialog_shown() const { return m_dialog_shown; } void render() const; + void set_range(const std::array& range) const { + m_volumes.set_slope_z_range({ -::cos(Geometry::deg2rad(90.0f - range[0])), -::cos(Geometry::deg2rad(90.0f - range[1])) }); + } }; #endif // ENABLE_SLOPE_RENDERING @@ -453,11 +454,6 @@ private: #endif // !ENABLE_GCODE_VIEWER WarningTexture m_warning_texture; wxTimer m_timer; -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - Bed3D& m_bed; - Camera& m_camera; - GLToolbar& m_view_toolbar; -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER LayersEditing m_layers_editing; Shader m_shader; Mouse m_mouse; @@ -526,22 +522,17 @@ private: Labels m_labels; #if ENABLE_CANVAS_TOOLTIP_USING_IMGUI mutable Tooltip m_tooltip; + mutable bool m_tooltip_enabled{ true }; #endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI #if ENABLE_SLOPE_RENDERING Slope m_slope; #endif // ENABLE_SLOPE_RENDERING public: -#if ENABLE_NON_STATIC_CANVAS_MANAGER explicit GLCanvas3D(wxGLCanvas* canvas); -#else - GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER ~GLCanvas3D(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER bool is_initialized() const { return m_initialized; } -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER void set_context(wxGLContext* context) { m_context = context; } @@ -593,13 +584,7 @@ public: void set_color_by(const std::string& value); -#if ENABLE_NON_STATIC_CANVAS_MANAGER void refresh_camera_scene_box(); -#else - void refresh_camera_scene_box() { m_camera.set_scene_box(scene_bounding_box()); } - const Camera& get_camera() const { return m_camera; } - Camera& get_camera() { return m_camera; } -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER const Shader& get_shader() const { return m_shader; } BoundingBoxf3 volumes_bounding_box() const; @@ -607,6 +592,7 @@ public: bool is_layers_editing_enabled() const; bool is_layers_editing_allowed() const; + bool is_search_pressed() const; void reset_layer_height_profile(); void adaptive_layer_height_profile(float quality_factor); @@ -662,7 +648,6 @@ public: void set_toolpaths_z_range(const std::array& range); #else std::vector get_current_print_zs(bool active_only) const; - void set_toolpaths_range(double low, double high); #endif // ENABLE_GCODE_VIEWER void set_toolpaths_range(double low, double high); @@ -694,6 +679,9 @@ public: void on_timer(wxTimerEvent& evt); void on_mouse(wxMouseEvent& evt); void on_paint(wxPaintEvent& evt); +#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI + void on_set_focus(wxFocusEvent& evt); +#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI Size get_canvas_size() const; Vec2d get_local_mouse_position() const; @@ -719,10 +707,6 @@ public: void update_ui_from_settings(); -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - float get_view_toolbar_height() const { return m_view_toolbar.get_height(); } -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; } int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); } void set_selected_extruder(int extruder) { m_selected_extruder = extruder;} @@ -765,6 +749,7 @@ public: int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); } void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); } void force_main_toolbar_right_action(int item_id) { m_main_toolbar.force_right_action(item_id, *this); } + void update_tooltip_for_settings_item_in_main_toolbar(); bool has_toolpaths_to_export() const; void export_toolpaths_to_obj(const char* filename) const; @@ -775,8 +760,10 @@ public: void show_labels(bool show) { m_labels.show(show); } #if ENABLE_SLOPE_RENDERING - bool is_slope_shown() const { return m_slope.is_shown(); } - void show_slope(bool show) { m_slope.show(show); } + bool is_slope_shown() const { return m_slope.is_dialog_shown(); } + void use_slope(bool use) { m_slope.use(use); } + void show_slope(bool show) { m_slope.show_dialog(show); } + void set_slope_range(const std::array& range) { m_slope.set_range(range); } #endif // ENABLE_SLOPE_RENDERING private: @@ -810,6 +797,7 @@ private: #if ENABLE_RENDER_SELECTION_CENTER void _render_selection_center() const; #endif // ENABLE_RENDER_SELECTION_CENTER + void _check_and_update_toolbar_icon_scale() const; void _render_overlays() const; void _render_warning_texture() const; #if !ENABLE_GCODE_VIEWER @@ -872,8 +860,10 @@ private: #endif // !ENABLE_GCODE_VIEWER // Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished. void _load_sla_shells(); +#if !ENABLE_GCODE_VIEWER // sets gcode geometry visibility according to user selection void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data); +#endif // !ENABLE_GCODE_VIEWER void _update_toolpath_volumes_outside_state(); void _update_sla_shells_outside_state(); void _show_warning_texture_if_needed(WarningTexture::Warning warning); diff --git a/src/slic3r/GUI/GLCanvas3DManager.hpp b/src/slic3r/GUI/GLCanvas3DManager.hpp deleted file mode 100644 index 428ccd96a..000000000 --- a/src/slic3r/GUI/GLCanvas3DManager.hpp +++ /dev/null @@ -1,211 +0,0 @@ -#ifndef slic3r_GLCanvas3DManager_hpp_ -#define slic3r_GLCanvas3DManager_hpp_ - -#include "libslic3r/BoundingBox.hpp" - -#include -#include - -class wxWindow; -class wxGLCanvas; -class wxGLContext; - -namespace Slic3r { - -class BackgroundSlicingProcess; -class DynamicPrintConfig; -class Model; -class ExPolygon; -typedef std::vector ExPolygons; -class ModelObject; -class PrintObject; - -namespace GUI { - -class GLCanvas3D; -#if !ENABLE_NON_STATIC_CANVAS_MANAGER -class Bed3D; -class GLToolbar; -struct Camera; -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - -class GLCanvas3DManager -{ -public: -#if ENABLE_NON_STATIC_CANVAS_MANAGER - enum class EFramebufferType : unsigned char - { - Unknown, - Arb, - Ext - }; -#else - enum EFramebufferType : unsigned char - { - FB_None, - FB_Arb, - FB_Ext - }; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - - class GLInfo - { -#if ENABLE_NON_STATIC_CANVAS_MANAGER - mutable bool m_detected{ false }; - mutable int m_max_tex_size{ 0 }; - mutable float m_max_anisotropy{ 0.0f }; - - mutable std::string m_version; - mutable std::string m_glsl_version; - mutable std::string m_vendor; - mutable std::string m_renderer; -#else - mutable bool m_detected; - - mutable std::string m_version; - mutable std::string m_glsl_version; - mutable std::string m_vendor; - mutable std::string m_renderer; - - mutable int m_max_tex_size; - mutable float m_max_anisotropy; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - - public: -#if ENABLE_NON_STATIC_CANVAS_MANAGER - GLInfo() = default; -#else - GLInfo(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - - const std::string& get_version() const; - const std::string& get_glsl_version() const; - const std::string& get_vendor() const; - const std::string& get_renderer() const; - - int get_max_tex_size() const; - float get_max_anisotropy() const; - - bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const; - - std::string to_string(bool format_as_html, bool extensions) const; - - private: - void detect() const; - }; - -#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 -#ifdef __APPLE__ - // Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets - struct OSInfo - { - int major{ 0 }; - int minor{ 0 }; - int micro{ 0 }; - }; -#endif //__APPLE__ -#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5 - -private: -#if ENABLE_NON_STATIC_CANVAS_MANAGER - enum class EMultisampleState : unsigned char - { - Unknown, - Enabled, - Disabled - }; -#else - enum EMultisampleState : unsigned char - { - MS_Unknown, - MS_Enabled, - MS_Disabled - }; - - typedef std::map CanvasesMap; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - -#if ENABLE_NON_STATIC_CANVAS_MANAGER - bool m_gl_initialized{ false }; - wxGLContext* m_context{ nullptr }; -#else - wxGLContext* m_context; - bool m_gl_initialized; - CanvasesMap m_canvases; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - static GLInfo s_gl_info; -#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 -#ifdef __APPLE__ - // Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets - static OSInfo s_os_info; -#endif //__APPLE__ -#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5 - static bool s_compressed_textures_supported; - static EMultisampleState s_multisample; - static EFramebufferType s_framebuffers_type; - -public: -#if ENABLE_NON_STATIC_CANVAS_MANAGER - GLCanvas3DManager() = default; -#else - GLCanvas3DManager(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - ~GLCanvas3DManager(); - -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - bool add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar); - bool remove(wxGLCanvas* canvas); - void remove_all(); - - size_t count() const; -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - -#if ENABLE_NON_STATIC_CANVAS_MANAGER - bool init_gl(); -#else - void init_gl(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - -#if ENABLE_NON_STATIC_CANVAS_MANAGER - wxGLContext* init_glcontext(wxGLCanvas& canvas); -#else - bool init(wxGLCanvas* canvas); - void destroy(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - GLCanvas3D* get_canvas(wxGLCanvas* canvas); -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - - static bool are_compressed_textures_supported() { return s_compressed_textures_supported; } -#if ENABLE_NON_STATIC_CANVAS_MANAGER - static bool can_multisample() { return s_multisample == EMultisampleState::Enabled; } - static bool are_framebuffers_supported() { return (s_framebuffers_type != EFramebufferType::Unknown); } -#else - static bool can_multisample() { return s_multisample == MS_Enabled; } - static bool are_framebuffers_supported() { return (s_framebuffers_type != FB_None); } -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - static EFramebufferType get_framebuffers_type() { return s_framebuffers_type; } - -#if ENABLE_NON_STATIC_CANVAS_MANAGER - static wxGLCanvas* create_wxglcanvas(wxWindow& parent); -#else - static wxGLCanvas* create_wxglcanvas(wxWindow *parent); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - - static const GLInfo& get_gl_info() { return s_gl_info; } - -private: -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - CanvasesMap::iterator do_get_canvas(wxGLCanvas* canvas); - CanvasesMap::const_iterator do_get_canvas(wxGLCanvas* canvas) const; - - bool init(GLCanvas3D& canvas); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - static void detect_multisample(int* attribList); -}; - -} // namespace GUI -} // namespace Slic3r - -#endif // slic3r_GLCanvas3DManager_hpp_ diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp new file mode 100644 index 000000000..e5b6cbdcb --- /dev/null +++ b/src/slic3r/GUI/GLModel.cpp @@ -0,0 +1,508 @@ +#include "libslic3r/libslic3r.h" +#include "GLModel.hpp" + +#include "3DScene.hpp" +#include "libslic3r/TriangleMesh.hpp" + +#include + +namespace Slic3r { +namespace GUI { + +void GL_Model::init_from(const GLModelInitializationData& data) +{ + assert(!data.positions.empty() && !data.triangles.empty()); + assert(data.positions.size() == data.normals.size()); + + if (m_vbo_id > 0) // call reset() if you want to reuse this model + return; + + // vertices/normals data + std::vector vertices(6 * data.positions.size()); + for (size_t i = 0; i < data.positions.size(); ++i) { + ::memcpy(static_cast(&vertices[i * 6]), static_cast(data.positions[i].data()), 3 * sizeof(float)); + ::memcpy(static_cast(&vertices[3 + i * 6]), static_cast(data.normals[i].data()), 3 * sizeof(float)); + } + + // indices data + std::vector indices(3 * data.triangles.size()); + for (size_t i = 0; i < data.triangles.size(); ++i) { + for (size_t j = 0; j < 3; ++j) { + indices[i * 3 + j] = static_cast(data.triangles[i][j]); + } + } + + m_indices_count = static_cast(indices.size()); + m_bounding_box = BoundingBoxf3(); + for (size_t i = 0; i < data.positions.size(); ++i) { + m_bounding_box.merge(data.positions[i].cast()); + } + + send_to_gpu(vertices, indices); +} + +void GL_Model::init_from(const TriangleMesh& mesh) +{ + auto get_normal = [](const std::array& triangle) { + return (triangle[1] - triangle[0]).cross(triangle[2] - triangle[0]).normalized(); + }; + + if (m_vbo_id > 0) // call reset() if you want to reuse this model + return; + + assert(!mesh.its.vertices.empty() && !mesh.its.indices.empty()); // call require_shared_vertices() before to pass the mesh to this method + + // vertices data -> load from mesh + std::vector vertices(6 * mesh.its.vertices.size()); + for (size_t i = 0; i < mesh.its.vertices.size(); ++i) { + ::memcpy(static_cast(&vertices[i * 6]), static_cast(mesh.its.vertices[i].data()), 3 * sizeof(float)); + } + + // indices/normals data -> load from mesh + std::vector indices(3 * mesh.its.indices.size()); + for (size_t i = 0; i < mesh.its.indices.size(); ++i) { + const stl_triangle_vertex_indices& triangle = mesh.its.indices[i]; + for (size_t j = 0; j < 3; ++j) { + indices[i * 3 + j] = static_cast(triangle[j]); + } + Vec3f normal = get_normal({ mesh.its.vertices[triangle[0]], mesh.its.vertices[triangle[1]], mesh.its.vertices[triangle[2]] }); + ::memcpy(static_cast(&vertices[3 + static_cast(triangle[0]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); + ::memcpy(static_cast(&vertices[3 + static_cast(triangle[1]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); + ::memcpy(static_cast(&vertices[3 + static_cast(triangle[2]) * 6]), static_cast(normal.data()), 3 * sizeof(float)); + } + + m_indices_count = static_cast(indices.size()); + m_bounding_box = mesh.bounding_box(); + + send_to_gpu(vertices, indices); +} + +void GL_Model::reset() +{ + // release gpu memory + if (m_ibo_id > 0) { + glsafe(::glDeleteBuffers(1, &m_ibo_id)); + m_ibo_id = 0; + } + + if (m_vbo_id > 0) { + glsafe(::glDeleteBuffers(1, &m_vbo_id)); + m_vbo_id = 0; + } + + m_indices_count = 0; + m_bounding_box = BoundingBoxf3(); +} + +void GL_Model::render() const +{ + if (m_vbo_id == 0 || m_ibo_id == 0) + return; + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); + glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)0)); + glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo_id)); + glsafe(::glDrawElements(GL_TRIANGLES, static_cast(m_indices_count), GL_UNSIGNED_INT, (const void*)0)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +} + +void GL_Model::send_to_gpu(const std::vector& vertices, const std::vector& indices) +{ + // vertex data -> send to gpu + glsafe(::glGenBuffers(1, &m_vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + + // indices data -> send to gpu + glsafe(::glGenBuffers(1, &m_ibo_id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo_id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) +{ + auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) { + data.positions.emplace_back(position); + data.normals.emplace_back(normal); + }; + + resolution = std::max(4, resolution); + + GLModelInitializationData data; + + const float angle_step = 2.0f * M_PI / static_cast(resolution); + std::vector cosines(resolution); + std::vector sines(resolution); + + for (int i = 0; i < resolution; ++i) + { + float angle = angle_step * static_cast(i); + cosines[i] = ::cos(angle); + sines[i] = -::sin(angle); + } + + const float total_height = tip_height + stem_height; + + // tip vertices/normals + append_vertex(data, { 0.0f, 0.0f, total_height }, Vec3f::UnitZ()); + for (int i = 0; i < resolution; ++i) + { + append_vertex(data, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); + } + + // tip triangles + for (int i = 0; i < resolution; ++i) + { + int v3 = (i < resolution - 1) ? i + 2 : 1; + data.triangles.emplace_back(0, i + 1, v3); + } + + // tip cap outer perimeter vertices + for (int i = 0; i < resolution; ++i) + { + append_vertex(data, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); + } + + // tip cap inner perimeter vertices + for (int i = 0; i < resolution; ++i) + { + append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); + } + + // tip cap triangles + for (int i = 0; i < resolution; ++i) + { + int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1; + int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1; + data.triangles.emplace_back(i + resolution + 1, v3, v2); + data.triangles.emplace_back(i + resolution + 1, i + 2 * resolution + 1, v3); + } + + // stem bottom vertices + for (int i = 0; i < resolution; ++i) + { + append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); + } + + // stem top vertices + for (int i = 0; i < resolution; ++i) + { + append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, { sines[i], cosines[i], 0.0f }); + } + + // stem triangles + for (int i = 0; i < resolution; ++i) + { + int v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1; + int v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1; + data.triangles.emplace_back(i + 3 * resolution + 1, v3, v2); + data.triangles.emplace_back(i + 3 * resolution + 1, i + 4 * resolution + 1, v3); + } + + // stem cap vertices + append_vertex(data, Vec3f::Zero(), -Vec3f::UnitZ()); + for (int i = 0; i < resolution; ++i) + { + append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, -Vec3f::UnitZ()); + } + + // stem cap triangles + for (int i = 0; i < resolution; ++i) + { + int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2; + data.triangles.emplace_back(5 * resolution + 1, v3, i + 5 * resolution + 2); + } + + return data; +} + +GLModelInitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness) +{ + auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) { + data.positions.emplace_back(position); + data.normals.emplace_back(normal); + }; + + resolution = std::max(2, resolution); + + GLModelInitializationData data; + + const float half_thickness = 0.5f * thickness; + const float half_stem_width = 0.5f * stem_width; + const float half_tip_width = 0.5f * tip_width; + + const float outer_radius = radius + half_stem_width; + const float inner_radius = radius - half_stem_width; + const float step_angle = 0.5f * PI / static_cast(resolution); + + // tip + // top face vertices + append_vertex(data, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { -tip_height, radius, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitZ()); + + // top face triangles + data.triangles.emplace_back(0, 1, 2); + data.triangles.emplace_back(0, 2, 4); + data.triangles.emplace_back(4, 2, 3); + + // bottom face vertices + append_vertex(data, { 0.0f, outer_radius, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { 0.0f, radius + half_tip_width, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { -tip_height, radius, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { 0.0f, radius - half_tip_width, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { 0.0f, inner_radius, -half_thickness }, -Vec3f::UnitZ()); + + // bottom face triangles + data.triangles.emplace_back(5, 7, 6); + data.triangles.emplace_back(5, 9, 7); + data.triangles.emplace_back(9, 8, 7); + + // side faces vertices + append_vertex(data, { 0.0f, outer_radius, -half_thickness }, Vec3f::UnitX()); + append_vertex(data, { 0.0f, radius + half_tip_width, -half_thickness }, Vec3f::UnitX()); + append_vertex(data, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitX()); + append_vertex(data, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitX()); + + Vec3f normal(-half_tip_width, tip_height, 0.0f); + normal.normalize(); + append_vertex(data, { 0.0f, radius + half_tip_width, -half_thickness }, normal); + append_vertex(data, { -tip_height, radius, -half_thickness }, normal); + append_vertex(data, { 0.0f, radius + half_tip_width, half_thickness }, normal); + append_vertex(data, { -tip_height, radius, half_thickness }, normal); + + normal = Vec3f(-half_tip_width, -tip_height, 0.0f); + normal.normalize(); + append_vertex(data, { -tip_height, radius, -half_thickness }, normal); + append_vertex(data, { 0.0f, radius - half_tip_width, -half_thickness }, normal); + append_vertex(data, { -tip_height, radius, half_thickness }, normal); + append_vertex(data, { 0.0f, radius - half_tip_width, half_thickness }, normal); + + append_vertex(data, { 0.0f, radius - half_tip_width, -half_thickness }, Vec3f::UnitX()); + append_vertex(data, { 0.0f, inner_radius, -half_thickness }, Vec3f::UnitX()); + append_vertex(data, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitX()); + append_vertex(data, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitX()); + + // side face triangles + for (int i = 0; i < 4; ++i) + { + int ii = i * 4; + data.triangles.emplace_back(10 + ii, 11 + ii, 13 + ii); + data.triangles.emplace_back(10 + ii, 13 + ii, 12 + ii); + } + + // stem + // top face vertices + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + append_vertex(data, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); + } + + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + append_vertex(data, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); + } + + // top face triangles + for (int i = 0; i < resolution; ++i) + { + data.triangles.emplace_back(26 + i, 27 + i, 27 + resolution + i); + data.triangles.emplace_back(27 + i, 28 + resolution + i, 27 + resolution + i); + } + + // bottom face vertices + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + append_vertex(data, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); + } + + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + append_vertex(data, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); + } + + // bottom face triangles + for (int i = 0; i < resolution; ++i) + { + data.triangles.emplace_back(28 + 2 * resolution + i, 29 + 3 * resolution + i, 29 + 2 * resolution + i); + data.triangles.emplace_back(29 + 2 * resolution + i, 29 + 3 * resolution + i, 30 + 3 * resolution + i); + } + + // side faces vertices and triangles + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + float c = ::cos(angle); + float s = ::sin(angle); + append_vertex(data, { inner_radius * s, inner_radius * c, -half_thickness }, { -s, -c, 0.0f }); + } + + for (int i = 0; i <= resolution; ++i) + { + float angle = static_cast(i) * step_angle; + float c = ::cos(angle); + float s = ::sin(angle); + append_vertex(data, { inner_radius * s, inner_radius * c, half_thickness }, { -s, -c, 0.0f }); + } + + int first_id = 26 + 4 * (resolution + 1); + for (int i = 0; i < resolution; ++i) + { + int ii = first_id + i; + data.triangles.emplace_back(ii, ii + 1, ii + resolution + 2); + data.triangles.emplace_back(ii, ii + resolution + 2, ii + resolution + 1); + } + + append_vertex(data, { inner_radius, 0.0f, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { outer_radius, 0.0f, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { inner_radius, 0.0f, half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { outer_radius, 0.0f, half_thickness }, -Vec3f::UnitY()); + + first_id = 26 + 6 * (resolution + 1); + data.triangles.emplace_back(first_id, first_id + 1, first_id + 3); + data.triangles.emplace_back(first_id, first_id + 3, first_id + 2); + + for (int i = resolution; i >= 0; --i) + { + float angle = static_cast(i) * step_angle; + float c = ::cos(angle); + float s = ::sin(angle); + append_vertex(data, { outer_radius * s, outer_radius * c, -half_thickness }, { s, c, 0.0f }); + } + + for (int i = resolution; i >= 0; --i) + { + float angle = static_cast(i) * step_angle; + float c = ::cos(angle); + float s = ::sin(angle); + append_vertex(data, { outer_radius * s, outer_radius * c, +half_thickness }, { s, c, 0.0f }); + } + + first_id = 30 + 6 * (resolution + 1); + for (int i = 0; i < resolution; ++i) + { + int ii = first_id + i; + data.triangles.emplace_back(ii, ii + 1, ii + resolution + 2); + data.triangles.emplace_back(ii, ii + resolution + 2, ii + resolution + 1); + } + + return data; +} + +GLModelInitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness) +{ + auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) { + data.positions.emplace_back(position); + data.normals.emplace_back(normal); + }; + + GLModelInitializationData data; + + const float half_thickness = 0.5f * thickness; + const float half_stem_width = 0.5f * stem_width; + const float half_tip_width = 0.5f * tip_width; + const float total_height = tip_height + stem_height; + + // top face vertices + append_vertex(data, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { 0.0, total_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { -half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { -half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(data, { -half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); + + // top face triangles + data.triangles.emplace_back(0, 1, 6); + data.triangles.emplace_back(6, 1, 5); + data.triangles.emplace_back(4, 5, 3); + data.triangles.emplace_back(5, 1, 3); + data.triangles.emplace_back(1, 2, 3); + + // bottom face vertices + append_vertex(data, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { 0.0, total_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(data, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ()); + + // bottom face triangles + data.triangles.emplace_back(7, 13, 8); + data.triangles.emplace_back(13, 12, 8); + data.triangles.emplace_back(12, 11, 10); + data.triangles.emplace_back(8, 12, 10); + data.triangles.emplace_back(9, 8, 10); + + // side faces vertices + append_vertex(data, { half_stem_width, 0.0, -half_thickness }, Vec3f::UnitX()); + append_vertex(data, { half_stem_width, stem_height, -half_thickness }, Vec3f::UnitX()); + append_vertex(data, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitX()); + append_vertex(data, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitX()); + + append_vertex(data, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY()); + + Vec3f normal(tip_height, half_tip_width, 0.0f); + normal.normalize(); + append_vertex(data, { half_tip_width, stem_height, -half_thickness }, normal); + append_vertex(data, { 0.0, total_height, -half_thickness }, normal); + append_vertex(data, { half_tip_width, stem_height, half_thickness }, normal); + append_vertex(data, { 0.0, total_height, half_thickness }, normal); + + normal = Vec3f(-tip_height, half_tip_width, 0.0f); + normal.normalize(); + append_vertex(data, { 0.0, total_height, -half_thickness }, normal); + append_vertex(data, { -half_tip_width, stem_height, -half_thickness }, normal); + append_vertex(data, { 0.0, total_height, half_thickness }, normal); + append_vertex(data, { -half_tip_width, stem_height, half_thickness }, normal); + + append_vertex(data, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { -half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY()); + + append_vertex(data, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitX()); + append_vertex(data, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitX()); + append_vertex(data, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitX()); + append_vertex(data, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitX()); + + append_vertex(data, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); + append_vertex(data, { half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); + + // side face triangles + for (int i = 0; i < 7; ++i) + { + int ii = i * 4; + data.triangles.emplace_back(14 + ii, 15 + ii, 17 + ii); + data.triangles.emplace_back(14 + ii, 17 + ii, 16 + ii); + } + + return data; +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp new file mode 100644 index 000000000..a11073b19 --- /dev/null +++ b/src/slic3r/GUI/GLModel.hpp @@ -0,0 +1,64 @@ +#ifndef slic3r_GLModel_hpp_ +#define slic3r_GLModel_hpp_ + +#include "libslic3r/Point.hpp" +#include "libslic3r/BoundingBox.hpp" +#include + +namespace Slic3r { + +class TriangleMesh; + +namespace GUI { + + struct GLModelInitializationData + { + std::vector positions; + std::vector normals; + std::vector triangles; + }; + + class GL_Model + { + unsigned int m_vbo_id{ 0 }; + unsigned int m_ibo_id{ 0 }; + size_t m_indices_count{ 0 }; + + BoundingBoxf3 m_bounding_box; + + public: + virtual ~GL_Model() { reset(); } + + void init_from(const GLModelInitializationData& data); + void init_from(const TriangleMesh& mesh); + void reset(); + void render() const; + + const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } + + private: + void send_to_gpu(const std::vector& vertices, const std::vector& indices); + }; + + + // create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution + // the origin of the arrow is in the center of the stem cap + // the arrow has its axis of symmetry along the Z axis and is pointing upward + GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, + float stem_radius, float stem_height); + + // create an arrow whose stem is a quarter of circle, with the given dimensions and resolution + // the origin of the arrow is in the center of the circle + // the arrow is contained in the 1st quadrant of the XY plane and is pointing counterclockwise + GLModelInitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness); + + // create an arrow with the given dimensions + // the origin of the arrow is in the center of the stem cap + // the arrow is contained in XY plane and has its main axis along the Y axis + GLModelInitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness); + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLModel_hpp_ + diff --git a/src/slic3r/GUI/GLSelectionRectangle.cpp b/src/slic3r/GUI/GLSelectionRectangle.cpp index eacf7a153..d1d2b89f1 100644 --- a/src/slic3r/GUI/GLSelectionRectangle.cpp +++ b/src/slic3r/GUI/GLSelectionRectangle.cpp @@ -2,9 +2,7 @@ #include "Camera.hpp" #include "3DScene.hpp" #include "GLCanvas3D.hpp" -#if ENABLE_NON_STATIC_CANVAS_MANAGER #include "GUI_App.hpp" -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #include @@ -38,11 +36,7 @@ namespace GUI { m_state = Off; -#if ENABLE_NON_STATIC_CANVAS_MANAGER const Camera& camera = wxGetApp().plater()->get_camera(); -#else - const Camera& camera = canvas.get_camera(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER const std::array& viewport = camera.get_viewport(); const Transform3d& modelview_matrix = camera.get_view_matrix(); const Transform3d& projection_matrix = camera.get_projection_matrix(); @@ -75,11 +69,7 @@ namespace GUI { if (!is_dragging()) return; -#if ENABLE_NON_STATIC_CANVAS_MANAGER const Camera& camera = wxGetApp().plater()->get_camera(); -#else - const Camera& camera = canvas.get_camera(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)camera.get_inv_zoom(); Size cnv_size = canvas.get_canvas_size(); diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 545b066bb..6a4d0f9b6 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -2,9 +2,7 @@ #include "GLTexture.hpp" #include "3DScene.hpp" -#if ENABLE_NON_STATIC_CANVAS_MANAGER -#include "GLCanvas3DManager.hpp" -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER +#include "OpenGLManager.hpp" #include @@ -443,7 +441,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo if (apply_anisotropy) { - GLfloat max_anisotropy = GLCanvas3DManager::get_gl_info().get_max_anisotropy(); + GLfloat max_anisotropy = OpenGLManager::get_gl_info().get_max_anisotropy(); if (max_anisotropy > 1.0f) glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy)); } @@ -592,7 +590,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo if (apply_anisotropy) { - GLfloat max_anisotropy = GLCanvas3DManager::get_gl_info().get_max_anisotropy(); + GLfloat max_anisotropy = OpenGLManager::get_gl_info().get_max_anisotropy(); if (max_anisotropy > 1.0f) glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy)); } diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 0bd087814..c69327cfa 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -3,14 +3,9 @@ #include "GLToolbar.hpp" -#if ENABLE_NON_STATIC_CANVAS_MANAGER #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Camera.hpp" -#else -#include "../../slic3r/GUI/GLCanvas3D.hpp" -#include "../../slic3r/GUI/Camera.hpp" -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #include #include @@ -252,7 +247,7 @@ bool GLToolbar::is_enabled() const void GLToolbar::set_enabled(bool enable) { - m_enabled = true; + m_enabled = enable;//true; etFIXME } bool GLToolbar::add_item(const GLToolbarItem::Data& data) @@ -537,7 +532,12 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) m_mouse_capture.left = true; m_mouse_capture.parent = &parent; processed = true; +#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI + if ((item_id != -2) && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() && + ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Left))) +#else if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Left))) +#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI { // mouse is inside an icon do_action(GLToolbarItem::Left, item_id, parent, true); @@ -554,7 +554,12 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) m_mouse_capture.right = true; m_mouse_capture.parent = &parent; processed = true; +#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI + if ((item_id != -2) && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() && + ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Right))) +#else if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Right))) +#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI { // mouse is inside an icon do_action(GLToolbarItem::Right, item_id, parent, true); @@ -632,6 +637,16 @@ float GLToolbar::get_main_size() const return size * m_layout.scale; } +int GLToolbar::get_visible_items_cnt() const +{ + int cnt = 0; + for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) + if (m_items[i]->is_visible() && !m_items[i]->is_separator()) + cnt++; + + return cnt; +} + void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover) { if ((m_pressed_toggable_id == -1) || (m_pressed_toggable_id == item_id)) @@ -639,7 +654,11 @@ void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas if ((0 <= item_id) && (item_id < (int)m_items.size())) { GLToolbarItem* item = m_items[item_id]; +#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI + if ((item != nullptr) && !item->is_separator() && !item->is_disabled() && (!check_hover || item->is_hovered())) +#else if ((item != nullptr) && !item->is_separator() && (!check_hover || item->is_hovered())) +#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI { if (((type == GLToolbarItem::Right) && item->is_right_toggable()) || ((type == GLToolbarItem::Left) && item->is_left_toggable())) @@ -729,11 +748,7 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC { // NB: mouse_pos is already scaled appropriately -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)parent.get_camera().get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float factor = m_layout.scale * inv_zoom; Size cnv_size = parent.get_canvas_size(); @@ -874,11 +889,7 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan { // NB: mouse_pos is already scaled appropriately -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)parent.get_camera().get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float factor = m_layout.scale * inv_zoom; Size cnv_size = parent.get_canvas_size(); @@ -1027,11 +1038,7 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3 { // NB: mouse_pos is already scaled appropriately -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)parent.get_camera().get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float factor = m_layout.scale * inv_zoom; Size cnv_size = parent.get_canvas_size(); @@ -1104,11 +1111,7 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& { // NB: mouse_pos is already scaled appropriately -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)parent.get_camera().get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float factor = m_layout.scale * inv_zoom; Size cnv_size = parent.get_canvas_size(); @@ -1260,11 +1263,7 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const int tex_width = m_icons_texture.get_width(); int tex_height = m_icons_texture.get_height(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)parent.get_camera().get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float factor = inv_zoom * m_layout.scale; float scaled_icons_size = m_layout.icons_size * factor; @@ -1312,11 +1311,7 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const int tex_width = m_icons_texture.get_width(); int tex_height = m_icons_texture.get_height(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float inv_zoom = (float)parent.get_camera().get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float factor = inv_zoom * m_layout.scale; float scaled_icons_size = m_layout.icons_size * factor; diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 27b43fef6..9911bb34a 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -319,6 +319,7 @@ public: void get_additional_tooltip(int item_id, std::string& text); void set_additional_tooltip(int item_id, const std::string& text); void set_tooltip(int item_id, const std::string& text); + int get_visible_items_cnt() const; // returns true if any item changed its state bool update_items_state(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 0774f3208..9a2a51e9c 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -283,29 +283,27 @@ GUI_App::~GUI_App() delete preset_updater; } -#if ENABLE_NON_STATIC_CANVAS_MANAGER std::string GUI_App::get_gl_info(bool format_as_html, bool extensions) { - return GLCanvas3DManager::get_gl_info().to_string(format_as_html, extensions); + return OpenGLManager::get_gl_info().to_string(format_as_html, extensions); } wxGLContext* GUI_App::init_glcontext(wxGLCanvas& canvas) { - return m_canvas_mgr.init_glcontext(canvas); + return m_opengl_mgr.init_glcontext(canvas); } bool GUI_App::init_opengl() { - return m_canvas_mgr.init_gl(); + return m_opengl_mgr.init_gl(); } -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER void GUI_App::init_app_config() { // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release. SetAppName(SLIC3R_APP_KEY); - //SetAppName(SLIC3R_APP_KEY "-beta"); - SetAppDisplayName(SLIC3R_APP_NAME); + SetAppName(SLIC3R_APP_KEY "-alpha"); +// SetAppDisplayName(SLIC3R_APP_NAME); // Set the Slic3r data directory at the Slic3r XS module. // Unix: ~/ .Slic3r @@ -324,6 +322,7 @@ void GUI_App::init_app_config() app_config->load(); } } + bool GUI_App::OnInit() { try { @@ -413,7 +412,8 @@ bool GUI_App::on_init_inner() if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) wxImage::AddHandler(new wxPNGHandler()); mainframe = new MainFrame(); - mainframe->switch_to(true); // hide settings tabs after first Layout + // hide settings tabs after first Layout + mainframe->select_tab(0); sidebar().obj_list()->init_objects(); // propagate model objects to object list // update_mode(); // !!! do that later @@ -580,28 +580,47 @@ float GUI_App::toolbar_icon_scale(const bool is_limited/* = false*/) const const std::string& use_val = app_config->get("use_custom_toolbar_size"); const std::string& val = app_config->get("custom_toolbar_size"); + const std::string& auto_val = app_config->get("auto_toolbar_size"); - if (val.empty() || use_val.empty() || use_val == "0") + if (val.empty() || auto_val.empty() || use_val.empty()) return icon_sc; - int int_val = atoi(val.c_str()); + int int_val = use_val == "0" ? 100 : atoi(val.c_str()); + // correct value in respect to auto_toolbar_size + int_val = std::min(atoi(auto_val.c_str()), int_val); + if (is_limited && int_val < 50) int_val = 50; return 0.01f * int_val * icon_sc; } -void GUI_App::recreate_GUI() +void GUI_App::set_auto_toolbar_icon_scale(float scale) const +{ +#ifdef __APPLE__ + const float icon_sc = 1.0f; // for Retina display will be used its own scale +#else + const float icon_sc = m_em_unit * 0.1f; +#endif // __APPLE__ + + int int_val = std::min(int(scale / icon_sc * 100), 100); + std::string val = std::to_string(int_val); + + app_config->set("auto_toolbar_size", val); +} + +void GUI_App::recreate_GUI(const wxString& msg_name) { mainframe->shutdown(); - const auto msg_name = _(L("Changing of an application language")) + dots; wxProgressDialog dlg(msg_name, msg_name); dlg.Pulse(); dlg.Update(10, _(L("Recreating")) + dots); MainFrame *old_main_frame = mainframe; mainframe = new MainFrame(); + // hide settings tabs after first Layout + mainframe->select_tab(0); // Propagate model objects to object list. sidebar().obj_list()->init_objects(); SetTopWindow(mainframe); @@ -705,12 +724,6 @@ void GUI_App::load_project(wxWindow *parent, wxString& input_file) const void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const { -#if ENABLE_CANVAS_TOOLTIP_USING_IMGUI - if (this->plater_ != nullptr) - // hides the tooltip - plater_->get_current_canvas3D()->set_tooltip(""); -#endif // ENABLE_CANVAS_TOOLTIP_USING_IMGUI - input_files.Clear(); wxFileDialog dialog(parent ? parent : GetTopWindow(), _(L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):")), @@ -724,7 +737,7 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const bool GUI_App::switch_language() { if (select_language()) { - recreate_GUI(); + recreate_GUI(_L("Changing of an application language") + dots); return true; } else { return false; @@ -1028,8 +1041,17 @@ void GUI_App::add_config_menu(wxMenuBar *menu) break; case ConfigMenuPreferences: { - PreferencesDialog dlg(mainframe); - dlg.ShowModal(); + bool recreate_app = false; + { + // the dialog needs to be destroyed before the call to recreate_GUI() + // or sometimes the application crashes into wxDialogBase() destructor + // so we put it into an inner scope + PreferencesDialog dlg(mainframe); + dlg.ShowModal(); + recreate_app = dlg.settings_layout_changed(); + } + if (recreate_app) + recreate_GUI(_L("Changing of the settings layout") + dots); break; } case ConfigMenuLanguage: diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index b2fcef48b..3afadf4e0 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -7,9 +7,7 @@ #include "MainFrame.hpp" #include "ImGuiWrapper.hpp" #include "ConfigWizard.hpp" -#if ENABLE_NON_STATIC_CANVAS_MANAGER -#include "GLCanvas3DManager.hpp" -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER +#include "OpenGLManager.hpp" #include #include @@ -100,9 +98,7 @@ class GUI_App : public wxApp // Best translation language, provided by Windows or OSX, owned by wxWidgets. const wxLanguageInfo *m_language_info_best = nullptr; -#if ENABLE_NON_STATIC_CANVAS_MANAGER - GLCanvas3DManager m_canvas_mgr; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER + OpenGLManager m_opengl_mgr; std::unique_ptr m_removable_drive_manager; @@ -117,11 +113,9 @@ public: GUI_App(); ~GUI_App() override; -#if ENABLE_NON_STATIC_CANVAS_MANAGER static std::string get_gl_info(bool format_as_html, bool extensions); wxGLContext* init_glcontext(wxGLCanvas& canvas); bool init_opengl(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER static unsigned get_colour_approx_luma(const wxColour &colour); static bool dark_mode(); @@ -141,8 +135,9 @@ public: const wxFont& normal_font() { return m_normal_font; } int em_unit() const { return m_em_unit; } float toolbar_icon_scale(const bool is_limited = false) const; + void set_auto_toolbar_icon_scale(float scale) const; - void recreate_GUI(); + void recreate_GUI(const wxString& message); void system_info(); void keyboard_shortcuts(); void load_project(wxWindow *parent, wxString& input_file) const; @@ -168,6 +163,7 @@ public: wxString current_language_code() const { return m_wxLocale->GetCanonicalName(); } // Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US". wxString current_language_code_safe() const; + bool is_localized() const { return m_wxLocale->GetLocale() != "English"; } virtual bool OnExceptionInMainLoop() override; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 1f2ce0221..b2283ba4a 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2771,6 +2771,7 @@ void ObjectList::delete_all_objects_from_list() void ObjectList::increase_object_instances(const size_t obj_idx, const size_t num) { select_item(m_objects_model->AddInstanceChild(m_objects_model->GetItemById(obj_idx), num)); + selection_changed(); } void ObjectList::decrease_object_instances(const size_t obj_idx, const size_t num) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index b4cbe4461..fc9779674 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1,5 +1,7 @@ #include "libslic3r/libslic3r.h" +#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/PreviewData.hpp" +#endif // !ENABLE_GCODE_VIEWER #include "GUI_Preview.hpp" #include "GUI_App.hpp" #include "GUI.hpp" @@ -7,7 +9,7 @@ #include "AppConfig.hpp" #include "3DScene.hpp" #include "BackgroundSlicingProcess.hpp" -#include "GLCanvas3DManager.hpp" +#include "OpenGLManager.hpp" #include "GLCanvas3D.hpp" #include "PresetBundle.hpp" #include "DoubleSlider.hpp" @@ -27,65 +29,36 @@ namespace Slic3r { namespace GUI { -#if ENABLE_NON_STATIC_CANVAS_MANAGER View3D::View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process) : m_canvas_widget(nullptr) , m_canvas(nullptr) { init(parent, model, config, process); } -#else -View3D::View3D(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process) - : m_canvas_widget(nullptr) - , m_canvas(nullptr) -{ - init(parent, bed, camera, view_toolbar, model, config, process); -} -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER View3D::~View3D() { -#if ENABLE_NON_STATIC_CANVAS_MANAGER if (m_canvas != nullptr) delete m_canvas; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER if (m_canvas_widget != nullptr) - { -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - _3DScene::remove_canvas(m_canvas_widget); -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER delete m_canvas_widget; -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - m_canvas = nullptr; -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - } } -#if ENABLE_NON_STATIC_CANVAS_MANAGER bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process) -#else -bool View3D::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process) -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER { if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */)) return false; -#if ENABLE_NON_STATIC_CANVAS_MANAGER - m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(*this); + m_canvas_widget = OpenGLManager::create_wxglcanvas(*this); if (m_canvas_widget == nullptr) return false; m_canvas = new GLCanvas3D(m_canvas_widget); m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget)); m_canvas->bind_event_handlers(); -#else - m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(this); - _3DScene::add_canvas(m_canvas_widget, bed, camera, view_toolbar); - m_canvas = _3DScene::get_canvas(this->m_canvas_widget); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - m_canvas->allow_multisample(GLCanvas3DManager::can_multisample()); + m_canvas->allow_multisample(OpenGLManager::can_multisample()); // XXX: If have OpenGL m_canvas->enable_picking(true); m_canvas->enable_moving(true); @@ -199,27 +172,15 @@ void View3D::render() m_canvas->set_as_dirty(); } -#if ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_GCODE_VIEWER Preview::Preview( wxWindow* parent, Model* model, DynamicPrintConfig* config, - BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, GCodeProcessor::Result* gcode_result, std::function schedule_background_process_func) + BackgroundSlicingProcess* process, GCodeProcessor::Result* gcode_result, std::function schedule_background_process_func) #else Preview::Preview( wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process_func) #endif // ENABLE_GCODE_VIEWER -#else -#if ENABLE_GCODE_VIEWER -Preview::Preview( - wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, - BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, GCodeProcessor::Result* gcode_result, std::function schedule_background_process_func) -#else -Preview::Preview( - wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, - BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process_func) -#endif // ENABLE_GCODE_VIEWER -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER : m_canvas_widget(nullptr) , m_canvas(nullptr) , m_double_slider_sizer(nullptr) @@ -241,9 +202,10 @@ Preview::Preview( #endif // ENABLE_GCODE_VIEWER , m_config(config) , m_process(process) - , m_gcode_preview_data(gcode_preview_data) #if ENABLE_GCODE_VIEWER , m_gcode_result(gcode_result) +#else + , m_gcode_preview_data(gcode_preview_data) #endif // ENABLE_GCODE_VIEWER , m_number_extruders(1) , m_preferred_color_mode("feature") @@ -254,11 +216,7 @@ Preview::Preview( , m_volumes_cleanup_required(false) #endif // __linux__ { -#if ENABLE_NON_STATIC_CANVAS_MANAGER if (init(parent, model)) -#else - if (init(parent, bed, camera, view_toolbar, model)) -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER { #if !ENABLE_GCODE_VIEWER show_hide_ui_elements("none"); @@ -267,29 +225,19 @@ Preview::Preview( } } -#if ENABLE_NON_STATIC_CANVAS_MANAGER bool Preview::init(wxWindow* parent, Model* model) -#else -bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model) -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER { if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */)) return false; -#if ENABLE_NON_STATIC_CANVAS_MANAGER - m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(*this); + m_canvas_widget = OpenGLManager::create_wxglcanvas(*this); if (m_canvas_widget == nullptr) return false; m_canvas = new GLCanvas3D(m_canvas_widget); m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget)); m_canvas->bind_event_handlers(); -#else - m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(this); - _3DScene::add_canvas(m_canvas_widget, bed, camera, view_toolbar); - m_canvas = _3DScene::get_canvas(this->m_canvas_widget); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - m_canvas->allow_multisample(GLCanvas3DManager::can_multisample()); + m_canvas->allow_multisample(OpenGLManager::can_multisample()); m_canvas->set_config(m_config); m_canvas->set_model(model); m_canvas->set_process(m_process); @@ -350,8 +298,9 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view _L("Pause prints") + "|0|" + _L("Custom GCodes") + "|0|" + _L("Shells") + "|0|" + + _L("Tool marker") + "|1|" + _L("Legend") + "|1" - ); +); Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_L("Options")), options_items); #else m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); @@ -435,19 +384,11 @@ Preview::~Preview() { unbind_event_handlers(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER if (m_canvas != nullptr) delete m_canvas; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER if (m_canvas_widget != nullptr) - { -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - _3DScene::remove_canvas(m_canvas_widget); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER delete m_canvas_widget; - m_canvas = nullptr; - } } void Preview::set_as_dirty() @@ -464,8 +405,13 @@ void Preview::set_number_extruders(unsigned int number_extruders) int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type m_choice_view_type->SetSelection(type); +#if ENABLE_GCODE_VIEWER + if ((0 <= type) && (type < static_cast(GCodeViewer::EViewType::Count))) + m_canvas->set_gcode_view_preview_type(static_cast(type)); +#else if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; +#endif // ENABLE_GCODE_VIEWER m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature"; } @@ -742,8 +688,13 @@ void Preview::update_view_type(bool slice_completed) int type = m_choice_view_type->FindString(choice); if (m_choice_view_type->GetSelection() != type) { m_choice_view_type->SetSelection(type); +#if ENABLE_GCODE_VIEWER + if ((0 <= type) && (type < static_cast(GCodeViewer::EViewType::Count))) + m_canvas->set_gcode_view_preview_type(static_cast(type)); +#else if (0 <= type && type < (int)GCodePreviewData::Extrusion::Num_View_Types) m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; +#endif // ENABLE_GCODE_VIEWER m_preferred_color_mode = "feature"; } } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index a7db054bc..a11a474cc 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -37,9 +37,7 @@ class GLCanvas3D; class GLToolbar; class Bed3D; struct Camera; -#if ENABLE_NON_STATIC_CANVAS_MANAGER class Plater; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER class View3D : public wxPanel { @@ -47,11 +45,7 @@ class View3D : public wxPanel GLCanvas3D* m_canvas; public: -#if ENABLE_NON_STATIC_CANVAS_MANAGER View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process); -#else - View3D(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER virtual ~View3D(); wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } @@ -79,11 +73,7 @@ public: void render(); private: -#if ENABLE_NON_STATIC_CANVAS_MANAGER bool init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process); -#else - bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER }; class Preview : public wxPanel @@ -133,23 +123,13 @@ class Preview : public wxPanel DoubleSlider::Control* m_slider {nullptr}; public: -#if ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_GCODE_VIEWER - Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, - BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, GCodeProcessor::Result* gcode_result, std::function schedule_background_process = []() {}); +Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process, + GCodeProcessor::Result* gcode_result, std::function schedule_background_process = []() {}); #else - Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, +Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process = []() {}); #endif // ENABLE_GCODE_VIEWER -#else -#if ENABLE_GCODE_VIEWER - Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, - BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, GCodeProcessor::Result* gcode_result, std::function schedule_background_process = []() {}); -#else - Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, - BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process = []() {}); -#endif // ENABLE_GCODE_VIEWER -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER virtual ~Preview(); wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } @@ -180,11 +160,7 @@ public: #endif // ENABLE_GCODE_VIEWER private: -#if ENABLE_NON_STATIC_CANVAS_MANAGER bool init(wxWindow* parent, Model* model); -#else - bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER void bind_event_handlers(); void unbind_event_handlers(); diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index dc64141ba..5b23aeee0 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -134,11 +134,8 @@ wxFont get_default_font_for_dpi(int dpi) NONCLIENTMETRICS nm; memset(&nm, 0, sizeof(NONCLIENTMETRICS)); nm.cbSize = sizeof(NONCLIENTMETRICS); - if (SystemParametersInfoForDpi_fn(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &nm, 0, dpi)) { - wxNativeFontInfo info; - info.lf = nm.lfMessageFont; - return wxFont(info); - } + if (SystemParametersInfoForDpi_fn(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &nm, 0, dpi)) + return wxFont(wxNativeFontInfo(nm.lfMessageFont)); } // Then try to guesstimate the font DPI scaling on Windows 8. // Let's hope that the font returned by the SystemParametersInfo(), which is used by wxWidgets internally, makes sense. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 8094d10ad..a75eed12c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -58,10 +58,10 @@ void GLGizmoFdmSupports::set_fdm_support_data(ModelObject* model_object, const S return; if (mo && selection.is_from_single_instance() - && (mo != m_old_mo || mo->volumes.size() != m_old_volumes_size)) + && (mo->id() != m_old_mo_id || mo->volumes.size() != m_old_volumes_size)) { - update_mesh(); - m_old_mo = mo; + update_from_model_object(); + m_old_mo_id = mo->id(); m_old_volumes_size = mo->volumes.size(); } } @@ -84,12 +84,29 @@ void GLGizmoFdmSupports::on_render() const void GLGizmoFdmSupports::render_triangles(const Selection& selection) const { + if (m_setting_angle) + return; + const ModelObject* mo = m_c->selection_info()->model_object(); glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); } ); glsafe(::glPolygonOffset(-1.0, 1.0)); + // Take care of the clipping plane. The normal of the clipping plane is + // saved with opposite sign than we need to pass to OpenGL (FIXME) + bool clipping_plane_active = m_c->object_clipper()->get_position() != 0.; + if (clipping_plane_active) { + const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane(); + double clp_data[4]; + memcpy(clp_data, clp->get_data(), 4 * sizeof(double)); + for (int i=0; i<3; ++i) + clp_data[i] = -1. * clp_data[i]; + + glsafe(::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)clp_data)); + glsafe(::glEnable(GL_CLIP_PLANE0)); + } + int mesh_id = -1; for (const ModelVolume* mv : mo->volumes) { if (! mv->is_model_part()) @@ -113,6 +130,8 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const } glsafe(::glPopMatrix()); } + if (clipping_plane_active) + glsafe(::glDisable(GL_CLIP_PLANE0)); } @@ -161,14 +180,21 @@ void GLGizmoFdmSupports::render_cursor_circle() const } -void GLGizmoFdmSupports::on_render_for_picking() const +void GLGizmoFdmSupports::update_model_object() const { - + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + ++idx; + if (! mv->is_model_part()) + continue; + for (int i=0; im_supported_facets.set_facet(i, m_selected_facets[idx][i]); + } } - -void GLGizmoFdmSupports::update_mesh() +void GLGizmoFdmSupports::update_from_model_object() { wxBusyCursor wait; @@ -177,7 +203,6 @@ void GLGizmoFdmSupports::update_mesh() for (const ModelVolume* mv : mo->volumes) if (mv->is_model_part()) ++num_of_volumes; - m_selected_facets.resize(num_of_volumes); m_neighbors.resize(num_of_volumes); m_ivas.clear(); @@ -218,6 +243,21 @@ void GLGizmoFdmSupports::update_mesh() +bool GLGizmoFdmSupports::is_mesh_point_clipped(const Vec3d& point) const +{ + if (m_c->object_clipper()->get_position() == 0.) + return false; + + auto sel_info = m_c->selection_info(); + int active_inst = m_c->selection_info()->get_active_instance(); + const ModelInstance* mi = sel_info->model_object()->instances[active_inst]; + const Transform3d& trafo = mi->get_transformation().get_matrix(); + + Vec3d transformed_point = trafo * point; + transformed_point(2) += sel_info->get_sla_shift(); + return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point); +} + bool operator<(const GLGizmoFdmSupports::NeighborData& a, const GLGizmoFdmSupports::NeighborData& b) { return a.first < b.first; @@ -310,6 +350,12 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous m_clipping_plane.get(), &facet)) { + // In case this hit is clipped, skip it. + if (is_mesh_point_clipped(hit.cast())) { + some_mesh_was_hit = true; + continue; + } + // Is this hit the closest to the camera so far? double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast()).squaredNorm(); if (hit_squared_distance < closest_hit_squared_distance) { @@ -430,19 +476,16 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp) && m_button_down != Button::None) { + // Take snapshot and update ModelVolume data. + wxString action_name = shift_down + ? _L("Remove selection") + : (m_button_down == Button::Left + ? _L("Add supports") + : _L("Block supports")); + Plater::TakeSnapshot(wxGetApp().plater(), action_name); + update_model_object(); + m_button_down = Button::None; - - // Synchronize gizmo with ModelVolume data. - ModelObject* mo = m_c->selection_info()->model_object(); - int idx = -1; - for (ModelVolume* mv : mo->volumes) { - ++idx; - if (! mv->is_model_part()) - continue; - for (int i=0; im_supported_facets.set_facet(i, m_selected_facets[idx][i]); - } - return true; } @@ -481,6 +524,46 @@ void GLGizmoFdmSupports::update_vertex_buffers(const ModelVolume* mv, } +void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool overwrite, bool block) +{ + float threshold = (M_PI/180.)*threshold_deg; + const Selection& selection = m_parent.get_selection(); + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; + + int mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++mesh_id; + + const Transform3d trafo_matrix = mi->get_matrix(true) * mv->get_matrix(true); + Vec3f down = (trafo_matrix.inverse() * (-Vec3d::UnitZ())).cast().normalized(); + Vec3f limit = (trafo_matrix.inverse() * Vec3d(std::sin(threshold), 0, -std::cos(threshold))).cast().normalized(); + + float dot_limit = limit.dot(down); + + // Now calculate dot product of vert_direction and facets' normals. + int idx = -1; + for (const stl_facet& facet : mv->mesh().stl.facet_start) { + ++idx; + if (facet.normal.dot(down) > dot_limit && (overwrite || m_selected_facets[mesh_id][idx] == FacetSupportType::NONE)) + m_selected_facets[mesh_id][idx] = block + ? FacetSupportType::BLOCKER + : FacetSupportType::ENFORCER; + } + update_vertex_buffers(mv, mesh_id, true, true); + } + + Plater::TakeSnapshot(wxGetApp().plater(), block ? _L("Block supports by angle") + : _L("Add supports by angle")); + update_model_object(); + m_parent.set_as_dirty(); + m_setting_angle = false; +} + + void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_limit) { if (! m_c->selection_info()->model_object()) @@ -489,96 +572,131 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l const float approx_height = m_imgui->scaled(18.0f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); - m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); - // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: - const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); - const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); - const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); - const float minimal_slider_width = m_imgui->scaled(4.f); + if (! m_setting_angle) { + m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); - float caption_max = 0.f; - float total_text_max = 0.; - for (const std::string& t : {"enforce", "block", "remove"}) { - caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x); - total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); - } - caption_max += m_imgui->scaled(1.f); - total_text_max += m_imgui->scaled(1.f); + // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); + const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); + const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); + const float minimal_slider_width = m_imgui->scaled(4.f); - float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left); - window_width = std::max(window_width, total_text_max); - window_width = std::max(window_width, button_width); + float caption_max = 0.f; + float total_text_max = 0.; + for (const std::string& t : {"enforce", "block", "remove"}) { + caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x); + total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); + } + caption_max += m_imgui->scaled(1.f); + total_text_max += m_imgui->scaled(1.f); - auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { - static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - m_imgui->text(caption); - ImGui::PopStyleColor(); - ImGui::SameLine(caption_max); - m_imgui->text(text); - }; + float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left); + window_width = std::max(window_width, total_text_max); + window_width = std::max(window_width, button_width); - for (const std::string& t : {"enforce", "block", "remove"}) - draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); + auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { + static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + m_imgui->text(caption); + ImGui::PopStyleColor(); + ImGui::SameLine(caption_max); + m_imgui->text(text); + }; - m_imgui->text(""); + for (const std::string& t : {"enforce", "block", "remove"}) + draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); - if (m_imgui->button(m_desc.at("remove_all"))) { - ModelObject* mo = m_c->selection_info()->model_object(); - int idx = -1; - for (ModelVolume* mv : mo->volumes) { - ++idx; - if (mv->is_model_part()) { - m_selected_facets[idx].assign(m_selected_facets[idx].size(), FacetSupportType::NONE); - mv->m_supported_facets.clear(); - update_vertex_buffers(mv, idx, true, true); - m_parent.set_as_dirty(); + m_imgui->text(""); + + if (m_imgui->button("Autoset by angle...")) { + m_setting_angle = true; + } + + ImGui::SameLine(); + + if (m_imgui->button(m_desc.at("remove_all"))) { + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + ++idx; + if (mv->is_model_part()) { + m_selected_facets[idx].assign(m_selected_facets[idx].size(), FacetSupportType::NONE); + mv->m_supported_facets.clear(); + update_vertex_buffers(mv, idx, true, true); + m_parent.set_as_dirty(); + } } } - } - const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; + const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; - m_imgui->text(m_desc.at("cursor_size")); - ImGui::SameLine(clipping_slider_left); - ImGui::PushItemWidth(window_width - clipping_slider_left); - ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(max_tooltip_width); - ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } + m_imgui->text(m_desc.at("cursor_size")); + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } - ImGui::Separator(); - if (m_c->object_clipper()->get_position() == 0.f) - m_imgui->text(m_desc.at("clipping_of_view")); - else { - if (m_imgui->button(m_desc.at("reset_direction"))) { - wxGetApp().CallAfter([this](){ - m_c->object_clipper()->set_position(-1., false); - }); + ImGui::Separator(); + if (m_c->object_clipper()->get_position() == 0.f) + m_imgui->text(m_desc.at("clipping_of_view")); + else { + if (m_imgui->button(m_desc.at("reset_direction"))) { + wxGetApp().CallAfter([this](){ + m_c->object_clipper()->set_position(-1., false); + }); + } + } + + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + float clp_dist = m_c->object_clipper()->get_position(); + if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) + m_c->object_clipper()->set_position(clp_dist, true); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + m_imgui->end(); + if (m_setting_angle) { + m_parent.show_slope(false); + m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg}); + m_parent.use_slope(true); + m_parent.set_as_dirty(); } } - - ImGui::SameLine(clipping_slider_left); - ImGui::PushItemWidth(window_width - clipping_slider_left); - float clp_dist = m_c->object_clipper()->get_position(); - if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) - m_c->object_clipper()->set_position(clp_dist, true); - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(max_tooltip_width); - ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); + else { + std::string name = "Autoset custom supports"; + m_imgui->begin(wxString(name), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->text("Threshold:"); + ImGui::SameLine(); + if (m_imgui->slider_float("", &m_angle_threshold_deg, 0.f, 90.f, "%.f")) + m_parent.set_slope_range({90.f - m_angle_threshold_deg, 90.f - m_angle_threshold_deg}); + m_imgui->checkbox(wxString("Overwrite already selected facets"), m_overwrite_selected); + if (m_imgui->button("Enforce")) + select_facets_by_angle(m_angle_threshold_deg, m_overwrite_selected, false); + ImGui::SameLine(); + if (m_imgui->button("Block")) + select_facets_by_angle(m_angle_threshold_deg, m_overwrite_selected, true); + ImGui::SameLine(); + if (m_imgui->button("Cancel")) + m_setting_angle = false; + m_imgui->end(); + if (! m_setting_angle) { + m_parent.use_slope(false); + m_parent.set_as_dirty(); + } } - - - - m_imgui->end(); } bool GLGizmoFdmSupports::on_is_activable() const @@ -627,12 +745,24 @@ void GLGizmoFdmSupports::on_set_state() return; if (m_state == On && m_old_state != On) { // the gizmo was just turned on - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned on"))); + { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned on"))); + } + if (! m_parent.get_gizmos_manager().is_serializing()) { + wxGetApp().CallAfter([]() { + wxGetApp().plater()->enter_gizmos_stack(); + }); + } } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off // we are actually shutting down - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned off"))); - m_old_mo = nullptr; + m_setting_angle = false; + m_parent.use_slope(false); + wxGetApp().plater()->leave_gizmos_stack(); + { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned off"))); + } + m_old_mo_id = -1; m_ivas.clear(); m_neighbors.clear(); m_selected_facets.clear(); @@ -657,7 +787,7 @@ void GLGizmoFdmSupports::on_stop_dragging() void GLGizmoFdmSupports::on_load(cereal::BinaryInputArchive& ar) { - + update_from_model_object(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 994218f69..bed6d00a0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -19,7 +19,7 @@ enum class SLAGizmoEventType : unsigned char; class GLGizmoFdmSupports : public GLGizmoBase { private: - const ModelObject* m_old_mo = nullptr; + ObjectID m_old_mo_id; size_t m_old_volumes_size = 0; GLUquadricObj* m_quadric; @@ -53,14 +53,23 @@ public: private: bool on_init() override; void on_render() const override; - void on_render_for_picking() const override; + void on_render_for_picking() const override {} void render_triangles(const Selection& selection) const; void render_cursor_circle() const; - void update_mesh(); + + void update_model_object() const; + void update_from_model_object(); + + void select_facets_by_angle(float threshold, bool overwrite, bool block); + bool m_overwrite_selected = false; + float m_angle_threshold_deg = 45.f; + + bool is_mesh_point_clipped(const Vec3d& point) const; float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; + bool m_setting_angle = false; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index d2f668dab..8d4616da3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -58,7 +58,10 @@ void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&) const ModelObject* mo = m_c->selection_info()->model_object(); if (mo) { - reload_cache(); + if (m_old_mo_id != mo->id()) { + reload_cache(); + m_old_mo_id = mo->id(); + } if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) m_holes_in_drilled_mesh = mo->sla_drain_holes; } @@ -220,11 +223,7 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pairraycaster()->raycaster()) return false; -#if ENABLE_NON_STATIC_CANVAS_MANAGER const Camera& camera = wxGetApp().plater()->get_camera(); -#else - const Camera& camera = m_parent.get_camera(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER const Selection& selection = m_parent.get_selection(); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); Geometry::Transformation trafo = volume->get_instance_transformation(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 3ee83c345..93e577743 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -43,6 +43,8 @@ private: void hollow_mesh(bool postpone_error_messages = false); bool unsaved_changes() const; + ObjectID m_old_mo_id = -1; + // bool m_show_supports = true; float m_new_hole_radius = 2.f; // Size of a new hole. float m_new_hole_height = 6.f; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 496568d51..261738d44 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -66,11 +66,10 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S ModelObject* mo = m_c->selection_info()->model_object(); - if (mo != m_old_mo) { + if (mo && mo->id() != m_old_mo_id) { disable_editing_mode(); - if (mo) - reload_cache(); - m_old_mo = mo; + reload_cache(); + m_old_mo_id = mo->id(); } // If we triggered autogeneration before, check backend and fetch results if they are there diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 917d22a98..b6cad8f9a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -83,7 +83,7 @@ private: float m_density_stash = 0.f; // and again mutable std::vector m_editing_cache; // a support point and whether it is currently selected std::vector m_normal_cache; // to restore after discarding changes or undo/redo - const ModelObject* m_old_mo = nullptr; + ObjectID m_old_mo_id; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 685e49d36..8ff6c121f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -2,9 +2,7 @@ #include "GLGizmosManager.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/3DScene.hpp" -#if ENABLE_NON_STATIC_CANVAS_MANAGER #include "slic3r/GUI/Camera.hpp" -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_ObjectManipulation.hpp" #include "slic3r/GUI/PresetBundle.hpp" @@ -371,15 +369,6 @@ void GLGizmosManager::set_sla_support_data(ModelObject* model_object) || wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) return; - /*m_common_gizmos_data->update_from_backend(m_parent, model_object); - - auto* gizmo_supports = dynamic_cast(m_gizmos[SlaSupports].get()); - - - // note: sla support gizmo takes care of updating the common data. - // following lines are thus dependent - //gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection()); - */ auto* gizmo_hollow = dynamic_cast(m_gizmos[Hollow].get()); auto* gizmo_supports = dynamic_cast(m_gizmos[SlaSupports].get()); gizmo_hollow->set_sla_support_data(model_object, m_parent.get_selection()); @@ -1076,13 +1065,8 @@ void GLGizmosManager::do_render_overlay() const float cnv_w = (float)m_parent.get_canvas_size().get_width(); float cnv_h = (float)m_parent.get_canvas_size().get_height(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER float zoom = (float)wxGetApp().plater()->get_camera().get_zoom(); float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); -#else - float zoom = (float)m_parent.get_camera().get_zoom(); - float inv_zoom = (float)m_parent.get_camera().get_inv_zoom(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER float height = get_scaled_total_height(); float width = get_scaled_total_width(); @@ -1133,11 +1117,7 @@ void GLGizmosManager::do_render_overlay() const GLTexture::render_sub_texture(icons_texture_id, zoomed_top_x, zoomed_top_x + zoomed_icons_size, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); if (idx == m_current) { -#if ENABLE_NON_STATIC_CANVAS_MANAGER float toolbar_top = cnv_h - wxGetApp().plater()->get_view_toolbar().get_height(); -#else - float toolbar_top = cnv_h - m_parent.get_view_toolbar_height(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER gizmo->render_input_window(width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top); } zoomed_top_y -= zoomed_stride_y; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 478774718..a7fb82126 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -139,6 +139,11 @@ public: EType new_current = m_current; m_current = old_current; + // Update common data. They should be updated when activate_gizmo is + // called, so it can be used in on_set_state which is called from there. + if (new_current != Undefined) + m_common_gizmos_data->update(m_gizmos[new_current]->get_requirements()); + // activate_gizmo call sets m_current and calls set_state for the gizmo // it does nothing in case the gizmo is already activated // it can safely be called for Undefined gizmo @@ -167,6 +172,7 @@ public: void refresh_on_off_state(); void reset_all_states(); + bool is_serializing() const { return m_serializing; } void set_hover_id(int id); void enable_grabber(EType type, unsigned int id, bool enable); @@ -220,6 +226,8 @@ public: void update_after_undo_redo(const UndoRedo::Snapshot& snapshot); + int get_selectable_icons_cnt() const { return get_selectable_idxs().size(); } + private: void render_background(float left, float top, float right, float bottom, float border) const; void do_render_overlay() const; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index ea8b2afd3..755d84180 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -27,10 +28,22 @@ #include "I18N.hpp" #include "Search.hpp" +#include "../Utils/MacDarkMode.hpp" +#include "nanosvg/nanosvg.h" +#include "nanosvg/nanosvgrast.h" + namespace Slic3r { namespace GUI { +static const std::map font_icons = { + {ImGui::PrintIconMarker , "cog" }, + {ImGui::PrinterIconMarker , "printer" }, + {ImGui::PrinterSlaIconMarker, "sla_printer"}, + {ImGui::FilamentIconMarker , "spool" }, + {ImGui::MaterialIconMarker , "resin" } +}; + ImGuiWrapper::ImGuiWrapper() : m_glyph_ranges(nullptr) , m_font_cjk(false) @@ -614,8 +627,9 @@ static void process_key_down(ImGuiKey imgui_key, std::function f) } void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str, - Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel) + Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel, bool is_localized) { + int& hovered_id = view_params.hovered_id; // ImGui::ListBoxHeader("", size); { // rewrote part of function to add a TextInput instead of label Text @@ -655,7 +669,7 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co ImGui::InputTextEx("", NULL, search_str, 20, search_size, ImGuiInputTextFlags_AutoSelectAll, NULL, NULL); edited = ImGui::IsItemEdited(); if (edited) - view_params.hovered_id = -1; + hovered_id = 0; process_key_down(ImGuiKey_Escape, [&selected, search_str, str]() { // use 9999 to mark selection as a Esc key @@ -671,7 +685,6 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co const char* item_text; const char* tooltip; int mouse_hovered = -1; - int& hovered_id = view_params.hovered_id; while (items_getter(i, &item_text, &tooltip)) { @@ -679,7 +692,7 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", /*item_text*/tooltip); - view_params.hovered_id = -1; + hovered_id = -1; mouse_hovered = i; } @@ -688,8 +701,6 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co i++; } - scroll_y(mouse_hovered); - // Process mouse wheel if (mouse_hovered > 0) process_mouse_wheel(mouse_wheel); @@ -699,7 +710,7 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co if (mouse_hovered > 0) scroll_up(); else { - if (hovered_id > 0 && hovered_id != size_t(-1)) + if (hovered_id > 0) --hovered_id; scroll_y(hovered_id); } @@ -709,9 +720,9 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co if (mouse_hovered > 0) scroll_down(); else { - if (hovered_id == size_t(-1)) + if (hovered_id < 0) hovered_id = 0; - else if (hovered_id < size_t(i - 1)) + else if (hovered_id < i - 1) ++hovered_id; scroll_y(hovered_id); } @@ -735,9 +746,10 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co // add checkboxes for show/hide Categories and Groups text(_L("Use for search")+":"); - check_box(_L("Type"), view_params.type); check_box(_L("Category"), view_params.category); check_box(_L("Group"), view_params.group); + if (is_localized) + check_box(_L("Search in English"), view_params.english); } void ImGuiWrapper::disabled_begin(bool disabled) @@ -791,6 +803,59 @@ static const ImWchar ranges_keyboard_shortcuts[] = }; #endif // __APPLE__ + +std::vector ImGuiWrapper::load_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height) +{ +#ifdef __APPLE__ + // Note: win->GetContentScaleFactor() is not used anymore here because it tends to + // return bogus results quite often (such as 1.0 on Retina or even 0.0). + // We're using the max scaling factor across all screens because it's very likely to be good enough. + double scale = mac_max_scaling_factor(); +#else + double scale = 1.0; +#endif + std::vector empty_vector; + +#ifdef __WXMSW__ + std::string folder = "white\\"; +#else + std::string folder = "white/"; +#endif + if (!boost::filesystem::exists(Slic3r::var(folder + bitmap_name + ".svg"))) + folder.clear(); + + NSVGimage* image = ::nsvgParseFromFile(Slic3r::var(folder + bitmap_name + ".svg").c_str(), "px", 96.0f); + if (image == nullptr) + return empty_vector; + + target_height != 0 ? target_height *= scale : target_width *= scale; + + float svg_scale = target_height != 0 ? + (float)target_height / image->height : target_width != 0 ? + (float)target_width / image->width : 1; + + int width = (int)(svg_scale * image->width + 0.5f); + int height = (int)(svg_scale * image->height + 0.5f); + int n_pixels = width * height; + if (n_pixels <= 0) { + ::nsvgDelete(image); + return empty_vector; + } + + NSVGrasterizer* rast = ::nsvgCreateRasterizer(); + if (rast == nullptr) { + ::nsvgDelete(image); + return empty_vector; + } + + std::vector data(n_pixels * 4, 0); + ::nsvgRasterize(rast, image, 0, 0, svg_scale, data.data(), width, height, width * 4); + ::nsvgDeleteRasterizer(rast); + ::nsvgDelete(image); + + return data; +} + void ImGuiWrapper::init_font(bool compress) { destroy_font(); @@ -829,11 +894,33 @@ void ImGuiWrapper::init_font(bool compress) } #endif + float font_scale = m_font_size/15; + int icon_sz = lround(16 * font_scale); // default size of icon is 16 px + + int rect_id = io.Fonts->CustomRects.Size; // id of the rectangle added next + // add rectangles for the icons to the font atlas + for (auto& icon : font_icons) + io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz); + // Build texture atlas unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + // Fill rectangles from the SVG-icons + for (auto icon : font_icons) { + if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { + std::vector raw_data = load_svg(icon.second, icon_sz, icon_sz); + const ImU32* pIn = (ImU32*)raw_data.data(); + for (int y = 0; y < icon_sz; y++) { + ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); + for (int x = 0; x < icon_sz; x++) + *pOut++ = *pIn++; + } + } + rect_id++; + } + // Upload texture to graphics system GLint last_texture; glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 6a1e27dcb..bf542e138 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -79,7 +79,7 @@ public: bool combo(const wxString& label, const std::vector& options, int& selection); // Use -1 to not mark any option as selected bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel); void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str, - Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel); + Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel, bool is_localized); void disabled_begin(bool disabled); void disabled_end(); @@ -96,6 +96,7 @@ private: void render_draw_data(ImDrawData *draw_data); bool display_initialized() const; void destroy_font(); + std::vector load_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height); static const char* clipboard_get(void* user_data); static void clipboard_set(void* user_data, const char* text); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index d3bd2720e..73b78d3df 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +//#include #include #include @@ -28,6 +28,7 @@ #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" #include "I18N.hpp" +#include "GLCanvas3D.hpp" #include #include "GUI_App.hpp" @@ -90,10 +91,12 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S // initialize layout auto sizer = new wxBoxSizer(wxVERTICAL); - if (m_plater) + if (m_plater && m_layout != slOld) sizer->Add(m_plater, 1, wxEXPAND); - if (m_tabpanel) + + if (m_tabpanel && m_layout != slDlg) sizer->Add(m_tabpanel, 1, wxEXPAND); + sizer->SetSizeHints(this); SetSizer(sizer); Fit(); @@ -212,7 +215,6 @@ void MainFrame::shutdown() if (m_plater) m_plater->stop_jobs(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER // Unbinding of wxWidgets event handling in canvases needs to be done here because on MAC, // when closing the application using Command+Q, a mouse event is triggered after this lambda is completed, // causing a crash @@ -221,7 +223,6 @@ void MainFrame::shutdown() // Cleanup of canvases' volumes needs to be done here or a crash may happen on some Linux Debian flavours // see: https://github.com/prusa3d/PrusaSlicer/issues/3964 if (m_plater) m_plater->reset_canvas_volumes(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER // Weird things happen as the Paint messages are floating around the windows being destructed. // Avoid the Paint messages by hiding the main window. @@ -229,6 +230,9 @@ void MainFrame::shutdown() // In addition, there were some crashes due to the Paint events sent to already destructed windows. this->Show(false); + if (m_settings_dialog) + m_settings_dialog->Destroy(); + // Stop the background thread (Windows and Linux). // Disconnect from a 3DConnextion driver (OSX). m_plater->get_mouse3d_controller().shutdown(); @@ -244,9 +248,6 @@ void MainFrame::shutdown() wxGetApp().app_config->save(); // if (m_plater) // m_plater->print = undef; -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - _3DScene::remove_all_canvases(); -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER // Slic3r::GUI::deregister_on_request_update_callback(); // set to null tabs and a plater @@ -289,12 +290,25 @@ void MainFrame::update_title() void MainFrame::init_tabpanel() { - // wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10 - // with multiple high resolution displays connected. - m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME); + m_layout = wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? slOld : + wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? slNew : + wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? slDlg : slOld; + + // From the very beginning the Print settings should be selected + m_last_selected_tab = m_layout == slDlg ? 0 : 1; + + if (m_layout == slDlg) { + m_settings_dialog = new SettingsDialog(this); + m_tabpanel = m_settings_dialog->get_tabpanel(); + } + else { + // wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10 + // with multiple high resolution displays connected. + m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME); #ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList - m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font()); + m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font()); #endif + } m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxEvent&) { auto panel = m_tabpanel->GetCurrentPage(); @@ -307,17 +321,22 @@ void MainFrame::init_tabpanel() // On GTK, the wxEVT_NOTEBOOK_PAGE_CHANGED event is triggered // before the MainFrame is fully set up. static_cast(panel)->OnActivate(); + m_last_selected_tab = m_tabpanel->GetSelection(); } else select_tab(0); }); -//! m_plater = new Slic3r::GUI::Plater(m_tabpanel, this); - m_plater = new Plater(this, this); - + if (m_layout == slOld) { + m_plater = new Plater(m_tabpanel, this); + m_tabpanel->AddPage(m_plater, _L("Plater")); + } + else { + m_plater = new Plater(this, this); + if (m_layout == slNew) + m_tabpanel->AddPage(new wxPanel(m_tabpanel), _L("Plater")); // empty panel just for Plater tab + } wxGetApp().plater_ = m_plater; -// m_tabpanel->AddPage(m_plater, _(L("Plater"))); - m_tabpanel->AddPage(new wxPanel(m_tabpanel), _L("Plater")); // empty panel just for Plater tab wxGetApp().obj_list()->create_popup_menus(); @@ -343,13 +362,6 @@ void MainFrame::init_tabpanel() } } -void MainFrame::switch_to(bool plater) -{ - this->m_plater->Show(plater); - this->m_tabpanel->Show(!plater); - this->Layout(); -} - void MainFrame::create_preset_tabs() { wxGetApp().update_label_colours_from_appconfig(); @@ -465,6 +477,11 @@ bool MainFrame::can_slice() const bool MainFrame::can_change_view() const { + if (m_layout == slNew) + return m_plater->IsShown(); + if (m_layout == slDlg) + return true; + // slOld layout mode int page_id = m_tabpanel->GetSelection(); return page_id != wxNOT_FOUND && dynamic_cast(m_tabpanel->GetPage((size_t)page_id)) != nullptr; } @@ -763,25 +780,21 @@ void MainFrame::init_menubar() // Window menu auto windowMenu = new wxMenu(); { -//! size_t tab_offset = 0; if (m_plater) { append_menu_item(windowMenu, wxID_HIGHEST + 1, _(L("&Plater Tab")) + "\tCtrl+1", _(L("Show the plater")), - [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*(size_t)(-1)*/0); }, "plater", nullptr, + [this](wxCommandEvent&) { select_tab(0); }, "plater", nullptr, [this]() {return true; }, this); -//! tab_offset += 1; -//! } -//! if (tab_offset > 0) { windowMenu->AppendSeparator(); } append_menu_item(windowMenu, wxID_HIGHEST + 2, _(L("P&rint Settings Tab")) + "\tCtrl+2", _(L("Show the print settings")), - [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + 0*/1); }, "cog", nullptr, + [this/*, tab_offset*/](wxCommandEvent&) { select_tab(1); }, "cog", nullptr, [this]() {return true; }, this); wxMenuItem* item_material_tab = append_menu_item(windowMenu, wxID_HIGHEST + 3, _(L("&Filament Settings Tab")) + "\tCtrl+3", _(L("Show the filament settings")), - [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + 1*/2); }, "spool", nullptr, + [this/*, tab_offset*/](wxCommandEvent&) { select_tab(2); }, "spool", nullptr, [this]() {return true; }, this); m_changeable_menu_items.push_back(item_material_tab); wxMenuItem* item_printer_tab = append_menu_item(windowMenu, wxID_HIGHEST + 4, _(L("Print&er Settings Tab")) + "\tCtrl+4", _(L("Show the printer settings")), - [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + 2*/3); }, "printer", nullptr, + [this/*, tab_offset*/](wxCommandEvent&) { select_tab(3); }, "printer", nullptr, [this]() {return true; }, this); m_changeable_menu_items.push_back(item_printer_tab); if (m_plater) { @@ -1245,17 +1258,39 @@ void MainFrame::load_config(const DynamicPrintConfig& config) #endif } -void MainFrame::select_tab(size_t tab) +void MainFrame::select_tab(size_t tab/* = size_t(-1)*/) { - if (tab == /*(size_t)(-1)*/0) { - if (m_plater && !m_plater->IsShown()) - this->switch_to(true); + if (m_layout == slDlg) { + if (tab==0) { + if (m_settings_dialog->IsShown()) + this->SetFocus(); + // plater should be focused for correct navigation inside search window + if (m_plater->canvas3D()->is_search_pressed()) + m_plater->SetFocus(); + return; + } + // Show/Activate Settings Dialog + if (m_settings_dialog->IsShown()) +#ifdef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList + m_settings_dialog->Hide(); +#else + m_settings_dialog->SetFocus(); + else +#endif + m_settings_dialog->Show(); } - else { - if (m_plater && m_plater->IsShown()) - switch_to(false); - m_tabpanel->SetSelection(tab); + else if (m_layout == slNew) { + m_plater->Show(tab == 0); + m_tabpanel->Show(tab != 0); + + // plater should be focused for correct navigation inside search window + if (tab == 0 && m_plater->canvas3D()->is_search_pressed()) + m_plater->SetFocus(); + Layout(); } + + // when tab == -1, it means we should to show the last selected tab + m_tabpanel->SetSelection(tab == (size_t)(-1) ? m_last_selected_tab : (m_layout == slDlg && tab != 0) ? tab-1 : tab); } // Set a camera direction, zoom to all objects. @@ -1360,5 +1395,79 @@ std::string MainFrame::get_dir_name(const wxString &full_name) const return boost::filesystem::path(full_name.wx_str()).parent_path().string(); } + +// ---------------------------------------------------------------------------- +// SettingsDialog +// ---------------------------------------------------------------------------- + +SettingsDialog::SettingsDialog(MainFrame* mainframe) +: DPIDialog(nullptr, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings")), + m_main_frame(mainframe) +{ + this->SetFont(wxGetApp().normal_font()); + + wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + this->SetBackgroundColour(bgr_clr); + + // Load the icon either from the exe, or from the ico file. +#if _WIN32 + { + TCHAR szExeFileName[MAX_PATH]; + GetModuleFileName(nullptr, szExeFileName, MAX_PATH); + SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); + } +#else + SetIcon(wxIcon(var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); +#endif // _WIN32 + + // wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10 + // with multiple high resolution displays connected. + m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME); +#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList + m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font()); +#endif + + m_tabpanel->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& evt) { + if ((evt.GetModifiers() & wxMOD_CONTROL) != 0) { + switch (evt.GetKeyCode()) { + case '1': { m_main_frame->select_tab(0); break; } + case '2': { m_main_frame->select_tab(1); break; } + case '3': { m_main_frame->select_tab(2); break; } + case '4': { m_main_frame->select_tab(3); break; } + default:break; + } + } + }); + + // initialize layout + auto sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(m_tabpanel, 1, wxEXPAND); + sizer->SetSizeHints(this); + SetSizer(sizer); + Fit(); + + const wxSize min_size = wxSize(85 * em_unit(), 50 * em_unit()); +#ifdef __APPLE__ + // Using SetMinSize() on Mac messes up the window position in some cases + // cf. https://groups.google.com/forum/#!topic/wx-users/yUKPBBfXWO0 + SetSize(min_size); +#else + SetMinSize(min_size); + SetSize(GetMinSize()); +#endif + Layout(); +} + +void SettingsDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + const int& em = em_unit(); + const wxSize& size = wxSize(85 * em, 50 * em); + + SetMinSize(size); + Fit(); + Refresh(); +} + + } // GUI } // Slic3r diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 8fc0ed1f2..219f68319 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -43,6 +43,23 @@ struct PresetTab { PrinterTechnology technology; }; +// ---------------------------------------------------------------------------- +// SettingsDialog +// ---------------------------------------------------------------------------- + +class SettingsDialog : public DPIDialog +{ + wxNotebook* m_tabpanel { nullptr }; + MainFrame* m_main_frame {nullptr }; +public: + SettingsDialog(MainFrame* mainframe); + ~SettingsDialog() {} + wxNotebook* get_tabpanel() { return m_tabpanel; } + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; +}; + class MainFrame : public DPIFrame { bool m_loaded {false}; @@ -57,6 +74,8 @@ class MainFrame : public DPIFrame PrintHostQueueDialog *m_printhost_queue_dlg; + size_t m_last_selected_tab; + std::string get_base_name(const wxString &full_name, const char *extension = nullptr) const; std::string get_dir_name(const wxString &full_name) const; @@ -94,6 +113,12 @@ class MainFrame : public DPIFrame wxFileHistory m_recent_projects; + enum SettingsLayout { + slOld = 0, + slNew, + slDlg, + } m_layout; + protected: virtual void on_dpi_changed(const wxRect &suggested_rect); @@ -109,7 +134,6 @@ public: void update_title(); void init_tabpanel(); - void switch_to(bool plater); void create_preset_tabs(); void add_created_tab(Tab* panel); void init_menubar(); @@ -130,7 +154,9 @@ public: void export_configbundle(); void load_configbundle(wxString file = wxEmptyString); void load_config(const DynamicPrintConfig& config); - void select_tab(size_t tab); + // Select tab in m_tabpanel + // When tab == -1, will be selected last selected tab + void select_tab(size_t tab = size_t(-1)); void select_view(const std::string& direction); // Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig void on_config_changed(DynamicPrintConfig* cfg) const ; @@ -141,6 +167,7 @@ public: Plater* m_plater { nullptr }; wxNotebook* m_tabpanel { nullptr }; + SettingsDialog* m_settings_dialog { nullptr }; wxProgressDialog* m_progress_dialog { nullptr }; std::shared_ptr m_statusbar; diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 0905f4915..d9b9af016 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -1710,10 +1710,11 @@ bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state) { dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); xoffset = icon.GetWidth() + 4; + + if (rect.height==0) + rect.height= icon.GetHeight(); } - if (rect.height==0) - rect.height= icon.GetHeight(); RenderText(m_value.GetText(), xoffset, rect, dc, state); return true; diff --git a/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp similarity index 53% rename from src/slic3r/GUI/GLCanvas3DManager.cpp rename to src/slic3r/GUI/OpenGLManager.cpp index d4522bb07..bdb005b1e 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -1,23 +1,18 @@ #include "libslic3r/libslic3r.h" -#include "GLCanvas3DManager.hpp" -#include "../../slic3r/GUI/GUI.hpp" -#include "../../slic3r/GUI/AppConfig.hpp" -#include "../../slic3r/GUI/GLCanvas3D.hpp" +#include "OpenGLManager.hpp" + +#include "GUI.hpp" +#include "I18N.hpp" +#include "3DScene.hpp" #include #include #include -#if ENABLE_NON_STATIC_CANVAS_MANAGER #include -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER -#include -#include -#include -#include -#include -#include +#include +#include #if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 #ifdef __APPLE__ @@ -33,20 +28,7 @@ namespace Slic3r { namespace GUI { -#if !ENABLE_NON_STATIC_CANVAS_MANAGER -GLCanvas3DManager::GLInfo::GLInfo() - : m_detected(false) - , m_version("") - , m_glsl_version("") - , m_vendor("") - , m_renderer("") - , m_max_tex_size(0) - , m_max_anisotropy(0.0f) -{ -} -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - -const std::string& GLCanvas3DManager::GLInfo::get_version() const +const std::string& OpenGLManager::GLInfo::get_version() const { if (!m_detected) detect(); @@ -54,7 +36,7 @@ const std::string& GLCanvas3DManager::GLInfo::get_version() const return m_version; } -const std::string& GLCanvas3DManager::GLInfo::get_glsl_version() const +const std::string& OpenGLManager::GLInfo::get_glsl_version() const { if (!m_detected) detect(); @@ -62,7 +44,7 @@ const std::string& GLCanvas3DManager::GLInfo::get_glsl_version() const return m_glsl_version; } -const std::string& GLCanvas3DManager::GLInfo::get_vendor() const +const std::string& OpenGLManager::GLInfo::get_vendor() const { if (!m_detected) detect(); @@ -70,7 +52,7 @@ const std::string& GLCanvas3DManager::GLInfo::get_vendor() const return m_vendor; } -const std::string& GLCanvas3DManager::GLInfo::get_renderer() const +const std::string& OpenGLManager::GLInfo::get_renderer() const { if (!m_detected) detect(); @@ -78,7 +60,7 @@ const std::string& GLCanvas3DManager::GLInfo::get_renderer() const return m_renderer; } -int GLCanvas3DManager::GLInfo::get_max_tex_size() const +int OpenGLManager::GLInfo::get_max_tex_size() const { if (!m_detected) detect(); @@ -93,7 +75,7 @@ int GLCanvas3DManager::GLInfo::get_max_tex_size() const #endif // __APPLE__ } -float GLCanvas3DManager::GLInfo::get_max_anisotropy() const +float OpenGLManager::GLInfo::get_max_anisotropy() const { if (!m_detected) detect(); @@ -101,7 +83,7 @@ float GLCanvas3DManager::GLInfo::get_max_anisotropy() const return m_max_anisotropy; } -void GLCanvas3DManager::GLInfo::detect() const +void OpenGLManager::GLInfo::detect() const { const char* data = (const char*)::glGetString(GL_VERSION); if (data != nullptr) @@ -132,7 +114,7 @@ void GLCanvas3DManager::GLInfo::detect() const m_detected = true; } -bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const +bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { if (!m_detected) detect(); @@ -163,7 +145,7 @@ bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int majo return gl_minor >= minor; } -std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool extensions) const +std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extensions) const { if (!m_detected) detect(); @@ -203,34 +185,20 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten return out.str(); } -GLCanvas3DManager::GLInfo GLCanvas3DManager::s_gl_info; -bool GLCanvas3DManager::s_compressed_textures_supported = false; -#if ENABLE_NON_STATIC_CANVAS_MANAGER -GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::EMultisampleState::Unknown; -GLCanvas3DManager::EFramebufferType GLCanvas3DManager::s_framebuffers_type = GLCanvas3DManager::EFramebufferType::Unknown; -#else -GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown; -GLCanvas3DManager::EFramebufferType GLCanvas3DManager::s_framebuffers_type = GLCanvas3DManager::FB_None; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER +OpenGLManager::GLInfo OpenGLManager::s_gl_info; +bool OpenGLManager::s_compressed_textures_supported = false; +OpenGLManager::EMultisampleState OpenGLManager::s_multisample = OpenGLManager::EMultisampleState::Unknown; +OpenGLManager::EFramebufferType OpenGLManager::s_framebuffers_type = OpenGLManager::EFramebufferType::Unknown; #if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 #ifdef __APPLE__ // Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets -GLCanvas3DManager::OSInfo GLCanvas3DManager::s_os_info; +OpenGLManager::OSInfo OpenGLManager::s_os_info; #endif // __APPLE__ #endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5 -#if !ENABLE_NON_STATIC_CANVAS_MANAGER -GLCanvas3DManager::GLCanvas3DManager() - : m_context(nullptr) - , m_gl_initialized(false) +OpenGLManager::~OpenGLManager() { -} -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - -GLCanvas3DManager::~GLCanvas3DManager() -{ -#if ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 #ifdef __APPLE__ // This is an ugly hack needed to solve the crash happening when closing the application on OSX 10.9.5 with newer wxWidgets @@ -248,116 +216,29 @@ GLCanvas3DManager::~GLCanvas3DManager() } #endif //__APPLE__ #endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5 -#else - this->destroy(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER } -#if !ENABLE_NON_STATIC_CANVAS_MANAGER -bool GLCanvas3DManager::add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar) -{ - if (canvas == nullptr) - return false; - - if (do_get_canvas(canvas) != m_canvases.end()) - return false; - - GLCanvas3D* canvas3D = new GLCanvas3D(canvas, bed, camera, view_toolbar); - if (canvas3D == nullptr) - return false; - - canvas3D->bind_event_handlers(); - - if (m_context == nullptr) - { - m_context = new wxGLContext(canvas); - if (m_context == nullptr) - return false; - -#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 -#ifdef __APPLE__ - // Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets - s_os_info.major = wxPlatformInfo::Get().GetOSMajorVersion(); - s_os_info.minor = wxPlatformInfo::Get().GetOSMinorVersion(); - s_os_info.micro = wxPlatformInfo::Get().GetOSMicroVersion(); -#endif //__APPLE__ -#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5 - } - - canvas3D->set_context(m_context); - - m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); - - return true; -} - -bool GLCanvas3DManager::remove(wxGLCanvas* canvas) -{ - CanvasesMap::iterator it = do_get_canvas(canvas); - if (it == m_canvases.end()) - return false; - - it->second->unbind_event_handlers(); - delete it->second; - m_canvases.erase(it); - - return true; -} - -void GLCanvas3DManager::remove_all() -{ - for (CanvasesMap::value_type& item : m_canvases) - { - item.second->unbind_event_handlers(); - delete item.second; - } - m_canvases.clear(); -} - -size_t GLCanvas3DManager::count() const -{ - return m_canvases.size(); -} -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - -#if ENABLE_NON_STATIC_CANVAS_MANAGER -bool GLCanvas3DManager::init_gl() -#else -void GLCanvas3DManager::init_gl() -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER +bool OpenGLManager::init_gl() { if (!m_gl_initialized) { -#if ENABLE_NON_STATIC_CANVAS_MANAGER if (glewInit() != GLEW_OK) { BOOST_LOG_TRIVIAL(error) << "Unable to init glew library"; return false; } -#else - glewInit(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER m_gl_initialized = true; if (GLEW_EXT_texture_compression_s3tc) s_compressed_textures_supported = true; else s_compressed_textures_supported = false; -#if ENABLE_NON_STATIC_CANVAS_MANAGER if (GLEW_ARB_framebuffer_object) s_framebuffers_type = EFramebufferType::Arb; else if (GLEW_EXT_framebuffer_object) s_framebuffers_type = EFramebufferType::Ext; else s_framebuffers_type = EFramebufferType::Unknown; -#else - if (GLEW_ARB_framebuffer_object) - s_framebuffers_type = FB_Arb; - else if (GLEW_EXT_framebuffer_object) - s_framebuffers_type = FB_Ext; - else - s_framebuffers_type = FB_None; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER if (! s_gl_info.is_version_greater_or_equal_to(2, 0)) { // Complain about the OpenGL version. @@ -365,22 +246,19 @@ void GLCanvas3DManager::init_gl() _utf8(L("PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n" "while OpenGL version %s, render %s, vendor %s was detected."))) % s_gl_info.get_version() % s_gl_info.get_renderer() % s_gl_info.get_vendor()).str()); message += "\n"; - message += _(L("You may need to update your graphics card driver.")); + message += _L("You may need to update your graphics card driver."); #ifdef _WIN32 message += "\n"; - message += _(L("As a workaround, you may run PrusaSlicer with a software rendered 3D graphics by running prusa-slicer.exe with the --sw_renderer parameter.")); + message += _L("As a workaround, you may run PrusaSlicer with a software rendered 3D graphics by running prusa-slicer.exe with the --sw_renderer parameter."); #endif - wxMessageBox(message, wxString("PrusaSlicer - ") + _(L("Unsupported OpenGL version")), wxOK | wxICON_ERROR); + wxMessageBox(message, wxString("PrusaSlicer - ") + _L("Unsupported OpenGL version"), wxOK | wxICON_ERROR); } } -#if ENABLE_NON_STATIC_CANVAS_MANAGER return true; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER } -#if ENABLE_NON_STATIC_CANVAS_MANAGER -wxGLContext* GLCanvas3DManager::init_glcontext(wxGLCanvas& canvas) +wxGLContext* OpenGLManager::init_glcontext(wxGLCanvas& canvas) { if (m_context == nullptr) { @@ -397,48 +275,8 @@ wxGLContext* GLCanvas3DManager::init_glcontext(wxGLCanvas& canvas) } return m_context; } -#else -bool GLCanvas3DManager::init(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = do_get_canvas(canvas); - if (it != m_canvases.end()) - return (it->second != nullptr) ? init(*it->second) : false; - else - return false; -} -void GLCanvas3DManager::destroy() -{ - if (m_context != nullptr) - { -#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 -#ifdef __APPLE__ - // this is an ugly hack needed to solve the crash happening when closing the application on OSX 10.9.5 - // the crash is inside wxGLContext destructor - if (s_os_info.major == 10 && s_os_info.minor == 9 && s_os_info.micro == 5) - return; -#endif //__APPLE__ -#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5 - - delete m_context; - m_context = nullptr; - } -} -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - -#if !ENABLE_NON_STATIC_CANVAS_MANAGER -GLCanvas3D* GLCanvas3DManager::get_canvas(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = do_get_canvas(canvas); - return (it != m_canvases.end()) ? it->second : nullptr; -} -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - -#if ENABLE_NON_STATIC_CANVAS_MANAGER -wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow& parent) -#else -wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent) -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER +wxGLCanvas* OpenGLManager::create_wxglcanvas(wxWindow& parent) { int attribList[] = { WX_GL_RGBA, @@ -456,11 +294,7 @@ wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent) 0 }; -#if ENABLE_NON_STATIC_CANVAS_MANAGER if (s_multisample == EMultisampleState::Unknown) -#else - if (s_multisample == MS_Unknown) -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER { detect_multisample(attribList); // // debug output @@ -470,42 +304,14 @@ wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent) if (! can_multisample()) attribList[12] = 0; -#if ENABLE_NON_STATIC_CANVAS_MANAGER return new wxGLCanvas(&parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS); -#else - return new wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER } -#if !ENABLE_NON_STATIC_CANVAS_MANAGER -GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::do_get_canvas(wxGLCanvas* canvas) -{ - return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); -} - -GLCanvas3DManager::CanvasesMap::const_iterator GLCanvas3DManager::do_get_canvas(wxGLCanvas* canvas) const -{ - return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); -} - -bool GLCanvas3DManager::init(GLCanvas3D& canvas) -{ - if (!m_gl_initialized) - init_gl(); - - return canvas.init(); -} -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER - -void GLCanvas3DManager::detect_multisample(int* attribList) +void OpenGLManager::detect_multisample(int* attribList) { int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER; bool enable_multisample = wxVersion >= 30003; -#if ENABLE_NON_STATIC_CANVAS_MANAGER s_multisample = (enable_multisample && wxGLCanvas::IsDisplaySupported(attribList)) ? EMultisampleState::Enabled : EMultisampleState::Disabled; -#else - s_multisample = (enable_multisample && wxGLCanvas::IsDisplaySupported(attribList)) ? MS_Enabled : MS_Disabled; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER // Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows // s_multisample = enable_multisample && wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample"); } diff --git a/src/slic3r/GUI/OpenGLManager.hpp b/src/slic3r/GUI/OpenGLManager.hpp new file mode 100644 index 000000000..9d7ee5bab --- /dev/null +++ b/src/slic3r/GUI/OpenGLManager.hpp @@ -0,0 +1,106 @@ +#ifndef slic3r_OpenGLManager_hpp_ +#define slic3r_OpenGLManager_hpp_ + +class wxWindow; +class wxGLCanvas; +class wxGLContext; + +namespace Slic3r { +namespace GUI { + +class OpenGLManager +{ +public: + enum class EFramebufferType : unsigned char + { + Unknown, + Arb, + Ext + }; + + class GLInfo + { + mutable bool m_detected{ false }; + mutable int m_max_tex_size{ 0 }; + mutable float m_max_anisotropy{ 0.0f }; + + mutable std::string m_version; + mutable std::string m_glsl_version; + mutable std::string m_vendor; + mutable std::string m_renderer; + + public: + GLInfo() = default; + + const std::string& get_version() const; + const std::string& get_glsl_version() const; + const std::string& get_vendor() const; + const std::string& get_renderer() const; + + int get_max_tex_size() const; + float get_max_anisotropy() const; + + bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const; + + std::string to_string(bool format_as_html, bool extensions) const; + + private: + void detect() const; + }; + +#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 +#ifdef __APPLE__ + // Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets + struct OSInfo + { + int major{ 0 }; + int minor{ 0 }; + int micro{ 0 }; + }; +#endif //__APPLE__ +#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5 + +private: + enum class EMultisampleState : unsigned char + { + Unknown, + Enabled, + Disabled + }; + + bool m_gl_initialized{ false }; + wxGLContext* m_context{ nullptr }; + static GLInfo s_gl_info; +#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5 +#ifdef __APPLE__ + // Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets + static OSInfo s_os_info; +#endif //__APPLE__ +#endif // ENABLE_HACK_CLOSING_ON_OSX_10_9_5 + static bool s_compressed_textures_supported; + static EMultisampleState s_multisample; + static EFramebufferType s_framebuffers_type; + +public: + OpenGLManager() = default; + ~OpenGLManager(); + + bool init_gl(); + + wxGLContext* init_glcontext(wxGLCanvas& canvas); + + static bool are_compressed_textures_supported() { return s_compressed_textures_supported; } + static bool can_multisample() { return s_multisample == EMultisampleState::Enabled; } + static bool are_framebuffers_supported() { return (s_framebuffers_type != EFramebufferType::Unknown); } + static EFramebufferType get_framebuffers_type() { return s_framebuffers_type; } + static wxGLCanvas* create_wxglcanvas(wxWindow& parent); + static const GLInfo& get_gl_info() { return s_gl_info; } + +private: + static void detect_multisample(int* attribList); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_OpenGLManager_hpp_ diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 83eb6c76f..5b2b5703b 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -105,6 +105,7 @@ void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& fiel if (!m_show_modified_btns) { field->m_Undo_btn->set_as_hidden(); field->m_Undo_to_sys_btn->set_as_hidden(); + field->m_blinking_bmp->Hide(); return; } diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index 6128eb7ab..09df9ea65 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -178,7 +178,7 @@ public: if (staticbox) { stb = new wxStaticBox(_parent, wxID_ANY, _(title)); if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT); - stb->SetFont(wxGetApp().bold_font()); + stb->SetFont(wxOSX ? wxGetApp().normal_font() : wxGetApp().bold_font()); } else stb = nullptr; sizer = (staticbox ? new wxStaticBoxSizer(stb, wxVERTICAL) : new wxBoxSizer(wxVERTICAL)); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a9e82b8e8..8d505f2b6 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -33,7 +33,9 @@ #include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/AMF.hpp" #include "libslic3r/Format/3mf.hpp" +#if !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/PreviewData.hpp" +#endif // !ENABLE_GCODE_VIEWER #include "libslic3r/GCode/ThumbnailData.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/SLA/Hollowing.hpp" @@ -77,11 +79,9 @@ #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" -#if ENABLE_NON_STATIC_CANVAS_MANAGER #ifdef __APPLE__ #include "Gizmos/GLGizmosManager.hpp" #endif // __APPLE__ -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #include // Needs to be last because reasons :-/ #include "WipeTowerDialog.hpp" @@ -356,10 +356,10 @@ PresetBitmapComboBox(parent, wxSize(15 * wxGetApp().em_unit(), -1)), if (page_id == wxNOT_FOUND) return; - wxGetApp().tab_panel()->ChangeSelection(page_id); + wxGetApp().tab_panel()->SetSelection(page_id); // Switch to Settings NotePad - wxGetApp().mainframe->switch_to(false); + wxGetApp().mainframe->select_tab(); /* In a case of a multi-material printing, for editing another Filament Preset * it's needed to select this preset for the "Filament settings" Tab @@ -1101,9 +1101,8 @@ void Sidebar::jump_to_option(size_t selected) const Search::Option& opt = p->searcher.get_option(selected); wxGetApp().get_tab(opt.type)->activate_option(boost::nowide::narrow(opt.opt_key), boost::nowide::narrow(opt.category)); - // Switch to the Settings NotePad, if plater is shown - if (p->plater->IsShown()) - wxGetApp().mainframe->switch_to(false); + // Switch to the Settings NotePad + wxGetApp().mainframe->select_tab(); } ObjectManipulation* Sidebar::obj_manipul() @@ -1530,9 +1529,10 @@ struct Plater::priv Slic3r::SLAPrint sla_print; Slic3r::Model model; PrinterTechnology printer_technology = ptFFF; - Slic3r::GCodePreviewData gcode_preview_data; #if ENABLE_GCODE_VIEWER Slic3r::GCodeProcessor::Result gcode_result; +#else + Slic3r::GCodePreviewData gcode_preview_data; #endif // ENABLE_GCODE_VIEWER // GUI elements @@ -1635,10 +1635,8 @@ struct Plater::priv void set_current_canvas_as_dirty(); GLCanvas3D* get_current_canvas3D(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER void unbind_canvas_event_handlers(); void reset_canvas_volumes(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER bool init_view_toolbar(); #if ENABLE_GCODE_VIEWER @@ -1647,6 +1645,7 @@ struct Plater::priv void reset_all_gizmos(); void update_ui_from_settings(); + void update_main_toolbar_tooltips(); std::shared_ptr statusbar(); std::string get_config(const std::string &key) const; BoundingBoxf bed_shape_bb() const; @@ -1844,9 +1843,10 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) background_process.set_fff_print(&fff_print); background_process.set_sla_print(&sla_print); - background_process.set_gcode_preview_data(&gcode_preview_data); #if ENABLE_GCODE_VIEWER background_process.set_gcode_result(&gcode_result); +#else + background_process.set_gcode_preview_data(&gcode_preview_data); #endif // ENABLE_GCODE_VIEWER background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) { @@ -1870,10 +1870,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) sla_print.set_status_callback(statuscb); this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this); -#if ENABLE_NON_STATIC_CANVAS_MANAGER view3D = new View3D(q, &model, config, &background_process); #if ENABLE_GCODE_VIEWER - preview = new Preview(q, &model, config, &background_process, &gcode_preview_data, &gcode_result, [this]() { schedule_background_process(); }); + preview = new Preview(q, &model, config, &background_process, &gcode_result, [this]() { schedule_background_process(); }); #else preview = new Preview(q, &model, config, &background_process, &gcode_preview_data, [this]() { schedule_background_process(); }); #endif // ENABLE_GCODE_VIEWER @@ -1882,14 +1881,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // set default view_toolbar icons size equal to GLGizmosManager::Default_Icons_Size view_toolbar.set_icons_size(GLGizmosManager::Default_Icons_Size); #endif // __APPLE__ -#else - view3D = new View3D(q, bed, camera, view_toolbar, &model, config, &background_process); -#if ENABLE_GCODE_VIEWER - preview = new Preview(q, bed, camera, view_toolbar, &model, config, &background_process, &gcode_preview_data, &gcode_result, [this]() { schedule_background_process(); }); -#else - preview = new Preview(q, bed, camera, view_toolbar, &model, config, &background_process, &gcode_preview_data, [this]() { schedule_background_process(); }); -#endif // ENABLE_GCODE_VIEWER -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER panels.push_back(view3D); panels.push_back(preview); @@ -1990,10 +1981,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // Drop target: q->SetDropTarget(new PlaterDropTarget(q)); // if my understanding is right, wxWindow takes the owenership - -#if !ENABLE_NON_STATIC_CANVAS_MANAGER - update_ui_from_settings(); -#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER q->Layout(); set_current_panel(view3D); @@ -2059,7 +2046,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // collapse sidebar according to saved value - sidebar->collapse(wxGetApp().app_config->get("collapsed_sidebar") == "1"); + bool is_collapsed = wxGetApp().app_config->get("collapsed_sidebar") == "1"; + sidebar->collapse(is_collapsed); + // Update an enable of the collapse_toolbar: if sidebar is collapsed, then collapse_toolbar should be visible + if (is_collapsed) + wxGetApp().app_config->set("show_collapse_button", "1"); } Plater::priv::~priv() @@ -2134,6 +2125,13 @@ void Plater::priv::update_ui_from_settings() preview->get_canvas3d()->update_ui_from_settings(); } +// Called after the print technology was changed. +// Update the tooltips for "Switch to Settings" button in maintoolbar +void Plater::priv::update_main_toolbar_tooltips() +{ + view3D->get_canvas3d()->update_tooltip_for_settings_item_in_main_toolbar(); +} + std::shared_ptr Plater::priv::statusbar() { return main_frame->m_statusbar; @@ -3801,7 +3799,6 @@ GLCanvas3D* Plater::priv::get_current_canvas3D() return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr); } -#if ENABLE_NON_STATIC_CANVAS_MANAGER void Plater::priv::unbind_canvas_event_handlers() { if (view3D != nullptr) @@ -3819,7 +3816,6 @@ void Plater::priv::reset_canvas_volumes() if (preview != nullptr) preview->get_canvas3d()->reset_volumes(); } -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER bool Plater::priv::init_view_toolbar() { @@ -4478,8 +4474,6 @@ void Plater::increase_instances(size_t num) // p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec)); } - sidebar().obj_list()->increase_object_instances(obj_idx, was_one_instance ? num + 1 : num); - if (p->get_config("autocenter") == "1") arrange(); @@ -4487,8 +4481,9 @@ void Plater::increase_instances(size_t num) p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1); - p->selection_changed(); + sidebar().obj_list()->increase_object_instances(obj_idx, was_one_instance ? num + 1 : num); + p->selection_changed(); this->p->schedule_background_process(); } @@ -5270,7 +5265,6 @@ void Plater::set_current_canvas_as_dirty() p->set_current_canvas_as_dirty(); } -#if ENABLE_NON_STATIC_CANVAS_MANAGER void Plater::unbind_canvas_event_handlers() { p->unbind_canvas_event_handlers(); @@ -5280,7 +5274,6 @@ void Plater::reset_canvas_volumes() { p->reset_canvas_volumes(); } -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER PrinterTechnology Plater::printer_technology() const { @@ -5303,6 +5296,10 @@ void Plater::set_printer_technology(PrinterTechnology printer_technology) if (wxGetApp().mainframe) wxGetApp().mainframe->update_menubar(); + + p->update_main_toolbar_tooltips(); + + p->sidebar->get_searcher().set_printer_technology(printer_technology); } void Plater::changed_object(int obj_idx) @@ -5391,6 +5388,9 @@ void Plater::paste_from_clipboard() void Plater::search(bool plater_is_active) { if (plater_is_active) { + // plater should be focused for correct navigation inside search window + this->SetFocus(); + wxKeyEvent evt; #ifdef __APPLE__ evt.m_keyCode = 'f'; @@ -5438,7 +5438,6 @@ Camera& Plater::get_camera() return p->camera; } -#if ENABLE_NON_STATIC_CANVAS_MANAGER const Bed3D& Plater::get_bed() const { return p->bed; @@ -5458,7 +5457,6 @@ GLToolbar& Plater::get_view_toolbar() { return p->view_toolbar; } -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_GCODE_VIEWER void Plater::update_preview_bottom_toolbar() diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 7e0283935..7a08c04ef 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -45,10 +45,8 @@ class ObjectList; class GLCanvas3D; class Mouse3DController; struct Camera; -#if ENABLE_NON_STATIC_CANVAS_MANAGER class Bed3D; class GLToolbar; -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER using t_optgroups = std::vector >; @@ -280,10 +278,8 @@ public: void find_new_position(const ModelInstancePtrs &instances, coord_t min_d); void set_current_canvas_as_dirty(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER void unbind_canvas_event_handlers(); void reset_canvas_volumes(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER PrinterTechnology printer_technology() const; const DynamicPrintConfig * config() const; @@ -316,13 +312,11 @@ public: const Camera& get_camera() const; Camera& get_camera(); -#if ENABLE_NON_STATIC_CANVAS_MANAGER const Bed3D& get_bed() const; Bed3D& get_bed(); const GLToolbar& get_view_toolbar() const; GLToolbar& get_view_toolbar(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #if ENABLE_GCODE_VIEWER void update_preview_bottom_toolbar(); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index e2bccb1c7..9e462d4bf 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -147,6 +147,13 @@ void PreferencesDialog::build() } }; + def.label = L("Show the button for the collapse sidebar"); + def.type = coBool; + def.tooltip = L("If enabled, the button for the collapse sidebar will be appeared in top right corner of the 3D Scene"); + def.set_default_value(new ConfigOptionBool{ app_config->get("show_collapse_button") == "1" }); + option = Option(def, "show_collapse_button"); + m_optgroup_gui->append_single_option_line(option); + def.label = L("Use custom size for toolbar icons"); def.type = coBool; def.tooltip = L("If enabled, you can change size of toolbar icons manually."); @@ -157,6 +164,8 @@ void PreferencesDialog::build() create_icon_size_slider(); m_icon_size_sizer->ShowItems(app_config->get("use_custom_toolbar_size") == "1"); + create_settings_mode_widget(); + auto sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(m_optgroup_general->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); sizer->Add(m_optgroup_camera->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); @@ -167,7 +176,7 @@ void PreferencesDialog::build() auto buttons = CreateStdDialogButtonSizer(wxOK | wxCANCEL); wxButton* btn = static_cast(FindWindowById(wxID_OK, this)); btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); }); - sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); + sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 5); SetSizer(sizer); sizer->SetSizeHints(this); @@ -179,17 +188,53 @@ void PreferencesDialog::accept() warning_catcher(this, wxString::Format(_(L("You need to restart %s to make the changes effective.")), SLIC3R_APP_NAME)); } - auto app_config = get_app_config(); - for (std::map::iterator it = m_values.begin(); it != m_values.end(); ++it) { - app_config->set(it->first, it->second); + auto app_config = get_app_config(); + + m_settings_layout_changed = false; + for (const std::string& key : {"old_settings_layout_mode", + "new_settings_layout_mode", + "dlg_settings_layout_mode" }) + { + auto it = m_values.find(key); + if (it != m_values.end() && app_config->get(key) != it->second) { + m_settings_layout_changed = true; + break; + } } - app_config->save(); + if (m_settings_layout_changed) { + // the dialog needs to be destroyed before the call to recreate_gui() + // or sometimes the application crashes into wxDialogBase() destructor + // so we put it into an inner scope + wxMessageDialog dialog(nullptr, + _L("Switching the settings layout mode will trigger application restart.\n" + "You will lose content of the plater.") + "\n\n" + + _L("Do you want to proceed?"), + wxString(SLIC3R_APP_NAME) + " - " + _L("Switching the settings layout mode"), + wxICON_QUESTION | wxOK | wxCANCEL); + if (dialog.ShowModal() == wxID_CANCEL) + { + int selection = app_config->get("old_settings_layout_mode") == "1" ? 0 : + app_config->get("new_settings_layout_mode") == "1" ? 1 : + app_config->get("dlg_settings_layout_mode") == "1" ? 2 : 0; + + m_layout_mode_box->SetSelection(selection); + return; + } + } + + for (std::map::iterator it = m_values.begin(); it != m_values.end(); ++it) + app_config->set(it->first, it->second); + + app_config->save(); EndModal(wxID_OK); - // Nothify the UI to update itself from the ini file. - wxGetApp().update_ui_from_settings(); + if (m_settings_layout_changed) + ;// application will be recreated after Preference dialog will be destroyed + else + // Nothify the UI to update itself from the ini file. + wxGetApp().update_ui_from_settings(); } void PreferencesDialog::on_dpi_changed(const wxRect &suggested_rect) @@ -272,6 +317,38 @@ void PreferencesDialog::create_icon_size_slider() m_optgroup_gui->sizer->Add(m_icon_size_sizer, 0, wxEXPAND | wxALL, em); } +void PreferencesDialog::create_settings_mode_widget() +{ + wxString choices[] = { _L("Old regular layout with tab bar"), + _L("New layout without the tab bar on the platter"), + _L("Settings will be shown in non-modal dialog") }; + + auto app_config = get_app_config(); + int selection = app_config->get("old_settings_layout_mode") == "1" ? 0 : + app_config->get("new_settings_layout_mode") == "1" ? 1 : + app_config->get("dlg_settings_layout_mode") == "1" ? 2 : 0; + + wxWindow* parent = m_optgroup_gui->ctrl_parent(); + + m_layout_mode_box = new wxRadioBox(parent, wxID_ANY, _L("Settings layout mode"), wxDefaultPosition, wxDefaultSize, WXSIZEOF(choices), choices, + 3, wxRA_SPECIFY_ROWS); + m_layout_mode_box->SetFont(wxGetApp().normal_font()); + m_layout_mode_box->SetSelection(selection); + + m_layout_mode_box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) { + int selection = e.GetSelection(); + + m_values["old_settings_layout_mode"] = boost::any_cast(selection == 0) ? "1" : "0"; + m_values["new_settings_layout_mode"] = boost::any_cast(selection == 1) ? "1" : "0"; + m_values["dlg_settings_layout_mode"] = boost::any_cast(selection == 2) ? "1" : "0"; + }); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(m_layout_mode_box, 1, wxALIGN_CENTER_VERTICAL); + + m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND); +} + } // GUI } // Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 35bf2b8c5..d90f01e2b 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -7,6 +7,8 @@ #include #include +class wxRadioBox; + namespace Slic3r { namespace GUI { @@ -19,11 +21,15 @@ class PreferencesDialog : public DPIDialog std::shared_ptr m_optgroup_camera; std::shared_ptr m_optgroup_gui; wxSizer* m_icon_size_sizer; + wxRadioBox* m_layout_mode_box; bool isOSX {false}; + bool m_settings_layout_changed {false}; public: PreferencesDialog(wxWindow* parent); ~PreferencesDialog() {} + bool settings_layout_changed() { return m_settings_layout_changed; } + void build(); void accept(); @@ -31,6 +37,7 @@ protected: void on_dpi_changed(const wxRect &suggested_rect) override; void layout(); void create_icon_size_slider(); + void create_settings_mode_widget(); }; } // GUI diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index 9c2cebd50..dc0078091 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -125,6 +125,7 @@ public: TYPE_FILAMENT, TYPE_SLA_MATERIAL, TYPE_PRINTER, + TYPE_COUNT, }; Preset(Type type, const std::string &name, bool is_default = false) : type(type), is_default(is_default), name(name) {} diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index c6894bc5b..b929b37d0 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -25,41 +25,35 @@ using GUI::into_u8; namespace Search { -static std::map NameByType = { - { Preset::TYPE_PRINT, L("Print") }, - { Preset::TYPE_FILAMENT, L("Filament") }, - { Preset::TYPE_SLA_MATERIAL, L("Material") }, - { Preset::TYPE_SLA_PRINT, L("Print") }, - { Preset::TYPE_PRINTER, L("Printer") } -}; - -FMFlag Option::fuzzy_match(wchar_t const* search_pattern, int& outScore, std::vector &out_matches) const +static const std::vector& NameByType() { - FMFlag flag = fmUndef; - int score; + static std::vector data; + if (data.empty()) { + data.assign(Preset::TYPE_COUNT, std::wstring()); + data[Preset::TYPE_PRINT ] = _L("Print" ).ToStdWstring(); + data[Preset::TYPE_FILAMENT ] = _L("Filament" ).ToStdWstring(); + data[Preset::TYPE_SLA_MATERIAL ] = _L("Material" ).ToStdWstring(); + data[Preset::TYPE_SLA_PRINT ] = _L("Print" ).ToStdWstring(); + data[Preset::TYPE_PRINTER ] = _L("Printer" ).ToStdWstring(); + }; + return data; +} - uint16_t matches[fts::max_matches + 1]; // +1 for the stopper - auto save_matches = [&matches, &out_matches]() { - size_t cnt = 0; - for (; matches[cnt] != fts::stopper; ++cnt); - out_matches.assign(matches, matches + cnt); - }; - if (fts::fuzzy_match(search_pattern, label_local.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmLabelLocal ; save_matches(); } - if (fts::fuzzy_match(search_pattern, group_local.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmGroupLocal ; save_matches(); } - if (fts::fuzzy_match(search_pattern, category_local.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmCategoryLocal; save_matches(); } - if (fts::fuzzy_match(search_pattern, opt_key.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmOptKey ; save_matches(); } - if (fts::fuzzy_match(search_pattern, label.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmLabel ; save_matches(); } - if (fts::fuzzy_match(search_pattern, group.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmGroup ; save_matches(); } - if (fts::fuzzy_match(search_pattern, category.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmCategory ; save_matches(); } - - return flag; +static char marker_by_type(Preset::Type type, PrinterTechnology pt) +{ + switch(type) { + case Preset::TYPE_PRINT: + case Preset::TYPE_SLA_PRINT: + return ImGui::PrintIconMarker; + case Preset::TYPE_FILAMENT: + return ImGui::FilamentIconMarker; + case Preset::TYPE_SLA_MATERIAL: + return ImGui::MaterialIconMarker; + case Preset::TYPE_PRINTER: + return pt == ptSLA ? ImGui::PrinterSlaIconMarker : ImGui::PrinterIconMarker; + default: + return ' '; + } } void FoundOption::get_marked_label_and_tooltip(const char** label_, const char** tooltip_) const @@ -89,12 +83,16 @@ void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type ty return; wxString suffix; - if (gc.category == "Machine limits") + wxString suffix_local; + if (gc.category == "Machine limits") { suffix = opt_key.back()=='1' ? L("Stealth") : L("Normal"); + suffix_local = " " + _(suffix); + suffix = " " + suffix; + } if (!label.IsEmpty()) options.emplace_back(Option{ boost::nowide::widen(opt_key), type, - (label+ " " + suffix).ToStdWstring(), (_(label)+ " " + _(suffix)).ToStdWstring(), + (label + suffix).ToStdWstring(), (_(label) + suffix_local).ToStdWstring(), gc.group.ToStdWstring(), _(gc.group).ToStdWstring(), gc.category.ToStdWstring(), _(gc.category).ToStdWstring() }); }; @@ -125,17 +123,8 @@ void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type ty emplace(opt_key, label); else for (int i = 0; i < cnt; ++i) - emplace(opt_key + "#" + std::to_string(i), label); - - /*const GroupAndCategory& gc = groups_and_categories[opt_key]; - if (gc.group.IsEmpty() || gc.category.IsEmpty()) - continue; - - if (!label.IsEmpty()) - options.emplace_back(Option{opt_key, type, - label, _(label), - gc.group, _(gc.group), - gc.category, _(gc.category) });*/ + // ! It's very important to use "#". opt_key#n is a real option key used in GroupAndCategory + emplace(opt_key + "#" + std::to_string(i), label); } } @@ -179,6 +168,20 @@ bool OptionsSearcher::search() return search(search_line, true); } +static bool fuzzy_match(const std::wstring &search_pattern, const std::wstring &label, int& out_score, std::vector &out_matches) +{ + uint16_t matches[fts::max_matches + 1]; // +1 for the stopper + int score; + if (fts::fuzzy_match(search_pattern.c_str(), label.c_str(), score, matches)) { + size_t cnt = 0; + for (; matches[cnt] != fts::stopper; ++cnt); + out_matches.assign(matches, matches + cnt); + out_score = score; + return true; + } else + return false; +} + bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) { if (search_line == search && !force) @@ -187,78 +190,97 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) found.clear(); bool full_list = search.empty(); - wxString sep = " : "; + std::wstring sep = L" : "; - auto get_label = [this, sep](const Option& opt) + auto get_label = [this, &sep](const Option& opt) { - wxString label; - if (view_params.type) - label += _(NameByType[opt.type]) + sep; - if (view_params.category) - label += opt.category_local + sep; - if (view_params.group) - label += opt.group_local + sep; - label += opt.label_local; - return label; + std::wstring out; + out += marker_by_type(opt.type, printer_technology); + const std::wstring *prev = nullptr; + for (const std::wstring * const s : { + view_params.category ? &opt.category_local : nullptr, + view_params.group ? &opt.group_local : nullptr, + &opt.label_local }) + if (s != nullptr && (prev == nullptr || *prev != *s)) { +// if (! out.empty()) + if (out.size()>2) + out += sep; + out += *s; + prev = s; + } + return out; }; - auto get_tooltip = [this, sep](const Option& opt) + auto get_label_english = [this, &sep](const Option& opt) { - return _(NameByType[opt.type]) + sep + + std::wstring out; + out += marker_by_type(opt.type, printer_technology); + const std::wstring*prev = nullptr; + for (const std::wstring * const s : { + view_params.category ? &opt.category : nullptr, + view_params.group ? &opt.group : nullptr, + &opt.label }) + if (s != nullptr && (prev == nullptr || *prev != *s)) { +// if (! out.empty()) + if (out.size()>2) + out += sep; + out += *s; + prev = s; + } + return out; + }; + + auto get_tooltip = [this, &sep](const Option& opt) + { + return marker_by_type(opt.type, printer_technology) + opt.category_local + sep + opt.group_local + sep + opt.label_local; }; - std::vector matches; + std::vector matches, matches2; for (size_t i=0; i < options.size(); i++) { const Option &opt = options[i]; if (full_list) { std::string label = into_u8(get_label(opt)); - found.emplace_back(FoundOption{ label, label, into_u8(get_tooltip(opt)), i, fmUndef, 0 }); + found.emplace_back(FoundOption{ label, label, boost::nowide::narrow(get_tooltip(opt)), i, 0 }); continue; } - int score = 0; - FMFlag fuzzy_match_flag = opt.fuzzy_match(boost::nowide::widen(search).c_str(), score, matches); - if (fuzzy_match_flag != fmUndef) - { - wxString label; - - if (view_params.type) - label += _(NameByType[opt.type]) + sep; - if (fuzzy_match_flag == fmCategoryLocal) - label += mark_string(opt.category_local, matches) + sep; - else if (view_params.category) - label += opt.category_local + sep; - if (fuzzy_match_flag == fmGroupLocal) - label += mark_string(opt.group_local, matches) + sep; - else if (view_params.group) - label += opt.group_local + sep; - label += ((fuzzy_match_flag == fmLabelLocal) ? mark_string(opt.label_local, matches) : opt.label_local) + sep; - - switch (fuzzy_match_flag) { - case fmLabelLocal: - case fmGroupLocal: - case fmCategoryLocal: - break; - case fmLabel: label = get_label(opt) + "(" + mark_string(opt.label, matches) + ")"; break; - case fmGroup: label = get_label(opt) + "(" + mark_string(opt.group, matches) + ")"; break; - case fmCategory: label = get_label(opt) + "(" + mark_string(opt.category, matches) + ")"; break; - case fmOptKey: label = get_label(opt) + "(" + mark_string(opt.opt_key, matches) + ")"; break; - case fmUndef: assert(false); break; - } - - std::string label_plain = into_u8(label); - boost::erase_all(label_plain, std::wstring(1, wchar_t(ImGui::ColorMarkerStart))); - boost::erase_all(label_plain, std::wstring(1, wchar_t(ImGui::ColorMarkerEnd))); - found.emplace_back(FoundOption{ label_plain, into_u8(label), into_u8(get_tooltip(opt)), i, fuzzy_match_flag, score }); + std::wstring wsearch = boost::nowide::widen(search); + boost::trim_left(wsearch); + std::wstring label = get_label(opt); + std::wstring label_english = get_label_english(opt); + int score = std::numeric_limits::min(); + int score2; + matches.clear(); + fuzzy_match(wsearch, label, score, matches); + if (fuzzy_match(wsearch, opt.opt_key, score2, matches2) && score2 > score) { + for (fts::pos_type &pos : matches2) + pos += label.size() + 1; + label += L"(" + opt.opt_key + L")"; + append(matches, matches2); + score = score2; + } + if (view_params.english && fuzzy_match(wsearch, label_english, score2, matches2) && score2 > score) { + label = std::move(label_english); + matches = std::move(matches2); + score = score2; + } + if (score > std::numeric_limits::min()) { + label = mark_string(label, matches); + label += L" [" + std::to_wstring(score) + L"]";// add score value + std::string label_u8 = into_u8(label); + std::string label_plain = label_u8; + boost::erase_all(label_plain, std::string(1, char(ImGui::ColorMarkerStart))); + boost::erase_all(label_plain, std::string(1, char(ImGui::ColorMarkerEnd))); + found.emplace_back(FoundOption{ label_plain, label_u8, boost::nowide::narrow(get_tooltip(opt)), i, score }); } } if (!full_list) sort_found(); - + if (search_line != search) search_line = search; @@ -411,15 +433,17 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher) wxBoxSizer* check_sizer = new wxBoxSizer(wxHORIZONTAL); - check_type = new wxCheckBox(this, wxID_ANY, _L("Type")); check_category = new wxCheckBox(this, wxID_ANY, _L("Category")); check_group = new wxCheckBox(this, wxID_ANY, _L("Group")); + if (GUI::wxGetApp().is_localized()) + check_english = new wxCheckBox(this, wxID_ANY, _L("Search in English")); wxStdDialogButtonSizer* cancel_btn = this->CreateStdDialogButtonSizer(wxCANCEL); - check_sizer->Add(check_type, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border); check_sizer->Add(check_category, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border); - check_sizer->Add(check_group, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border); + check_sizer->Add(check_group, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border); + if (GUI::wxGetApp().is_localized()) + check_sizer->Add(check_english, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border); check_sizer->AddStretchSpacer(border); check_sizer->Add(cancel_btn, 0, wxALIGN_CENTER_VERTICAL); @@ -438,7 +462,8 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher) search_list->Bind(wxEVT_LEFT_UP, &SearchDialog::OnMouseClick, this); search_list->Bind(wxEVT_KEY_DOWN,&SearchDialog::OnKeyDown, this); - check_type ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this); + if (GUI::wxGetApp().is_localized()) + check_english ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this); check_category->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this); check_group ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this); @@ -458,9 +483,9 @@ void SearchDialog::Popup(wxPoint position /*= wxDefaultPosition*/) update_list(); const OptionViewParameters& params = searcher->view_params; - check_type->SetValue(params.type); check_category->SetValue(params.category); check_group->SetValue(params.group); + check_english->SetValue(params.english); this->SetPosition(position); this->ShowModal(); @@ -526,7 +551,7 @@ void SearchDialog::update_list() const std::vector& filters = searcher->found_options(); for (const FoundOption& item : filters) - search_list->Append(from_u8(item.label)); + search_list->Append(from_u8(item.label).Remove(0, 1)); } void SearchDialog::OnKeyDown(wxKeyEvent& event) @@ -558,7 +583,7 @@ void SearchDialog::OnKeyDown(wxKeyEvent& event) void SearchDialog::OnCheck(wxCommandEvent& event) { OptionViewParameters& params = searcher->view_params; - params.type = check_type->GetValue(); + params.english = check_english->GetValue(); params.category = check_category->GetValue(); params.group = check_group->GetValue(); diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index 1c24248ac..291633a2b 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -36,20 +36,6 @@ struct GroupAndCategory { wxString category; }; -// fuzzy_match flag -// Sorted by the order of importance. The outputs will be sorted by the importance if the match value given by fuzzy_match is equal. -enum FMFlag -{ - fmUndef = 0, // didn't find - fmOptKey, - fmLabel, - fmLabelLocal, - fmGroup, - fmGroupLocal, - fmCategory, - fmCategoryLocal -}; - struct Option { bool operator<(const Option& other) const { return other.label > this->label; } bool operator>(const Option& other) const { return other.label < this->label; } @@ -64,8 +50,6 @@ struct Option { std::wstring group_local; std::wstring category; std::wstring category_local; - - FMFlag fuzzy_match(wchar_t const *search_pattern, int &outScore, std::vector &out_matches) const; }; struct FoundOption { @@ -74,7 +58,6 @@ struct FoundOption { std::string marked_label; std::string tooltip; size_t option_idx {0}; - FMFlag category {fmUndef}; int outScore {0}; // Returning pointers to contents of std::string members, to be used by ImGUI for rendering. @@ -83,17 +66,18 @@ struct FoundOption { struct OptionViewParameters { - bool type {false}; bool category {false}; bool group {true }; + bool english {false}; - int hovered_id {-1}; + int hovered_id {0}; }; class OptionsSearcher { std::string search_line; std::map groups_and_categories; + PrinterTechnology printer_technology; std::vector