Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_gcode_viewer
This commit is contained in:
commit
89dafeac95
21 changed files with 686 additions and 167 deletions
3
deps/CMakeLists.txt
vendored
3
deps/CMakeLists.txt
vendored
|
@ -152,6 +152,9 @@ include(MPFR/MPFR.cmake)
|
|||
include(CGAL/CGAL.cmake)
|
||||
include(wxWidgets/wxWidgets.cmake)
|
||||
|
||||
add_dependencies(dep_blosc ${ZLIB_PKG})
|
||||
add_dependencies(dep_openexr ${ZLIB_PKG})
|
||||
|
||||
if (MSVC)
|
||||
|
||||
add_custom_target(deps ALL
|
||||
|
|
6
deps/deps-windows.cmake
vendored
6
deps/deps-windows.cmake
vendored
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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<float>(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<SegmentedIntersectionLine> &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<AntPath> m_matrix;
|
||||
};
|
||||
|
||||
|
@ -1724,7 +1731,98 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
|
|||
return monotonous_regions;
|
||||
}
|
||||
|
||||
static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, std::vector<SegmentedIntersectionLine> &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<SegmentedIntersectionLine> &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<float>(total_length);
|
||||
}
|
||||
|
||||
static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, const ExPolygonWithOffset &poly_with_offset, std::vector<SegmentedIntersectionLine> &segs)
|
||||
{
|
||||
// Map from low intersection to left / right side of a monotonous region.
|
||||
using MapType = std::pair<SegmentIntersection*, MonotonousRegion*>;
|
||||
|
@ -1816,6 +1914,20 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> ®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<MonotonousRegionLink> chain_monotonous_regions(
|
||||
std::vector<MonotonousRegion> ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs, std::mt19937_64 &rng)
|
||||
{
|
||||
|
@ -1940,14 +2053,18 @@ static std::vector<MonotonousRegionLink> 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<int>(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<MonotonousRegionLink> 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<MonotonousRegionLink> 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<MonotonousRegionLink> 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<MonotonousRegionLink> chain_monotonous_regions(
|
|||
for (std::vector<NextCandidate>::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<MonotonousRegionLink> 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<MonotonousRegionLink> 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<MonotonousRegionLink> &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<MonotonousRegionLink> &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<MonotonousRegion> 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<MonotonousRegionLink> 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;
|
||||
|
|
|
@ -119,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
|
||||
|
|
|
@ -218,6 +218,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()
|
||||
|
|
|
@ -4323,6 +4323,9 @@ void GLCanvas3D::update_ui_from_settings()
|
|||
_refresh_if_shown_on_screen();
|
||||
}
|
||||
#endif // ENABLE_RETINA_GL
|
||||
|
||||
bool enable_collapse = wxGetApp().app_config->get("show_collapse_button") == "1";
|
||||
enable_collapse_toolbar(enable_collapse);
|
||||
}
|
||||
|
||||
|
||||
|
@ -4376,6 +4379,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();
|
||||
|
@ -5003,10 +5016,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
|
||||
|
@ -5028,7 +5057,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)
|
||||
{
|
||||
|
@ -5164,7 +5193,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;
|
||||
|
@ -5205,61 +5234,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()
|
||||
|
|
|
@ -739,6 +739,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;
|
||||
|
|
|
@ -247,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)
|
||||
|
|
|
@ -412,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
|
||||
|
@ -601,6 +602,8 @@ void GUI_App::recreate_GUI()
|
|||
|
||||
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);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/event.h>
|
||||
|
@ -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<const char, std::string> 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)
|
||||
|
@ -735,9 +748,9 @@ 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);
|
||||
check_box(_L("Search in English"), view_params.english);
|
||||
}
|
||||
|
||||
void ImGuiWrapper::disabled_begin(bool disabled)
|
||||
|
@ -791,6 +804,59 @@ static const ImWchar ranges_keyboard_shortcuts[] =
|
|||
};
|
||||
#endif // __APPLE__
|
||||
|
||||
|
||||
std::vector<unsigned char> 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<unsigned char> 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<unsigned char> 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 +895,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<unsigned char> 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));
|
||||
|
|
|
@ -96,6 +96,7 @@ private:
|
|||
void render_draw_data(ImDrawData *draw_data);
|
||||
bool display_initialized() const;
|
||||
void destroy_font();
|
||||
std::vector<unsigned char> 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);
|
||||
|
|
|
@ -90,10 +90,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();
|
||||
|
@ -227,6 +229,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();
|
||||
|
@ -284,12 +289,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();
|
||||
|
@ -302,17 +320,22 @@ void MainFrame::init_tabpanel()
|
|||
// On GTK, the wxEVT_NOTEBOOK_PAGE_CHANGED event is triggered
|
||||
// before the MainFrame is fully set up.
|
||||
static_cast<Tab*>(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();
|
||||
|
||||
|
@ -338,13 +361,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();
|
||||
|
@ -460,6 +476,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<const Slic3r::GUI::Plater*>(m_tabpanel->GetPage((size_t)page_id)) != nullptr;
|
||||
}
|
||||
|
@ -758,25 +779,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) {
|
||||
|
@ -1240,17 +1257,28 @@ 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();
|
||||
return;
|
||||
}
|
||||
// Show/Activate Settings Dialog
|
||||
if (m_settings_dialog->IsShown())
|
||||
m_settings_dialog->SetFocus();
|
||||
else
|
||||
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);
|
||||
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.
|
||||
|
@ -1355,5 +1383,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
|
||||
|
|
|
@ -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<ProgressStatusBar> m_statusbar;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
@ -1646,6 +1645,7 @@ struct Plater::priv
|
|||
|
||||
void reset_all_gizmos();
|
||||
void update_ui_from_settings();
|
||||
void update_main_toolbar_tooltips();
|
||||
std::shared_ptr<ProgressStatusBar> statusbar();
|
||||
std::string get_config(const std::string &key) const;
|
||||
BoundingBoxf bed_shape_bb() const;
|
||||
|
@ -2046,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()
|
||||
|
@ -2121,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<ProgressStatusBar> Plater::priv::statusbar()
|
||||
{
|
||||
return main_frame->m_statusbar;
|
||||
|
@ -5286,6 +5297,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)
|
||||
|
|
|
@ -117,6 +117,13 @@ void PreferencesDialog::build()
|
|||
m_optgroup_general->append_single_option_line(option);
|
||||
#endif
|
||||
|
||||
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_general->append_single_option_line(option);
|
||||
|
||||
m_optgroup_camera = std::make_shared<ConfigOptionsGroup>(this, _(L("Camera")));
|
||||
m_optgroup_camera->label_width = 40;
|
||||
m_optgroup_camera->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
|
@ -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<wxButton*>(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,46 @@ 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<std::string, std::string>::iterator it = m_values.begin(); it != m_values.end(); ++it) {
|
||||
app_config->set(it->first, it->second);
|
||||
auto app_config = get_app_config();
|
||||
|
||||
bool settings_layout_changed = m_values.find("old_settings_layout_mode") != m_values.end() ||
|
||||
m_values.find("new_settings_layout_mode") != m_values.end() ||
|
||||
m_values.find("dlg_settings_layout_mode") != m_values.end();
|
||||
|
||||
if (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;
|
||||
}
|
||||
}
|
||||
|
||||
app_config->save();
|
||||
for (std::map<std::string, std::string>::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 (settings_layout_changed)
|
||||
// recreate application, if settings layout was changed
|
||||
wxGetApp().recreate_GUI();
|
||||
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 +310,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<bool>(selection == 0) ? "1" : "0";
|
||||
m_values["new_settings_layout_mode"] = boost::any_cast<bool>(selection == 1) ? "1" : "0";
|
||||
m_values["dlg_settings_layout_mode"] = boost::any_cast<bool>(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
|
|
@ -7,6 +7,8 @@
|
|||
#include <wx/dialog.h>
|
||||
#include <map>
|
||||
|
||||
class wxRadioBox;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
@ -19,6 +21,7 @@ class PreferencesDialog : public DPIDialog
|
|||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_camera;
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_gui;
|
||||
wxSizer* m_icon_size_sizer;
|
||||
wxRadioBox* m_layout_mode_box;
|
||||
bool isOSX {false};
|
||||
public:
|
||||
PreferencesDialog(wxWindow* parent);
|
||||
|
@ -31,6 +34,7 @@ protected:
|
|||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
void layout();
|
||||
void create_icon_size_slider();
|
||||
void create_settings_mode_widget();
|
||||
};
|
||||
|
||||
} // GUI
|
||||
|
|
|
@ -39,6 +39,23 @@ static const std::vector<std::wstring>& NameByType()
|
|||
return data;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
*label_ = marked_label.c_str();
|
||||
|
@ -106,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,19 +191,19 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
|
|||
|
||||
bool full_list = search.empty();
|
||||
std::wstring sep = L" : ";
|
||||
const std::vector<std::wstring>& name_by_type = NameByType();
|
||||
|
||||
auto get_label = [this, &name_by_type, &sep](const Option& opt)
|
||||
auto get_label = [this, &sep](const Option& opt)
|
||||
{
|
||||
std::wstring out;
|
||||
out += marker_by_type(opt.type, printer_technology);
|
||||
const std::wstring *prev = nullptr;
|
||||
for (const std::wstring * const s : {
|
||||
view_params.type ? &(name_by_type[opt.type]) : nullptr,
|
||||
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.empty())
|
||||
if (out.size()>2)
|
||||
out += sep;
|
||||
out += *s;
|
||||
prev = s;
|
||||
|
@ -203,17 +211,18 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
|
|||
return out;
|
||||
};
|
||||
|
||||
auto get_label_english = [this, &name_by_type, &sep](const Option& opt)
|
||||
auto get_label_english = [this, &sep](const Option& opt)
|
||||
{
|
||||
std::wstring out;
|
||||
out += marker_by_type(opt.type, printer_technology);
|
||||
const std::wstring*prev = nullptr;
|
||||
for (const std::wstring * const s : {
|
||||
view_params.type ? &name_by_type[opt.type] : nullptr,
|
||||
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.empty())
|
||||
if (out.size()>2)
|
||||
out += sep;
|
||||
out += *s;
|
||||
prev = s;
|
||||
|
@ -221,9 +230,9 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
|
|||
return out;
|
||||
};
|
||||
|
||||
auto get_tooltip = [this, &name_by_type, &sep](const Option& opt)
|
||||
auto get_tooltip = [this, &sep](const Option& opt)
|
||||
{
|
||||
return name_by_type[opt.type] + sep +
|
||||
return marker_by_type(opt.type, printer_technology) +
|
||||
opt.category_local + sep +
|
||||
opt.group_local + sep + opt.label_local;
|
||||
};
|
||||
|
@ -423,15 +432,15 @@ 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"));
|
||||
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_english, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border);
|
||||
check_sizer->AddStretchSpacer(border);
|
||||
check_sizer->Add(cancel_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
|
@ -450,7 +459,7 @@ 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);
|
||||
check_english ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this);
|
||||
check_category->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this);
|
||||
check_group ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this);
|
||||
|
||||
|
@ -470,9 +479,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();
|
||||
|
@ -538,7 +547,7 @@ void SearchDialog::update_list()
|
|||
|
||||
const std::vector<FoundOption>& 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)
|
||||
|
@ -570,7 +579,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();
|
||||
|
||||
|
|
|
@ -66,9 +66,9 @@ struct FoundOption {
|
|||
|
||||
struct OptionViewParameters
|
||||
{
|
||||
bool type {false};
|
||||
bool category {false};
|
||||
bool group {true };
|
||||
bool english {false};
|
||||
|
||||
int hovered_id {-1};
|
||||
};
|
||||
|
@ -77,6 +77,7 @@ class OptionsSearcher
|
|||
{
|
||||
std::string search_line;
|
||||
std::map<std::string, GroupAndCategory> groups_and_categories;
|
||||
PrinterTechnology printer_technology;
|
||||
|
||||
std::vector<Option> options {};
|
||||
std::vector<FoundOption> found {};
|
||||
|
@ -120,6 +121,8 @@ public:
|
|||
const std::vector<FoundOption>& found_options() { return found; }
|
||||
const GroupAndCategory& get_group_and_category (const std::string& opt_key) { return groups_and_categories[opt_key]; }
|
||||
std::string& search_string() { return search_line; }
|
||||
|
||||
void set_printer_technology(PrinterTechnology pt) { printer_technology = pt; }
|
||||
};
|
||||
|
||||
|
||||
|
@ -167,9 +170,9 @@ class SearchDialog : public GUI::DPIDialog
|
|||
|
||||
wxTextCtrl* search_line { nullptr };
|
||||
wxListBox* search_list { nullptr };
|
||||
wxCheckBox* check_type { nullptr };
|
||||
wxCheckBox* check_category { nullptr };
|
||||
wxCheckBox* check_group { nullptr };
|
||||
wxCheckBox* check_english { nullptr };
|
||||
|
||||
OptionsSearcher* searcher;
|
||||
|
||||
|
|
Loading…
Reference in a new issue