mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-26 18:21:18 -06:00
Merge branch 'master' into fs_removes_SimplifyMesh
This commit is contained in:
commit
3c61d2b352
189 changed files with 34880 additions and 2818 deletions
|
|
@ -154,7 +154,8 @@ namespace ImGui
|
|||
const wchar_t DocumentationHoverButton = 0x2601;
|
||||
const wchar_t ClippyMarker = 0x2602;
|
||||
const wchar_t InfoMarker = 0x2603;
|
||||
|
||||
const wchar_t SliderFloatEditBtnIcon = 0x2604;
|
||||
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,8 +71,6 @@ void AppConfig::set_defaults()
|
|||
if (get("drop_project_action").empty())
|
||||
set("drop_project_action", "1");
|
||||
|
||||
if (get("version_check").empty())
|
||||
set("version_check", "1");
|
||||
if (get("preset_update").empty())
|
||||
set("preset_update", "1");
|
||||
|
||||
|
|
@ -182,6 +180,9 @@ void AppConfig::set_defaults()
|
|||
|
||||
if (get("dark_color_mode").empty())
|
||||
set("dark_color_mode", "0");
|
||||
|
||||
if (get("sys_menu_enabled").empty())
|
||||
set("sys_menu_enabled", "1");
|
||||
#endif // _WIN32
|
||||
|
||||
// Remove legacy window positions/sizes
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ ArchiveData extract_sla_archive(const std::string &zipfname,
|
|||
ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
|
||||
double px_w, double px_h)
|
||||
{
|
||||
ExPolygons polys; polys.reserve(rings.size());
|
||||
auto polys = reserve_vector<ExPolygon>(rings.size());
|
||||
|
||||
for (const marchsq::Ring &ring : rings) {
|
||||
Polygon poly; Points &pts = poly.points;
|
||||
|
|
@ -147,7 +147,7 @@ ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
|
|||
polys.emplace_back(poly);
|
||||
}
|
||||
|
||||
// reverse the raster transformations
|
||||
// TODO: Is a union necessary?
|
||||
return union_ex(polys);
|
||||
}
|
||||
|
||||
|
|
@ -270,11 +270,11 @@ std::vector<ExPolygons> extract_slices_from_sla_archive(
|
|||
png::ReadBuf rb{arch.images[i].buf.data(), arch.images[i].buf.size()};
|
||||
if (!png::decode_png(rb, img)) return;
|
||||
|
||||
auto rings = marchsq::execute(img, 128, rstp.win);
|
||||
uint8_t isoval = 128;
|
||||
auto rings = marchsq::execute(img, isoval, rstp.win);
|
||||
ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h);
|
||||
|
||||
// Invert the raster transformations indicated in
|
||||
// the profile metadata
|
||||
// Invert the raster transformations indicated in the profile metadata
|
||||
invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height);
|
||||
|
||||
slices[i] = std::move(expolys);
|
||||
|
|
@ -310,7 +310,24 @@ ConfigSubstitutions import_sla_archive(
|
|||
std::string exclude_entries{"thumbnail"};
|
||||
ArchiveData arch = extract_sla_archive(zipfname, exclude_entries);
|
||||
DynamicPrintConfig profile_in, profile_use;
|
||||
ConfigSubstitutions config_substitutions = profile_in.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||
ConfigSubstitutions config_substitutions =
|
||||
profile_in.load(arch.profile,
|
||||
ForwardCompatibilitySubstitutionRule::Enable);
|
||||
|
||||
if (profile_in.empty()) { // missing profile... do guess work
|
||||
// try to recover the layer height from the config.ini which was
|
||||
// present in all versions of sl1 files.
|
||||
if (auto lh_opt = arch.config.find("layerHeight");
|
||||
lh_opt != arch.config.not_found())
|
||||
{
|
||||
auto lh_str = lh_opt->second.data();
|
||||
try {
|
||||
double lh = std::stod(lh_str); // TODO replace with std::from_chars
|
||||
profile_out.set("layer_height", lh);
|
||||
profile_out.set("initial_layer_height", lh);
|
||||
} catch(...) {}
|
||||
}
|
||||
}
|
||||
|
||||
// If the archive contains an empty profile, use the one that was passed as output argument
|
||||
// then replace it with the readed profile to report that it was empty.
|
||||
|
|
|
|||
|
|
@ -769,27 +769,31 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
|||
// Find the 'F' word.
|
||||
const char *fpos = strstr(line_start + 2, " F") + 2;
|
||||
int new_feedrate = current_feedrate;
|
||||
// Modify the F word of the current G-code line.
|
||||
bool modify = false;
|
||||
// Remove the F word from the current G-code line.
|
||||
bool remove = false;
|
||||
assert(fpos != nullptr);
|
||||
if (line->slowdown) {
|
||||
modify = true;
|
||||
new_feedrate = int(floor(60. * line->feedrate + 0.5));
|
||||
} else {
|
||||
new_feedrate = atoi(fpos);
|
||||
if (new_feedrate != current_feedrate) {
|
||||
// Append the line without the comment.
|
||||
new_gcode.append(line_start, end - line_start);
|
||||
current_feedrate = new_feedrate;
|
||||
} else if ((line->type & (CoolingLine::TYPE_ADJUSTABLE | CoolingLine::TYPE_EXTERNAL_PERIMETER | CoolingLine::TYPE_WIPE)) || line->length == 0.) {
|
||||
new_feedrate = line->slowdown ? int(floor(60. * line->feedrate + 0.5)) : atoi(fpos);
|
||||
if (new_feedrate == current_feedrate) {
|
||||
// No need to change the F value.
|
||||
if ((line->type & (CoolingLine::TYPE_ADJUSTABLE | CoolingLine::TYPE_EXTERNAL_PERIMETER | CoolingLine::TYPE_WIPE)) || line->length == 0.)
|
||||
// Feedrate does not change and this line does not move the print head. Skip the complete G-code line including the G-code comment.
|
||||
end = line_end;
|
||||
} else {
|
||||
// Remove the feedrate from the G0/G1 line.
|
||||
modify = true;
|
||||
}
|
||||
else
|
||||
// Remove the feedrate from the G0/G1 line. The G-code line may become empty!
|
||||
remove = true;
|
||||
} else if (line->slowdown) {
|
||||
// The F value will be overwritten.
|
||||
modify = true;
|
||||
} else {
|
||||
// The F value is different from current_feedrate, but not slowed down, thus the G-code line will not be modified.
|
||||
// Emit the line without the comment.
|
||||
new_gcode.append(line_start, end - line_start);
|
||||
current_feedrate = new_feedrate;
|
||||
}
|
||||
if (modify) {
|
||||
if (new_feedrate != current_feedrate) {
|
||||
if (modify || remove) {
|
||||
if (modify) {
|
||||
// Replace the feedrate.
|
||||
new_gcode.append(line_start, fpos - line_start);
|
||||
current_feedrate = new_feedrate;
|
||||
|
|
@ -805,12 +809,16 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
|||
new_gcode.append(line_start, f - line_start + 1);
|
||||
}
|
||||
// Skip the non-whitespaces of the F parameter up the comment or end of line.
|
||||
for (; fpos != end && *fpos != ' ' && *fpos != ';' && *fpos != '\n'; ++fpos);
|
||||
for (; fpos != end && *fpos != ' ' && *fpos != ';' && *fpos != '\n'; ++ fpos);
|
||||
// Append the rest of the line without the comment.
|
||||
if (fpos < end)
|
||||
// The G-code line is not empty yet. Emit the rest of it.
|
||||
new_gcode.append(fpos, end - fpos);
|
||||
// There should never be an empty G1 statement emited by the filter. Such lines should be removed completely.
|
||||
assert(new_gcode.size() < 4 || new_gcode.substr(new_gcode.size() - 4) != "G1 \n");
|
||||
else if (remove && new_gcode == "G1") {
|
||||
// The G-code line only contained the F word, now it is empty. Remove it completely including the comments.
|
||||
new_gcode.resize(new_gcode.size() - 2);
|
||||
end = line_end;
|
||||
}
|
||||
}
|
||||
// Process the rest of the line.
|
||||
if (end < line_end) {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_
|
|||
break;
|
||||
}
|
||||
}
|
||||
const bool contained_z = -1e10 < obj_min_z && obj_max_z < print_volume_height;
|
||||
|
||||
const bool contained_z = -1e10 < obj_min_z && obj_max_z <= print_volume_height;
|
||||
return (contained_xy && contained_z) ? ModelInstancePVS_Inside : ModelInstancePVS_Partly_Outside;
|
||||
}
|
||||
|
||||
|
|
@ -1263,10 +1264,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
|||
instances[instance]->get_mirror()
|
||||
);
|
||||
|
||||
z -= instances[instance]->get_offset()(2);
|
||||
z -= instances[instance]->get_offset().z();
|
||||
|
||||
// Lower part per-instance bounding boxes
|
||||
std::vector<BoundingBoxf3> lower_bboxes { instances.size() };
|
||||
// Displacement (in instance coordinates) to be applied to place the upper parts
|
||||
Vec3d local_displace = Vec3d::Zero();
|
||||
|
||||
for (ModelVolume *volume : volumes) {
|
||||
const auto volume_matrix = volume->get_matrix();
|
||||
|
|
@ -1286,8 +1287,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
|||
if (attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
lower->add_volume(*volume);
|
||||
}
|
||||
else if (! volume->mesh().empty()) {
|
||||
|
||||
else if (! volume->mesh().empty()) {
|
||||
// Transform the mesh by the combined transformation matrix.
|
||||
// Flip the triangles in case the composite transformation is left handed.
|
||||
TriangleMesh mesh(volume->mesh());
|
||||
|
|
@ -1327,13 +1327,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
|||
assert(vol->config.id() != volume->config.id());
|
||||
vol->set_material(volume->material_id(), *volume->material());
|
||||
|
||||
// Compute the lower part instances' bounding boxes to figure out where to place
|
||||
// the upper part
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
|
||||
for (size_t i = 0; i < instances.size(); i++) {
|
||||
lower_bboxes[i].merge(instances[i]->transform_mesh_bounding_box(lower_mesh, true));
|
||||
}
|
||||
}
|
||||
// Compute the displacement (in instance coordinates) to be applied to place the upper parts
|
||||
// The upper part displacement is set to half of the lower part bounding box
|
||||
// this is done in hope at least a part of the upper part will always be visible and draggable
|
||||
local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1341,17 +1338,18 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
|||
ModelObjectPtrs res;
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) {
|
||||
upper->invalidate_bounding_box();
|
||||
upper->center_around_origin();
|
||||
if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
|
||||
upper->center_around_origin();
|
||||
upper->translate_instances(-upper->origin_translation);
|
||||
upper->origin_translation = Vec3d::Zero();
|
||||
}
|
||||
|
||||
// Reset instance transformation except offset and Z-rotation
|
||||
for (size_t i = 0; i < instances.size(); i++) {
|
||||
for (size_t i = 0; i < instances.size(); ++i) {
|
||||
auto &instance = upper->instances[i];
|
||||
const Vec3d offset = instance->get_offset();
|
||||
const double rot_z = instance->get_rotation()(2);
|
||||
// The upper part displacement is set to half of the lower part bounding box
|
||||
// this is done in hope at least a part of the upper part will always be visible and draggable
|
||||
const Vec3d displace = lower_bboxes[i].size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
|
||||
const double rot_z = instance->get_rotation().z();
|
||||
const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), instance->get_rotation()) * local_displace;
|
||||
|
||||
instance->set_transformation(Geometry::Transformation());
|
||||
instance->set_offset(offset + displace);
|
||||
|
|
@ -1361,14 +1359,16 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
|||
res.push_back(upper);
|
||||
}
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) {
|
||||
lower->invalidate_bounding_box();
|
||||
lower->center_around_origin();
|
||||
if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
|
||||
lower->center_around_origin();
|
||||
lower->translate_instances(-lower->origin_translation);
|
||||
lower->origin_translation = Vec3d::Zero();
|
||||
}
|
||||
|
||||
// Reset instance transformation except offset and Z-rotation
|
||||
for (auto *instance : lower->instances) {
|
||||
const Vec3d offset = instance->get_offset();
|
||||
const double rot_z = instance->get_rotation()(2);
|
||||
|
||||
const double rot_z = instance->get_rotation().z();
|
||||
instance->set_transformation(Geometry::Transformation());
|
||||
instance->set_offset(offset);
|
||||
instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, rot_z));
|
||||
|
|
@ -1860,18 +1860,17 @@ size_t ModelVolume::split(unsigned int max_extruders)
|
|||
|
||||
size_t idx = 0;
|
||||
size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
|
||||
std::string name = this->name;
|
||||
const std::string name = this->name;
|
||||
|
||||
unsigned int extruder_counter = 0;
|
||||
Vec3d offset = this->get_offset();
|
||||
const Vec3d offset = this->get_offset();
|
||||
|
||||
for (TriangleMesh &mesh : meshes) {
|
||||
if (mesh.empty())
|
||||
// Repair may have removed unconnected triangles, thus emptying the mesh.
|
||||
continue;
|
||||
|
||||
if (idx == 0)
|
||||
{
|
||||
if (idx == 0) {
|
||||
this->set_mesh(std::move(mesh));
|
||||
this->calculate_convex_hull();
|
||||
// Assign a new unique ID, so that a new GLVolume will be generated.
|
||||
|
|
@ -1890,7 +1889,19 @@ size_t ModelVolume::split(unsigned int max_extruders)
|
|||
this->object->volumes[ivolume]->m_is_splittable = 0;
|
||||
++ idx;
|
||||
}
|
||||
|
||||
|
||||
// discard volumes for which the convex hull was not generated or is degenerate
|
||||
size_t i = 0;
|
||||
while (i < this->object->volumes.size()) {
|
||||
const std::shared_ptr<const TriangleMesh> &hull = this->object->volumes[i]->get_convex_hull_shared_ptr();
|
||||
if (hull == nullptr || hull->its.vertices.empty() || hull->its.indices.empty()) {
|
||||
this->object->delete_volume(i);
|
||||
--idx;
|
||||
--i;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -684,7 +684,7 @@ public:
|
|||
|
||||
void calculate_convex_hull();
|
||||
const TriangleMesh& get_convex_hull() const;
|
||||
std::shared_ptr<const TriangleMesh> get_convex_hull_shared_ptr() const { return m_convex_hull; }
|
||||
const std::shared_ptr<const TriangleMesh>& get_convex_hull_shared_ptr() const { return m_convex_hull; }
|
||||
// Get count of errors in the mesh
|
||||
int get_repaired_errors_count() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ struct segment_traits<Slic3r::ColoredLine> {
|
|||
//#define MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
//#define MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
//#define MMU_SEGMENTATION_DEBUG_INPUT
|
||||
//#define MMU_SEGMENTATION_DEBUG_PAINTED_LINES
|
||||
//#define MMU_SEGMENTATION_DEBUG_COLORIZED_POLYGONS
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -147,18 +149,6 @@ struct PaintedLineVisitor
|
|||
static inline const double append_threshold2 = Slic3r::sqr(append_threshold);
|
||||
};
|
||||
|
||||
static std::vector<ColoredLine> to_colored_lines(const EdgeGrid::Contour &contour, int color)
|
||||
{
|
||||
std::vector<ColoredLine> lines;
|
||||
if (contour.num_segments() > 2) {
|
||||
lines.reserve(contour.num_segments());
|
||||
for (auto it = contour.begin(); it != contour.end() - 1; ++it)
|
||||
lines.push_back({Line(*it, *(it + 1)), color});
|
||||
lines.push_back({Line(contour.back(), contour.front()), color});
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
static Polygon colored_points_to_polygon(const std::vector<ColoredLine> &lines)
|
||||
{
|
||||
Polygon out;
|
||||
|
|
@ -242,25 +232,18 @@ static std::vector<std::vector<std::pair<size_t, size_t>>> get_all_segments(cons
|
|||
return all_segments;
|
||||
}
|
||||
|
||||
static std::vector<ColoredLine> colorize_line(const Line & line_to_process,
|
||||
const size_t start_idx,
|
||||
const size_t end_idx,
|
||||
std::vector<PaintedLine> &painted_lines)
|
||||
static std::vector<PaintedLine> filter_painted_lines(const Line &line_to_process, const size_t start_idx, const size_t end_idx, const std::vector<PaintedLine> &painted_lines)
|
||||
{
|
||||
std::vector<PaintedLine> internal_painted;
|
||||
for (size_t line_idx = start_idx; line_idx <= end_idx; ++line_idx)
|
||||
internal_painted.emplace_back(painted_lines[line_idx]);
|
||||
|
||||
const int filter_eps_value = scale_(0.1f);
|
||||
std::vector<PaintedLine> filtered_lines;
|
||||
filtered_lines.emplace_back(internal_painted.front());
|
||||
for (size_t line_idx = 1; line_idx < internal_painted.size(); ++line_idx) {
|
||||
filtered_lines.emplace_back(painted_lines[start_idx]);
|
||||
for (size_t line_idx = start_idx + 1; line_idx <= end_idx; ++line_idx) {
|
||||
// line_to_process is already all colored. Skip another possible duplicate coloring.
|
||||
if(filtered_lines.back().projected_line.b == line_to_process.b)
|
||||
break;
|
||||
|
||||
PaintedLine &prev = filtered_lines.back();
|
||||
PaintedLine &curr = internal_painted[line_idx];
|
||||
const PaintedLine &curr = painted_lines[line_idx];
|
||||
|
||||
double prev_length = prev.projected_line.length();
|
||||
double curr_dist_start = (curr.projected_line.a - prev.projected_line.a).cast<double>().norm();
|
||||
|
|
@ -278,31 +261,84 @@ static std::vector<ColoredLine> colorize_line(const Line & line_to_
|
|||
}
|
||||
} else {
|
||||
double curr_dist_end = (curr.projected_line.b - prev.projected_line.a).cast<double>().norm();
|
||||
if (curr_dist_end <= prev_length) {
|
||||
} else {
|
||||
if (prev.color == curr.color) {
|
||||
if (curr_dist_end > prev_length) {
|
||||
if (prev.color == curr.color)
|
||||
prev.projected_line.b = curr.projected_line.b;
|
||||
} else {
|
||||
curr.projected_line.a = prev.projected_line.b;
|
||||
filtered_lines.emplace_back(curr);
|
||||
}
|
||||
else
|
||||
filtered_lines.push_back({curr.contour_idx, curr.line_idx, Line{prev.projected_line.b, curr.projected_line.b}, curr.color});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ColoredLine> final_lines;
|
||||
double dist_to_start = (filtered_lines.front().projected_line.a - line_to_process.a).cast<double>().norm();
|
||||
if (dist_to_start <= filter_eps_value) {
|
||||
if (double dist_to_start = (filtered_lines.front().projected_line.a - line_to_process.a).cast<double>().norm(); dist_to_start <= filter_eps_value)
|
||||
filtered_lines.front().projected_line.a = line_to_process.a;
|
||||
final_lines.push_back({filtered_lines.front().projected_line, filtered_lines.front().color});
|
||||
} else {
|
||||
final_lines.push_back({Line(line_to_process.a, filtered_lines.front().projected_line.a), 0});
|
||||
final_lines.push_back({filtered_lines.front().projected_line, filtered_lines.front().color});
|
||||
|
||||
if (double dist_to_end = (filtered_lines.back().projected_line.b - line_to_process.b).cast<double>().norm(); dist_to_end <= filter_eps_value)
|
||||
filtered_lines.back().projected_line.b = line_to_process.b;
|
||||
|
||||
return filtered_lines;
|
||||
}
|
||||
|
||||
static std::vector<std::vector<PaintedLine>> post_process_painted_lines(const std::vector<EdgeGrid::Contour> &contours, std::vector<PaintedLine> &&painted_lines)
|
||||
{
|
||||
if (painted_lines.empty())
|
||||
return {};
|
||||
|
||||
auto comp = [&contours](const PaintedLine &first, const PaintedLine &second) {
|
||||
Point first_start_p = contours[first.contour_idx].segment_start(first.line_idx);
|
||||
return first.contour_idx < second.contour_idx ||
|
||||
(first.contour_idx == second.contour_idx &&
|
||||
(first.line_idx < second.line_idx ||
|
||||
(first.line_idx == second.line_idx &&
|
||||
((first.projected_line.a - first_start_p).cast<double>().squaredNorm() < (second.projected_line.a - first_start_p).cast<double>().squaredNorm() ||
|
||||
((first.projected_line.a - first_start_p).cast<double>().squaredNorm() == (second.projected_line.a - first_start_p).cast<double>().squaredNorm() &&
|
||||
(first.projected_line.b - first.projected_line.a).cast<double>().squaredNorm() < (second.projected_line.b - second.projected_line.a).cast<double>().squaredNorm())))));
|
||||
};
|
||||
std::sort(painted_lines.begin(), painted_lines.end(), comp);
|
||||
|
||||
std::vector<std::vector<PaintedLine>> filtered_painted_lines(contours.size());
|
||||
size_t prev_painted_line_idx = 0;
|
||||
for (size_t curr_painted_line_idx = 0; curr_painted_line_idx < painted_lines.size(); ++curr_painted_line_idx) {
|
||||
size_t next_painted_line_idx = curr_painted_line_idx + 1;
|
||||
if (next_painted_line_idx >= painted_lines.size() || painted_lines[curr_painted_line_idx].contour_idx != painted_lines[next_painted_line_idx].contour_idx || painted_lines[curr_painted_line_idx].line_idx != painted_lines[next_painted_line_idx].line_idx) {
|
||||
const PaintedLine &start_line = painted_lines[prev_painted_line_idx];
|
||||
const Line &line_to_process = contours[start_line.contour_idx].get_segment(start_line.line_idx);
|
||||
Slic3r::append(filtered_painted_lines[painted_lines[curr_painted_line_idx].contour_idx], filter_painted_lines(line_to_process, prev_painted_line_idx, curr_painted_line_idx, painted_lines));
|
||||
prev_painted_line_idx = next_painted_line_idx;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t line_idx = 1; line_idx < filtered_lines.size(); ++line_idx) {
|
||||
ColoredLine &prev = final_lines.back();
|
||||
PaintedLine &curr = filtered_lines[line_idx];
|
||||
return filtered_painted_lines;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
static bool are_lines_connected(const std::vector<ColoredLine> &colored_lines)
|
||||
{
|
||||
for (size_t line_idx = 1; line_idx < colored_lines.size(); ++line_idx)
|
||||
if (colored_lines[line_idx - 1].line.b != colored_lines[line_idx].line.a)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static std::vector<ColoredLine> colorize_line(const Line &line_to_process,
|
||||
const size_t start_idx,
|
||||
const size_t end_idx,
|
||||
const std::vector<PaintedLine> &painted_contour)
|
||||
{
|
||||
assert(start_idx < painted_contour.size() && end_idx < painted_contour.size() && start_idx <= end_idx);
|
||||
assert(std::all_of(painted_contour.begin() + start_idx, painted_contour.begin() + end_idx + 1, [&painted_contour, &start_idx](const auto &p_line) { return painted_contour[start_idx].line_idx == p_line.line_idx; }));
|
||||
|
||||
const int filter_eps_value = scale_(0.1f);
|
||||
std::vector<ColoredLine> final_lines;
|
||||
const PaintedLine &first_line = painted_contour[start_idx];
|
||||
if (double dist_to_start = (first_line.projected_line.a - line_to_process.a).cast<double>().norm(); dist_to_start > filter_eps_value)
|
||||
final_lines.push_back({Line(line_to_process.a, first_line.projected_line.a), 0});
|
||||
final_lines.push_back({first_line.projected_line, first_line.color});
|
||||
|
||||
for (size_t line_idx = start_idx + 1; line_idx <= end_idx; ++line_idx) {
|
||||
ColoredLine &prev = final_lines.back();
|
||||
const PaintedLine &curr = painted_contour[line_idx];
|
||||
|
||||
double line_dist = (curr.projected_line.a - prev.line.b).cast<double>().norm();
|
||||
if (line_dist <= filter_eps_value) {
|
||||
|
|
@ -318,18 +354,16 @@ static std::vector<ColoredLine> colorize_line(const Line & line_to_
|
|||
}
|
||||
}
|
||||
|
||||
double dist_to_end = (final_lines.back().line.b - line_to_process.b).cast<double>().norm();
|
||||
if (dist_to_end <= filter_eps_value)
|
||||
final_lines.back().line.b = line_to_process.b;
|
||||
else
|
||||
// If there is non-painted space, then inserts line painted by a default color.
|
||||
if (double dist_to_end = (final_lines.back().line.b - line_to_process.b).cast<double>().norm(); dist_to_end > filter_eps_value)
|
||||
final_lines.push_back({Line(final_lines.back().line.b, line_to_process.b), 0});
|
||||
|
||||
for (size_t line_idx = 1; line_idx < final_lines.size(); ++line_idx)
|
||||
assert(final_lines[line_idx - 1].line.b == final_lines[line_idx].line.a);
|
||||
// Make sure all the lines are connected.
|
||||
assert(are_lines_connected(final_lines));
|
||||
|
||||
for (size_t line_idx = 2; line_idx < final_lines.size(); ++line_idx) {
|
||||
const ColoredLine &line_0 = final_lines[line_idx - 2];
|
||||
ColoredLine & line_1 = final_lines[line_idx - 1];
|
||||
ColoredLine &line_1 = final_lines[line_idx - 1];
|
||||
const ColoredLine &line_2 = final_lines[line_idx - 0];
|
||||
|
||||
if (line_0.color == line_2.color && line_0.color != line_1.color)
|
||||
|
|
@ -349,52 +383,25 @@ static std::vector<ColoredLine> colorize_line(const Line & line_to_
|
|||
|
||||
final_lines = colored_lines_simple;
|
||||
|
||||
if (final_lines.size() > 1) {
|
||||
if (final_lines.size() > 1)
|
||||
if (final_lines.front().color != final_lines[1].color && final_lines.front().line.length() <= scale_(0.2)) {
|
||||
final_lines[1].line.a = final_lines.front().line.a;
|
||||
final_lines.erase(final_lines.begin());
|
||||
}
|
||||
}
|
||||
|
||||
if (final_lines.size() > 1) {
|
||||
if (final_lines.size() > 1)
|
||||
if (final_lines.back().color != final_lines[final_lines.size() - 2].color && final_lines.back().line.length() <= scale_(0.2)) {
|
||||
final_lines[final_lines.size() - 2].line.b = final_lines.back().line.b;
|
||||
final_lines.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
return final_lines;
|
||||
}
|
||||
|
||||
static std::vector<ColoredLine> colorize_polygon(const EdgeGrid::Contour &contour, const size_t start_idx, const size_t end_idx, std::vector<PaintedLine> &painted_lines)
|
||||
{
|
||||
std::vector<ColoredLine> new_lines;
|
||||
new_lines.reserve(end_idx - start_idx + 1);
|
||||
for (size_t idx = 0; idx < painted_lines[start_idx].line_idx; ++idx)
|
||||
new_lines.emplace_back(ColoredLine{contour.get_segment(idx), 0});
|
||||
|
||||
for (size_t first_idx = start_idx; first_idx <= end_idx; ++first_idx) {
|
||||
size_t second_idx = first_idx;
|
||||
while (second_idx <= end_idx && painted_lines[first_idx].line_idx == painted_lines[second_idx].line_idx) ++second_idx;
|
||||
--second_idx;
|
||||
|
||||
assert(painted_lines[first_idx].line_idx == painted_lines[second_idx].line_idx);
|
||||
std::vector<ColoredLine> lines_c_line = colorize_line(contour.get_segment(painted_lines[first_idx].line_idx), first_idx, second_idx, painted_lines);
|
||||
new_lines.insert(new_lines.end(), lines_c_line.begin(), lines_c_line.end());
|
||||
|
||||
if (second_idx + 1 <= end_idx)
|
||||
for (size_t idx = painted_lines[second_idx].line_idx + 1; idx < painted_lines[second_idx + 1].line_idx; ++idx)
|
||||
new_lines.emplace_back(ColoredLine{contour.get_segment(idx), 0});
|
||||
|
||||
first_idx = second_idx;
|
||||
}
|
||||
|
||||
for (size_t idx = painted_lines[end_idx].line_idx + 1; idx < contour.num_segments(); ++idx)
|
||||
new_lines.emplace_back(ColoredLine{contour.get_segment(idx), 0});
|
||||
|
||||
static std::vector<ColoredLine> filter_colorized_polygon(std::vector<ColoredLine> &&new_lines) {
|
||||
for (size_t line_idx = 2; line_idx < new_lines.size(); ++line_idx) {
|
||||
const ColoredLine &line_0 = new_lines[line_idx - 2];
|
||||
ColoredLine & line_1 = new_lines[line_idx - 1];
|
||||
ColoredLine &line_1 = new_lines[line_idx - 1];
|
||||
const ColoredLine &line_2 = new_lines[line_idx - 0];
|
||||
|
||||
if (line_0.color == line_2.color && line_0.color != line_1.color && line_0.color >= 1) {
|
||||
|
|
@ -404,8 +411,8 @@ static std::vector<ColoredLine> colorize_polygon(const EdgeGrid::Contour &contou
|
|||
|
||||
for (size_t line_idx = 3; line_idx < new_lines.size(); ++line_idx) {
|
||||
const ColoredLine &line_0 = new_lines[line_idx - 3];
|
||||
ColoredLine & line_1 = new_lines[line_idx - 2];
|
||||
ColoredLine & line_2 = new_lines[line_idx - 1];
|
||||
ColoredLine &line_1 = new_lines[line_idx - 2];
|
||||
ColoredLine &line_2 = new_lines[line_idx - 1];
|
||||
const ColoredLine &line_3 = new_lines[line_idx - 0];
|
||||
|
||||
if (line_0.color == line_3.color && (line_0.color != line_1.color || line_0.color != line_2.color) && line_0.color >= 1 && line_3.color >= 1) {
|
||||
|
|
@ -425,79 +432,110 @@ static std::vector<ColoredLine> colorize_polygon(const EdgeGrid::Contour &contou
|
|||
return total_length;
|
||||
};
|
||||
|
||||
for (size_t pair_idx = 1; pair_idx < segments.size(); ++pair_idx) {
|
||||
int color0 = new_lines[segments[pair_idx - 1].first].color;
|
||||
int color1 = new_lines[segments[pair_idx - 0].first].color;
|
||||
if (segments.size() >= 2)
|
||||
for (size_t curr_idx = 0; curr_idx < segments.size(); ++curr_idx) {
|
||||
size_t next_idx = next_idx_modulo(curr_idx, segments.size());
|
||||
assert(curr_idx != next_idx);
|
||||
|
||||
double seg0l = segment_length(segments[pair_idx - 1]);
|
||||
double seg1l = segment_length(segments[pair_idx - 0]);
|
||||
int color0 = new_lines[segments[curr_idx].first].color;
|
||||
int color1 = new_lines[segments[next_idx].first].color;
|
||||
|
||||
if (color0 != color1 && seg0l >= scale_(0.1) && seg1l <= scale_(0.2)) {
|
||||
for (size_t seg_start_idx = segments[pair_idx].first; seg_start_idx != segments[pair_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0)
|
||||
new_lines[seg_start_idx].color = color0;
|
||||
new_lines[segments[pair_idx].second].color = color0;
|
||||
double seg0l = segment_length(segments[curr_idx]);
|
||||
double seg1l = segment_length(segments[next_idx]);
|
||||
|
||||
if (color0 != color1 && seg0l >= scale_(0.1) && seg1l <= scale_(0.2)) {
|
||||
for (size_t seg_start_idx = segments[next_idx].first; seg_start_idx != segments[next_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0)
|
||||
new_lines[seg_start_idx].color = color0;
|
||||
new_lines[segments[next_idx].second].color = color0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
segments = get_segments(new_lines);
|
||||
for (size_t pair_idx = 1; pair_idx < segments.size(); ++pair_idx) {
|
||||
int color0 = new_lines[segments[pair_idx - 1].first].color;
|
||||
int color1 = new_lines[segments[pair_idx - 0].first].color;
|
||||
double seg1l = segment_length(segments[pair_idx - 0]);
|
||||
if (segments.size() >= 2)
|
||||
for (size_t curr_idx = 0; curr_idx < segments.size(); ++curr_idx) {
|
||||
size_t next_idx = next_idx_modulo(curr_idx, segments.size());
|
||||
assert(curr_idx != next_idx);
|
||||
|
||||
if (color0 >= 1 && color0 != color1 && seg1l <= scale_(0.2)) {
|
||||
for (size_t seg_start_idx = segments[pair_idx].first; seg_start_idx != segments[pair_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0)
|
||||
new_lines[seg_start_idx].color = color0;
|
||||
new_lines[segments[pair_idx].second].color = color0;
|
||||
int color0 = new_lines[segments[curr_idx].first].color;
|
||||
int color1 = new_lines[segments[next_idx].first].color;
|
||||
double seg1l = segment_length(segments[next_idx]);
|
||||
|
||||
if (color0 >= 1 && color0 != color1 && seg1l <= scale_(0.2)) {
|
||||
for (size_t seg_start_idx = segments[next_idx].first; seg_start_idx != segments[next_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0)
|
||||
new_lines[seg_start_idx].color = color0;
|
||||
new_lines[segments[next_idx].second].color = color0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t pair_idx = 2; pair_idx < segments.size(); ++pair_idx) {
|
||||
int color0 = new_lines[segments[pair_idx - 2].first].color;
|
||||
int color1 = new_lines[segments[pair_idx - 1].first].color;
|
||||
int color2 = new_lines[segments[pair_idx - 0].first].color;
|
||||
segments = get_segments(new_lines);
|
||||
if (segments.size() >= 3)
|
||||
for (size_t curr_idx = 0; curr_idx < segments.size(); ++curr_idx) {
|
||||
size_t next_idx = next_idx_modulo(curr_idx, segments.size());
|
||||
size_t next_next_idx = next_idx_modulo(next_idx, segments.size());
|
||||
|
||||
if (color0 > 0 && color0 == color2 && color0 != color1 && segment_length(segments[pair_idx - 1]) <= scale_(0.5)) {
|
||||
for (size_t seg_start_idx = segments[pair_idx].first; seg_start_idx != segments[pair_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0)
|
||||
new_lines[seg_start_idx].color = color0;
|
||||
new_lines[segments[pair_idx].second].color = color0;
|
||||
int color0 = new_lines[segments[curr_idx].first].color;
|
||||
int color1 = new_lines[segments[next_idx].first].color;
|
||||
int color2 = new_lines[segments[next_next_idx].first].color;
|
||||
|
||||
if (color0 > 0 && color0 == color2 && color0 != color1 && segment_length(segments[next_idx]) <= scale_(0.5)) {
|
||||
for (size_t seg_start_idx = segments[next_next_idx].first; seg_start_idx != segments[next_next_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0)
|
||||
new_lines[seg_start_idx].color = color0;
|
||||
new_lines[segments[next_next_idx].second].color = color0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new_lines;
|
||||
return std::move(new_lines);
|
||||
}
|
||||
|
||||
static std::vector<std::vector<ColoredLine>> colorize_polygons(const std::vector<EdgeGrid::Contour> &contours, std::vector<PaintedLine> &painted_lines)
|
||||
{
|
||||
const size_t start_idx = 0;
|
||||
const size_t end_idx = painted_lines.size() - 1;
|
||||
static std::vector<ColoredLine> colorize_contour(const EdgeGrid::Contour &contour, const std::vector<PaintedLine> &painted_contour) {
|
||||
assert(painted_contour.empty() || std::all_of(painted_contour.begin(), painted_contour.end(), [&painted_contour](const auto &p_line) { return painted_contour.front().contour_idx == p_line.contour_idx; }));
|
||||
|
||||
std::vector<std::vector<ColoredLine>> new_polygons;
|
||||
new_polygons.reserve(contours.size());
|
||||
|
||||
for (size_t idx = 0; idx < painted_lines[start_idx].contour_idx; ++idx)
|
||||
new_polygons.emplace_back(to_colored_lines(contours[idx], 0));
|
||||
|
||||
for (size_t first_idx = start_idx; first_idx <= end_idx; ++first_idx) {
|
||||
size_t second_idx = first_idx;
|
||||
while (second_idx <= end_idx && painted_lines[first_idx].contour_idx == painted_lines[second_idx].contour_idx)
|
||||
++second_idx;
|
||||
--second_idx;
|
||||
|
||||
assert(painted_lines[first_idx].contour_idx == painted_lines[second_idx].contour_idx);
|
||||
new_polygons.emplace_back(colorize_polygon(contours[painted_lines[first_idx].contour_idx], first_idx, second_idx, painted_lines));
|
||||
|
||||
if (second_idx + 1 <= end_idx)
|
||||
for (size_t idx = painted_lines[second_idx].contour_idx + 1; idx < painted_lines[second_idx + 1].contour_idx; ++idx)
|
||||
new_polygons.emplace_back(to_colored_lines(contours[idx], 0));
|
||||
|
||||
first_idx = second_idx;
|
||||
std::vector<ColoredLine> colorized_contour;
|
||||
if (painted_contour.empty()) {
|
||||
// Appends contour with default color for lines before the first PaintedLine.
|
||||
colorized_contour.reserve(contour.num_segments());
|
||||
for (const Line &line : contour.get_segments())
|
||||
colorized_contour.emplace_back(ColoredLine{line, 0});
|
||||
return colorized_contour;
|
||||
}
|
||||
|
||||
for (size_t idx = painted_lines[end_idx].contour_idx + 1; idx < contours.size(); ++idx)
|
||||
new_polygons.emplace_back(to_colored_lines(contours[idx], 0));
|
||||
colorized_contour.reserve(contour.num_segments() + painted_contour.size());
|
||||
for (size_t idx = 0; idx < painted_contour.front().line_idx; ++idx)
|
||||
colorized_contour.emplace_back(ColoredLine{contour.get_segment(idx), 0});
|
||||
|
||||
return new_polygons;
|
||||
size_t prev_painted_line_idx = 0;
|
||||
for (size_t curr_painted_line_idx = 0; curr_painted_line_idx < painted_contour.size(); ++curr_painted_line_idx) {
|
||||
size_t next_painted_line_idx = curr_painted_line_idx + 1;
|
||||
if (next_painted_line_idx >= painted_contour.size() || painted_contour[curr_painted_line_idx].line_idx != painted_contour[next_painted_line_idx].line_idx) {
|
||||
const std::vector<PaintedLine> &painted_contour_copy = painted_contour;
|
||||
Slic3r::append(colorized_contour, colorize_line(contour.get_segment(painted_contour[prev_painted_line_idx].line_idx), prev_painted_line_idx, curr_painted_line_idx, painted_contour_copy));
|
||||
|
||||
// Appends contour with default color for lines between the current and the next PaintedLine.
|
||||
if (next_painted_line_idx < painted_contour.size())
|
||||
for (size_t idx = painted_contour[curr_painted_line_idx].line_idx + 1; idx < painted_contour[next_painted_line_idx].line_idx; ++idx)
|
||||
colorized_contour.emplace_back(ColoredLine{contour.get_segment(idx), 0});
|
||||
|
||||
prev_painted_line_idx = next_painted_line_idx;
|
||||
}
|
||||
}
|
||||
|
||||
// Appends contour with default color for lines after the last PaintedLine.
|
||||
for (size_t idx = painted_contour.back().line_idx + 1; idx < contour.num_segments(); ++idx)
|
||||
colorized_contour.emplace_back(ColoredLine{contour.get_segment(idx), 0});
|
||||
|
||||
assert(!colorized_contour.empty());
|
||||
return filter_colorized_polygon(std::move(colorized_contour));
|
||||
}
|
||||
|
||||
static std::vector<std::vector<ColoredLine>> colorize_contours(const std::vector<EdgeGrid::Contour> &contours, const std::vector<std::vector<PaintedLine>> &painted_contours)
|
||||
{
|
||||
assert(contours.size() == painted_contours.size());
|
||||
std::vector<std::vector<ColoredLine>> colorized_contours(contours.size());
|
||||
for (const std::vector<PaintedLine> &painted_contour : painted_contours) {
|
||||
size_t contour_idx = &painted_contour - &painted_contours.front();
|
||||
colorized_contours[contour_idx] = colorize_contour(contours[contour_idx], painted_contours[contour_idx]);
|
||||
}
|
||||
return colorized_contours;
|
||||
}
|
||||
|
||||
using boost::polygon::voronoi_diagram;
|
||||
|
|
@ -1075,7 +1113,7 @@ static inline Polygon to_polygon(const std::vector<Linef> &lines)
|
|||
// It iterates through all nodes on the border between two different colors, and from this point,
|
||||
// start selection always left most edges for every node to construct CCW polygons.
|
||||
// Assumes that graph is planar (without self-intersection edges)
|
||||
static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MMU_Graph &graph)
|
||||
static std::vector<ExPolygons> extract_colored_segments(const MMU_Graph &graph, const size_t num_extruders)
|
||||
{
|
||||
std::vector<bool> used_arcs(graph.arcs.size(), false);
|
||||
// When there is no next arc, then is returned original_arc or edge with is marked as used
|
||||
|
|
@ -1115,7 +1153,7 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MM
|
|||
return std::all_of(node.arc_idxs.cbegin(), node.arc_idxs.cend(), [&used_arcs](const size_t &arc_idx) -> bool { return used_arcs[arc_idx]; });
|
||||
};
|
||||
|
||||
std::vector<std::pair<Polygon, size_t>> polygons_segments;
|
||||
std::vector<ExPolygons> expolygons_segments(num_extruders + 1);
|
||||
for (size_t node_idx = 0; node_idx < graph.all_border_points; ++node_idx) {
|
||||
const MMU_Graph::Node &node = graph.nodes[node_idx];
|
||||
|
||||
|
|
@ -1145,12 +1183,11 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MM
|
|||
p_arc = &next;
|
||||
} while (graph.nodes[p_arc->to_idx].point != start_p || !all_arc_used(graph.nodes[p_arc->to_idx]));
|
||||
|
||||
Polygon poly = to_polygon(face_lines);
|
||||
if (poly.is_counter_clockwise() && poly.is_valid())
|
||||
polygons_segments.emplace_back(poly, arc.color);
|
||||
if (Polygon poly = to_polygon(face_lines); poly.is_counter_clockwise() && poly.is_valid())
|
||||
expolygons_segments[arc.color].emplace_back(std::move(poly));
|
||||
}
|
||||
}
|
||||
return polygons_segments;
|
||||
return expolygons_segments;
|
||||
}
|
||||
|
||||
// Used in remove_multiple_edges_in_vertices()
|
||||
|
|
@ -1231,21 +1268,20 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto
|
|||
}
|
||||
}
|
||||
|
||||
static void cut_segmented_layers(const std::vector<ExPolygons> &input_expolygons,
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> &segmented_regions,
|
||||
const float cut_width,
|
||||
const std::function<void()> &throw_on_cancel_callback)
|
||||
static void cut_segmented_layers(const std::vector<ExPolygons> &input_expolygons,
|
||||
std::vector<std::vector<ExPolygons>> &segmented_regions,
|
||||
const float cut_width,
|
||||
const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - cutting segmented layers in parallel - begin";
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, segmented_regions.size()),[&segmented_regions, &input_expolygons, &cut_width, &throw_on_cancel_callback](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
std::vector<std::pair<ExPolygon, size_t>> segmented_regions_cuts;
|
||||
for (const std::pair<ExPolygon, size_t> &colored_expoly : segmented_regions[layer_idx]) {
|
||||
ExPolygons cut_colored_expoly = diff_ex(colored_expoly.first, offset_ex(input_expolygons[layer_idx], cut_width));
|
||||
for (ExPolygon &expoly : cut_colored_expoly)
|
||||
segmented_regions_cuts.emplace_back(std::move(expoly), colored_expoly.second);
|
||||
}
|
||||
const size_t num_extruders_plus_one = segmented_regions[layer_idx].size();
|
||||
std::vector<ExPolygons> segmented_regions_cuts(num_extruders_plus_one); // Indexed by extruder_id
|
||||
for (size_t extruder_idx = 0; extruder_idx < num_extruders_plus_one; ++extruder_idx)
|
||||
if (const ExPolygons &ex_polygons = segmented_regions[layer_idx][extruder_idx]; !ex_polygons.empty())
|
||||
segmented_regions_cuts[extruder_idx] = diff_ex(ex_polygons, offset_ex(input_expolygons[layer_idx], cut_width));
|
||||
segmented_regions[layer_idx] = std::move(segmented_regions_cuts);
|
||||
}
|
||||
}); // end of parallel_for
|
||||
|
|
@ -1285,7 +1321,7 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
|||
// Project upwards pointing painted triangles over top surfaces,
|
||||
// project downards pointing painted triangles over bottom surfaces.
|
||||
std::vector<std::vector<Polygons>> top_raw(num_extruders), bottom_raw(num_extruders);
|
||||
std::vector<float> zs = zs_from_layers(print_object.layers());
|
||||
std::vector<float> zs = zs_from_layers(layers);
|
||||
Transform3d object_trafo = print_object.trafo_centered();
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
|
||||
|
|
@ -1494,31 +1530,42 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
|||
return triangles_by_color_merged;
|
||||
}
|
||||
|
||||
static std::vector<std::vector<std::pair<ExPolygon, size_t>>> merge_segmented_layers(
|
||||
const std::vector<std::vector<std::pair<ExPolygon, size_t>>> &segmented_regions,
|
||||
std::vector<std::vector<ExPolygons>> &&top_and_bottom_layers,
|
||||
const std::function<void()> &throw_on_cancel_callback)
|
||||
static std::vector<std::vector<ExPolygons>> merge_segmented_layers(
|
||||
const std::vector<std::vector<ExPolygons>> &segmented_regions,
|
||||
std::vector<std::vector<ExPolygons>> &&top_and_bottom_layers,
|
||||
const size_t num_extruders,
|
||||
const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> segmented_regions_merged(segmented_regions.size());
|
||||
const size_t num_layers = segmented_regions.size();
|
||||
std::vector<std::vector<ExPolygons>> segmented_regions_merged(num_layers);
|
||||
segmented_regions_merged.assign(num_layers, std::vector<ExPolygons>(num_extruders));
|
||||
assert(num_extruders + 1 == top_and_bottom_layers.size());
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging segmented layers in parallel - begin";
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, segmented_regions.size()), [&segmented_regions, &top_and_bottom_layers, &segmented_regions_merged, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers), [&segmented_regions, &top_and_bottom_layers, &segmented_regions_merged, &num_extruders, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
for (const std::pair<ExPolygon, size_t> &colored_expoly : segmented_regions[layer_idx]) {
|
||||
assert(segmented_regions[layer_idx].size() == num_extruders + 1);
|
||||
// Zero is skipped because it is the default color of the volume
|
||||
for (size_t extruder_id = 1; extruder_id < num_extruders + 1; ++extruder_id) {
|
||||
throw_on_cancel_callback();
|
||||
// Zero is the default color of the volume.
|
||||
if(colored_expoly.second == 0)
|
||||
continue;
|
||||
ExPolygons cut_colored_expoly = {colored_expoly.first};
|
||||
for (const std::vector<ExPolygons> &top_and_bottom_layer : top_and_bottom_layers)
|
||||
cut_colored_expoly = diff_ex(cut_colored_expoly, top_and_bottom_layer[layer_idx]);
|
||||
for (ExPolygon &ex_poly : cut_colored_expoly)
|
||||
segmented_regions_merged[layer_idx].emplace_back(std::move(ex_poly), colored_expoly.second - 1);
|
||||
}
|
||||
if (!segmented_regions[layer_idx][extruder_id].empty()) {
|
||||
ExPolygons segmented_regions_trimmed = segmented_regions[layer_idx][extruder_id];
|
||||
for (const std::vector<ExPolygons> &top_and_bottom_by_extruder : top_and_bottom_layers)
|
||||
if (!top_and_bottom_by_extruder[layer_idx].empty() && !segmented_regions_trimmed.empty())
|
||||
segmented_regions_trimmed = diff_ex(segmented_regions_trimmed, top_and_bottom_by_extruder[layer_idx]);
|
||||
|
||||
for (size_t color_idx = 1; color_idx < top_and_bottom_layers.size(); ++color_idx)
|
||||
for (ExPolygon &expoly : top_and_bottom_layers[color_idx][layer_idx])
|
||||
segmented_regions_merged[layer_idx].emplace_back(std::move(expoly), color_idx - 1);
|
||||
segmented_regions_merged[layer_idx][extruder_id - 1] = std::move(segmented_regions_trimmed);
|
||||
}
|
||||
|
||||
if (!top_and_bottom_layers[extruder_id][layer_idx].empty()) {
|
||||
bool was_top_and_bottom_empty = segmented_regions_merged[layer_idx][extruder_id - 1].empty();
|
||||
append(segmented_regions_merged[layer_idx][extruder_id - 1], top_and_bottom_layers[extruder_id][layer_idx]);
|
||||
|
||||
// Remove dimples (#7235) appearing after merging side segmentation of the model with tops and bottoms painted layers.
|
||||
if (!was_top_and_bottom_empty)
|
||||
segmented_regions_merged[layer_idx][extruder_id - 1] = offset2_ex(union_ex(segmented_regions_merged[layer_idx][extruder_id - 1]), float(SCALED_EPSILON), -float(SCALED_EPSILON));
|
||||
}
|
||||
}
|
||||
}
|
||||
}); // end of parallel_for
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging segmented layers in parallel - end";
|
||||
|
|
@ -1527,7 +1574,7 @@ static std::vector<std::vector<std::pair<ExPolygon, size_t>>> merge_segmented_la
|
|||
}
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
static void export_regions_to_svg(const std::string &path, const std::vector<std::pair<ExPolygon, size_t>> ®ions, const ExPolygons &lslices)
|
||||
static void export_regions_to_svg(const std::string &path, const std::vector<ExPolygons> ®ions, const ExPolygons &lslices)
|
||||
{
|
||||
const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "yellow"};
|
||||
coordf_t stroke_width = scale_(0.05);
|
||||
|
|
@ -1536,12 +1583,12 @@ static void export_regions_to_svg(const std::string &path, const std::vector<std
|
|||
::Slic3r::SVG svg(path.c_str(), bbox);
|
||||
|
||||
svg.draw_outline(lslices, "green", "lime", stroke_width);
|
||||
for (const std::pair<ExPolygon, size_t> ®ion : regions) {
|
||||
int region_color = int(region.second);
|
||||
if (region_color >= 0 && region_color < int(colors.size()))
|
||||
svg.draw(region.first, colors[region_color]);
|
||||
for (const ExPolygons &by_extruder : regions) {
|
||||
size_t extrude_idx = &by_extruder - ®ions.front();
|
||||
if (extrude_idx >= 0 && extrude_idx < int(colors.size()))
|
||||
svg.draw(by_extruder, colors[extrude_idx], stroke_width);
|
||||
else
|
||||
svg.draw(region.first, "black");
|
||||
svg.draw(by_extruder, "black", stroke_width);
|
||||
}
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
|
|
@ -1582,6 +1629,39 @@ void export_processed_input_expolygons_to_svg(const std::string &path, const Lay
|
|||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_INPUT
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_PAINTED_LINES
|
||||
static void export_painted_lines_to_svg(const std::string &path, const std::vector<std::vector<PaintedLine>> &all_painted_lines, const ExPolygons &lslices)
|
||||
{
|
||||
const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "yellow"};
|
||||
coordf_t stroke_width = scale_(0.05);
|
||||
BoundingBox bbox = get_extents(lslices);
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG svg(path.c_str(), bbox);
|
||||
|
||||
for (const Line &line : to_lines(lslices))
|
||||
svg.draw(line, "green", stroke_width);
|
||||
|
||||
for (const std::vector<PaintedLine> &painted_lines : all_painted_lines)
|
||||
for (const PaintedLine &painted_line : painted_lines)
|
||||
svg.draw(painted_line.projected_line, painted_line.color < int(colors.size()) ? colors[painted_line.color] : "black", stroke_width);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_PAINTED_LINES
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_COLORIZED_POLYGONS
|
||||
static void export_colorized_polygons_to_svg(const std::string &path, const std::vector<std::vector<ColoredLine>> &colorized_polygons, const ExPolygons &lslices)
|
||||
{
|
||||
const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "green", "yellow"};
|
||||
coordf_t stroke_width = scale_(0.05);
|
||||
BoundingBox bbox = get_extents(lslices);
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG svg(path.c_str(), bbox);
|
||||
|
||||
for (const std::vector<ColoredLine> &colorized_polygon : colorized_polygons)
|
||||
for (const ColoredLine &colorized_line : colorized_polygon)
|
||||
svg.draw(colorized_line.line, colorized_line.color < int(colors.size())? colors[colorized_line.color] : "black", stroke_width);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_COLORIZED_POLYGONS
|
||||
|
||||
// Check if all ColoredLine representing a single layer uses the same color.
|
||||
static bool has_layer_only_one_color(const std::vector<std::vector<ColoredLine>> &colored_polygons)
|
||||
{
|
||||
|
|
@ -1596,20 +1676,23 @@ static bool has_layer_only_one_color(const std::vector<std::vector<ColoredLine>>
|
|||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
|
||||
std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> segmented_regions(print_object.layers().size());
|
||||
std::vector<std::vector<PaintedLine>> painted_lines(print_object.layers().size());
|
||||
std::array<std::mutex, 64> painted_lines_mutex;
|
||||
std::vector<EdgeGrid::Grid> edge_grids(print_object.layers().size());
|
||||
const ConstLayerPtrsAdaptor layers = print_object.layers();
|
||||
std::vector<ExPolygons> input_expolygons(layers.size());
|
||||
const size_t num_extruders = print_object.print()->config().nozzle_diameter.size();
|
||||
const size_t num_layers = print_object.layers().size();
|
||||
std::vector<std::vector<ExPolygons>> segmented_regions(num_layers);
|
||||
segmented_regions.assign(num_layers, std::vector<ExPolygons>(num_extruders + 1));
|
||||
std::vector<std::vector<PaintedLine>> painted_lines(num_layers);
|
||||
std::array<std::mutex, 64> painted_lines_mutex;
|
||||
std::vector<EdgeGrid::Grid> edge_grids(num_layers);
|
||||
const ConstLayerPtrsAdaptor layers = print_object.layers();
|
||||
std::vector<ExPolygons> input_expolygons(num_layers);
|
||||
|
||||
throw_on_cancel_callback();
|
||||
|
||||
// Merge all regions and remove small holes
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - slices preparation in parallel - begin";
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()), [&layers, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers), [&layers, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
ExPolygons ex_polygons;
|
||||
|
|
@ -1640,7 +1723,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
}); // end of parallel_for
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - slices preparation in parallel - end";
|
||||
|
||||
for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) {
|
||||
for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
BoundingBox bbox(get_extents(layers[layer_idx]->regions()));
|
||||
bbox.merge(get_extents(input_expolygons[layer_idx]));
|
||||
|
|
@ -1652,8 +1735,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - projection of painted triangles - begin";
|
||||
for (const ModelVolume *mv : print_object.model_object()->volumes) {
|
||||
const size_t num_extruders = print_object.print()->config().nozzle_diameter.size() + 1;
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(1, num_extruders), [&mv, &print_object, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(1, num_extruders + 1), [&mv, &print_object, &layers, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t extruder_idx = range.begin(); extruder_idx < range.end(); ++extruder_idx) {
|
||||
throw_on_cancel_callback();
|
||||
const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx));
|
||||
|
|
@ -1661,7 +1743,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
continue;
|
||||
|
||||
const Transform3f tr = print_object.trafo().cast<float>() * mv->get_matrix().cast<float>();
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, custom_facets.indices.size()), [&tr, &custom_facets, &print_object, &edge_grids, &input_expolygons, &painted_lines, &painted_lines_mutex, &extruder_idx](const tbb::blocked_range<size_t> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, custom_facets.indices.size()), [&tr, &custom_facets, &print_object, &layers, &edge_grids, &input_expolygons, &painted_lines, &painted_lines_mutex, &extruder_idx](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t facet_idx = range.begin(); facet_idx < range.end(); ++facet_idx) {
|
||||
float min_z = std::numeric_limits<float>::max();
|
||||
float max_z = std::numeric_limits<float>::lowest();
|
||||
|
|
@ -1677,15 +1759,15 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); });
|
||||
|
||||
// Find lowest slice not below the triangle.
|
||||
auto first_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(min_z - EPSILON),
|
||||
auto first_layer = std::upper_bound(layers.begin(), layers.end(), float(min_z - EPSILON),
|
||||
[](float z, const Layer *l1) { return z < l1->slice_z; });
|
||||
auto last_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(max_z + EPSILON),
|
||||
auto last_layer = std::upper_bound(layers.begin(), layers.end(), float(max_z + EPSILON),
|
||||
[](float z, const Layer *l1) { return z < l1->slice_z; });
|
||||
--last_layer;
|
||||
|
||||
for (auto layer_it = first_layer; layer_it != (last_layer + 1); ++layer_it) {
|
||||
const Layer *layer = *layer_it;
|
||||
size_t layer_idx = layer_it - print_object.layers().begin();
|
||||
size_t layer_idx = layer_it - layers.begin();
|
||||
if (input_expolygons[layer_idx].empty() || facet[0].z() > layer->slice_z || layer->slice_z > facet[2].z())
|
||||
continue;
|
||||
|
||||
|
|
@ -1728,31 +1810,40 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
<< std::count_if(painted_lines.begin(), painted_lines.end(), [](const std::vector<PaintedLine> &pl) { return !pl.empty(); });
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - layers segmentation in parallel - begin";
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, print_object.layers().size()), [&edge_grids, &input_expolygons, &painted_lines, &segmented_regions, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers), [&edge_grids, &input_expolygons, &painted_lines, &segmented_regions, &num_extruders, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
auto comp = [&edge_grids, layer_idx](const PaintedLine &first, const PaintedLine &second) {
|
||||
Point first_start_p = edge_grids[layer_idx].contours()[first.contour_idx].segment_start(first.line_idx);
|
||||
return first.contour_idx < second.contour_idx ||
|
||||
(first.contour_idx == second.contour_idx &&
|
||||
(first.line_idx < second.line_idx ||
|
||||
(first.line_idx == second.line_idx &&
|
||||
((first.projected_line.a - first_start_p).cast<double>().squaredNorm() < (second.projected_line.a - first_start_p).cast<double>().squaredNorm() ||
|
||||
((first.projected_line.a - first_start_p).cast<double>().squaredNorm() == (second.projected_line.a - first_start_p).cast<double>().squaredNorm() &&
|
||||
(first.projected_line.b - first.projected_line.a).cast<double>().squaredNorm() < (second.projected_line.b - second.projected_line.a).cast<double>().squaredNorm())))));
|
||||
};
|
||||
if (!painted_lines[layer_idx].empty()) {
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_PAINTED_LINES
|
||||
{
|
||||
static int iRun = 0;
|
||||
export_painted_lines_to_svg(debug_out_path("mm-painted-lines-%d-%d.svg", layer_idx, iRun++), {painted_lines[layer_idx]}, input_expolygons[layer_idx]);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_PAINTED_LINES
|
||||
|
||||
std::sort(painted_lines[layer_idx].begin(), painted_lines[layer_idx].end(), comp);
|
||||
std::vector<PaintedLine> &painted_lines_single = painted_lines[layer_idx];
|
||||
std::vector<std::vector<PaintedLine>> post_processed_painted_lines = post_process_painted_lines(edge_grids[layer_idx].contours(), std::move(painted_lines[layer_idx]));
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_PAINTED_LINES
|
||||
{
|
||||
static int iRun = 0;
|
||||
export_painted_lines_to_svg(debug_out_path("mm-painted-lines-post-processed-%d-%d.svg", layer_idx, iRun++), post_processed_painted_lines, input_expolygons[layer_idx]);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_PAINTED_LINES
|
||||
|
||||
std::vector<std::vector<ColoredLine>> color_poly = colorize_contours(edge_grids[layer_idx].contours(), post_processed_painted_lines);
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_COLORIZED_POLYGONS
|
||||
{
|
||||
static int iRun = 0;
|
||||
export_colorized_polygons_to_svg(debug_out_path("mm-colorized_polygons-%d-%d.svg", layer_idx, iRun++), color_poly, input_expolygons[layer_idx]);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_COLORIZED_POLYGONS
|
||||
|
||||
if (!painted_lines_single.empty()) {
|
||||
std::vector<std::vector<ColoredLine>> color_poly = colorize_polygons(edge_grids[layer_idx].contours(), painted_lines_single);
|
||||
assert(!color_poly.empty());
|
||||
assert(!color_poly.front().empty());
|
||||
if (has_layer_only_one_color(color_poly)) {
|
||||
// If the whole layer is painted using the same color, it is not needed to construct a Voronoi diagram for the segmentation of this layer.
|
||||
for (const ExPolygon &ex_polygon : input_expolygons[layer_idx])
|
||||
segmented_regions[layer_idx].emplace_back(ex_polygon, size_t(color_poly.front().front().color));
|
||||
segmented_regions[layer_idx][size_t(color_poly.front().front().color)] = input_expolygons[layer_idx];
|
||||
} else {
|
||||
MMU_Graph graph = build_graph(layer_idx, color_poly);
|
||||
remove_multiple_edges_in_vertices(graph, color_poly);
|
||||
|
|
@ -1765,9 +1856,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
|
||||
std::vector<std::pair<Polygon, size_t>> segmentation = extract_colored_segments(graph);
|
||||
for (std::pair<Polygon, size_t> ®ion : segmentation)
|
||||
segmented_regions[layer_idx].emplace_back(std::move(region));
|
||||
segmented_regions[layer_idx] = extract_colored_segments(graph, num_extruders);
|
||||
}
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
|
|
@ -1787,11 +1876,11 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
throw_on_cancel_callback();
|
||||
}
|
||||
|
||||
// return segmented_regions;
|
||||
std::vector<std::vector<ExPolygons>> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object, input_expolygons, throw_on_cancel_callback);
|
||||
// The first index is extruder number (includes default extruder), and the second one is layer number
|
||||
std::vector<std::vector<ExPolygons>> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object, input_expolygons, throw_on_cancel_callback);
|
||||
throw_on_cancel_callback();
|
||||
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers), throw_on_cancel_callback);
|
||||
std::vector<std::vector<ExPolygons>> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers), num_extruders, throw_on_cancel_callback);
|
||||
throw_on_cancel_callback();
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class PrintObject;
|
|||
class ExPolygon;
|
||||
|
||||
// Returns MMU segmentation based on painting in MMU segmentation gizmo
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
|
||||
std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
|||
|
|
@ -534,6 +534,7 @@ static std::vector<std::string> s_Preset_sla_print_options {
|
|||
};
|
||||
|
||||
static std::vector<std::string> s_Preset_sla_material_options {
|
||||
"material_colour",
|
||||
"material_type",
|
||||
"initial_layer_height",
|
||||
"bottle_cost",
|
||||
|
|
|
|||
|
|
@ -556,13 +556,15 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
|
|||
auto printer_technology = printers.get_selected_preset().printer_technology();
|
||||
if (printer_technology == ptFFF && ! preferred_selection.filament.empty()) {
|
||||
std::string preferred_preset_name = get_preset_name_by_alias(Preset::Type::TYPE_FILAMENT, preferred_selection.filament);
|
||||
if (auto it = filaments.find_preset_internal(preferred_preset_name); it != filaments.end() && it->is_visible) {
|
||||
if (auto it = filaments.find_preset_internal(preferred_preset_name);
|
||||
it != filaments.end() && it->is_visible && it->is_compatible) {
|
||||
filaments.select_preset_by_name_strict(preferred_preset_name);
|
||||
this->filament_presets.front() = filaments.get_selected_preset_name();
|
||||
}
|
||||
} else if (printer_technology == ptSLA && ! preferred_selection.sla_material.empty()) {
|
||||
std::string preferred_preset_name = get_preset_name_by_alias(Preset::Type::TYPE_SLA_MATERIAL, preferred_selection.sla_material);
|
||||
if (auto it = sla_materials.find_preset_internal(preferred_preset_name); it != sla_materials.end() && it->is_visible)
|
||||
if (auto it = sla_materials.find_preset_internal(preferred_preset_name);
|
||||
it != sla_materials.end() && it->is_visible && it->is_compatible)
|
||||
sla_materials.select_preset_by_name_strict(preferred_preset_name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3163,6 +3163,13 @@ void PrintConfigDef::init_sla_params()
|
|||
|
||||
|
||||
// SLA Material settings.
|
||||
|
||||
def = this->add("material_colour", coString);
|
||||
def->label = L("Color");
|
||||
def->tooltip = L("This is only used in the Slic3r interface as a visual help.");
|
||||
def->gui_type = ConfigOptionDef::GUIType::color;
|
||||
def->set_default_value(new ConfigOptionString("#29B2B2"));
|
||||
|
||||
def = this->add("material_type", coString);
|
||||
def->label = L("SLA material type");
|
||||
def->tooltip = L("SLA material type");
|
||||
|
|
|
|||
|
|
@ -538,7 +538,7 @@ template<typename ThrowOnCancel>
|
|||
static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_cancel)
|
||||
{
|
||||
// Returns MMU segmentation based on painting in MMU segmentation gizmo
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> segmentation = multi_material_segmentation_by_painting(print_object, throw_on_cancel);
|
||||
std::vector<std::vector<ExPolygons>> segmentation = multi_material_segmentation_by_painting(print_object, throw_on_cancel);
|
||||
assert(segmentation.size() == print_object.layer_count());
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, segmentation.size(), std::max(segmentation.size() / 128, size_t(1))),
|
||||
|
|
@ -568,9 +568,7 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
|
|||
bool layer_split = false;
|
||||
for (size_t extruder_id = 0; extruder_id < num_extruders; ++ extruder_id) {
|
||||
ByExtruder ®ion = by_extruder[extruder_id];
|
||||
for (const std::pair<ExPolygon, size_t> &colored_polygon : segmentation[layer_id])
|
||||
if (colored_polygon.second == extruder_id)
|
||||
region.expolygons.emplace_back(std::move(colored_polygon.first));
|
||||
append(region.expolygons, std::move(segmentation[layer_id][extruder_id]));
|
||||
if (! region.expolygons.empty()) {
|
||||
region.bbox = get_extents(region.expolygons);
|
||||
layer_split = true;
|
||||
|
|
@ -632,6 +630,13 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
|
|||
if (mine.empty())
|
||||
break;
|
||||
}
|
||||
// Filter out unprintable polygons produced by subtraction multi-material painted regions from layerm.region().
|
||||
// ExPolygon returned from multi-material segmentation does not precisely match ExPolygons in layerm.region()
|
||||
// (because of preprocessing of the input regions in multi-material segmentation). Therefore, subtraction from
|
||||
// layerm.region() could produce a huge number of small unprintable regions for the model's base extruder.
|
||||
// This could, on some models, produce bulges with the model's base color (#7109).
|
||||
if (! mine.empty())
|
||||
mine = opening(union_ex(mine), float(scale_(5 * EPSILON)), float(scale_(5 * EPSILON)));
|
||||
if (! mine.empty()) {
|
||||
ByRegion &dst = by_region[layerm.region().print_object_region_id()];
|
||||
if (dst.expolygons.empty()) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include <numeric>
|
||||
|
||||
#include "SlicesToTriangleMesh.hpp"
|
||||
|
||||
|
|
@ -22,11 +23,16 @@ inline indexed_triangle_set wall_strip(const Polygon &poly,
|
|||
|
||||
ret.vertices.reserve(ret.vertices.size() + 2 *offs);
|
||||
|
||||
// The expression unscaled(p).cast<float>().eval() is important here
|
||||
// as it ensures identical conversion of 2D scaled coordinates to float 3D
|
||||
// to that used by the tesselation. This way, the duplicated vertices in the
|
||||
// output mesh can be found with the == operator of the points.
|
||||
// its_merge_vertices will then reliably remove the duplicates.
|
||||
for (const Point &p : poly.points)
|
||||
ret.vertices.emplace_back(to_3d(unscaled<float>(p), float(lower_z_mm)));
|
||||
ret.vertices.emplace_back(to_3d(unscaled(p).cast<float>().eval(), float(lower_z_mm)));
|
||||
|
||||
for (const Point &p : poly.points)
|
||||
ret.vertices.emplace_back(to_3d(unscaled<float>(p), float(upper_z_mm)));
|
||||
ret.vertices.emplace_back(to_3d(unscaled(p).cast<float>().eval(), float(upper_z_mm)));
|
||||
|
||||
for (size_t i = startidx + 1; i < startidx + offs; ++i) {
|
||||
ret.indices.emplace_back(i - 1, i, i + offs - 1);
|
||||
|
|
@ -84,12 +90,14 @@ indexed_triangle_set slices_to_mesh(
|
|||
const ExPolygons &upper = slices[i + 1];
|
||||
const ExPolygons &lower = slices[i];
|
||||
|
||||
ExPolygons dff1 = diff_ex(lower, upper);
|
||||
ExPolygons dff2 = diff_ex(upper, lower);
|
||||
its_merge(layers[i], triangulate_expolygons_3d(dff1, grid[i], NORMALS_UP));
|
||||
its_merge(layers[i], triangulate_expolygons_3d(dff2, grid[i], NORMALS_DOWN));
|
||||
// Small 0 area artefacts can be created by diff_ex, and the
|
||||
// tesselation also can create 0 area triangles. These will be removed
|
||||
// by its_remove_degenerate_faces.
|
||||
ExPolygons free_top = diff_ex(lower, upper);
|
||||
ExPolygons overhang = diff_ex(upper, lower);
|
||||
its_merge(layers[i], triangulate_expolygons_3d(free_top, grid[i], NORMALS_UP));
|
||||
its_merge(layers[i], triangulate_expolygons_3d(overhang, grid[i], NORMALS_DOWN));
|
||||
its_merge(layers[i], straight_walls(upper, grid[i], grid[i + 1]));
|
||||
|
||||
});
|
||||
|
||||
auto merge_fn = []( const indexed_triangle_set &a, const indexed_triangle_set &b ) {
|
||||
|
|
@ -99,37 +107,30 @@ indexed_triangle_set slices_to_mesh(
|
|||
auto ret = execution::reduce(ex_tbb, layers.begin(), layers.end(),
|
||||
indexed_triangle_set{}, merge_fn);
|
||||
|
||||
// sla::Contour3D ret = tbb::parallel_reduce(
|
||||
// tbb::blocked_range(layers.begin(), layers.end()),
|
||||
// sla::Contour3D{},
|
||||
// [](const tbb::blocked_range<Layers::iterator>& r, sla::Contour3D
|
||||
// init) {
|
||||
// for(auto it = r.begin(); it != r.end(); ++it )
|
||||
// init.merge(*it); return init;
|
||||
// },
|
||||
// []( const sla::Contour3D &a, const sla::Contour3D &b ) {
|
||||
// sla::Contour3D res{a}; res.merge(b); return res;
|
||||
// });
|
||||
|
||||
its_merge(ret, triangulate_expolygons_3d(slices.front(), zmin, NORMALS_DOWN));
|
||||
its_merge(ret, straight_walls(slices.front(), zmin, grid.front()));
|
||||
its_merge(ret, triangulate_expolygons_3d(slices.back(), grid.back(), NORMALS_UP));
|
||||
|
||||
|
||||
// FIXME: these repairs do not fix the mesh entirely. There will be cracks
|
||||
// in the output. It is very hard to do the meshing in a way that does not
|
||||
// leave errors.
|
||||
its_merge_vertices(ret);
|
||||
its_remove_degenerate_faces(ret);
|
||||
its_compactify_vertices(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void slices_to_mesh(indexed_triangle_set & mesh,
|
||||
const std::vector<ExPolygons> &slices,
|
||||
double zmin,
|
||||
double lh,
|
||||
double ilh)
|
||||
const std::vector<ExPolygons> &slices,
|
||||
double zmin,
|
||||
double lh,
|
||||
double ilh)
|
||||
{
|
||||
std::vector<indexed_triangle_set> wall_meshes(slices.size());
|
||||
std::vector<float> grid(slices.size(), zmin + ilh);
|
||||
|
||||
for (size_t i = 1; i < grid.size(); ++i)
|
||||
grid[i] = grid[i - 1] + lh;
|
||||
|
||||
|
||||
for (size_t i = 1; i < grid.size(); ++i) grid[i] = grid[i - 1] + lh;
|
||||
|
||||
indexed_triangle_set cntr = slices_to_mesh(slices, zmin, grid);
|
||||
its_merge(mesh, cntr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
namespace Slic3r {
|
||||
|
||||
void slices_to_mesh(indexed_triangle_set & mesh,
|
||||
const std::vector<ExPolygons> &slices,
|
||||
double zmin,
|
||||
double lh,
|
||||
double ilh);
|
||||
const std::vector<ExPolygons> &slices,
|
||||
double zmin,
|
||||
double lh,
|
||||
double ilh);
|
||||
|
||||
inline indexed_triangle_set slices_to_mesh(
|
||||
const std::vector<ExPolygons> &slices, double zmin, double lh, double ilh)
|
||||
|
|
|
|||
|
|
@ -74,16 +74,25 @@
|
|||
|
||||
|
||||
//====================
|
||||
// 2.4.0.alpha4 techs
|
||||
// 2.4.0.beta1 techs
|
||||
//====================
|
||||
#define ENABLE_2_4_0_ALPHA4 1
|
||||
#define ENABLE_2_4_0_BETA1 1
|
||||
|
||||
// Enable rendering modifiers and similar objects always as transparent
|
||||
#define ENABLE_MODIFIERS_ALWAYS_TRANSPARENT (1 && ENABLE_2_4_0_ALPHA4)
|
||||
|
||||
#define ENABLE_MODIFIERS_ALWAYS_TRANSPARENT (1 && ENABLE_2_4_0_BETA1)
|
||||
// Enable the fix for the detection of the out of bed state for sinking objects
|
||||
// and detection of out of bed using the bed perimeter
|
||||
#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_2_4_0_ALPHA4)
|
||||
#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_2_4_0_BETA1)
|
||||
|
||||
|
||||
//====================
|
||||
// 2.4.0.beta2 techs
|
||||
//====================
|
||||
#define ENABLE_2_4_0_BETA2 1
|
||||
|
||||
// Enable modified ImGuiWrapper::slider_float() to create a compound widget where
|
||||
// an additional button can be used to set the keyboard focus into the slider
|
||||
// to allow the user to type in the desired value
|
||||
#define ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT (1 && ENABLE_2_4_0_BETA2)
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
|
|||
|
|
@ -705,22 +705,16 @@ void its_flip_triangles(indexed_triangle_set &its)
|
|||
|
||||
int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit)
|
||||
{
|
||||
int last = 0;
|
||||
for (int i = 0; i < int(its.indices.size()); ++ i) {
|
||||
const stl_triangle_vertex_indices &face = its.indices[i];
|
||||
if (face(0) != face(1) && face(0) != face(2) && face(1) != face(2)) {
|
||||
if (last < i)
|
||||
its.indices[last] = its.indices[i];
|
||||
++ last;
|
||||
}
|
||||
}
|
||||
int removed = int(its.indices.size()) - last;
|
||||
if (removed) {
|
||||
its.indices.erase(its.indices.begin() + last, its.indices.end());
|
||||
// Optionally shrink the vertices.
|
||||
if (shrink_to_fit)
|
||||
its.indices.shrink_to_fit();
|
||||
}
|
||||
auto it = std::remove_if(its.indices.begin(), its.indices.end(), [](auto &face) {
|
||||
return face(0) == face(1) || face(0) == face(2) || face(1) == face(2);
|
||||
});
|
||||
|
||||
int removed = std::distance(it, its.indices.end());
|
||||
its.indices.erase(it, its.indices.end());
|
||||
|
||||
if (removed && shrink_to_fit)
|
||||
its.indices.shrink_to_fit();
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
|
|
@ -1196,11 +1190,11 @@ std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its)
|
|||
}
|
||||
|
||||
// Number of disconnected patches (faces are connected if they share an edge, shared edge defined with 2 shared vertex indices).
|
||||
bool its_number_of_patches(const indexed_triangle_set &its)
|
||||
size_t its_number_of_patches(const indexed_triangle_set &its)
|
||||
{
|
||||
return its_number_of_patches<>(its);
|
||||
}
|
||||
bool its_number_of_patches(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors)
|
||||
size_t its_number_of_patches(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors)
|
||||
{
|
||||
return its_number_of_patches<>(ItsNeighborsWrapper{ its, face_neighbors });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -219,8 +219,8 @@ std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its);
|
|||
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors);
|
||||
|
||||
// Number of disconnected patches (faces are connected if they share an edge, shared edge defined with 2 shared vertex indices).
|
||||
bool its_number_of_patches(const indexed_triangle_set &its);
|
||||
bool its_number_of_patches(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors);
|
||||
size_t its_number_of_patches(const indexed_triangle_set &its);
|
||||
size_t its_number_of_patches(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors);
|
||||
// Same as its_number_of_patches(its) > 1, but faster.
|
||||
bool its_is_splittable(const indexed_triangle_set &its);
|
||||
bool its_is_splittable(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors);
|
||||
|
|
|
|||
|
|
@ -165,6 +165,11 @@ typedef struct NSVGimage
|
|||
// Parses SVG file from a file, returns SVG image as paths.
|
||||
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
|
||||
|
||||
// Parses SVG file from a file, returns SVG image as paths.
|
||||
// And makes replases befor parsing
|
||||
// replace_map containes old_value->new_value
|
||||
NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replace_map);
|
||||
|
||||
// Parses SVG file from a null terminated string, returns SVG image as paths.
|
||||
// Important note: changes the string.
|
||||
NSVGimage* nsvgParse(char* input, const char* units, float dpi);
|
||||
|
|
@ -189,6 +194,8 @@ void nsvgDelete(NSVGimage* image);
|
|||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#define NSVG_PI (3.14159265358979323846264338327f)
|
||||
#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs.
|
||||
|
||||
|
|
@ -2901,6 +2908,12 @@ NSVGimage* nsvgParse(char* input, const char* units, float dpi)
|
|||
|
||||
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
|
||||
{
|
||||
return nsvgParseFromFileWithReplace(filename, units, dpi, { {} });
|
||||
}
|
||||
|
||||
NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replaces)
|
||||
{
|
||||
std::string str;
|
||||
FILE* fp = NULL;
|
||||
size_t size;
|
||||
char* data = NULL;
|
||||
|
|
@ -2916,9 +2929,16 @@ NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
|
|||
if (fread(data, 1, size, fp) != size) goto error;
|
||||
data[size] = '\0'; // Must be null terminated.
|
||||
fclose(fp);
|
||||
image = nsvgParse(data, units, dpi);
|
||||
free(data);
|
||||
|
||||
if(replaces.empty())
|
||||
image = nsvgParse(data, units, dpi);
|
||||
else {
|
||||
str.assign(data);
|
||||
for (auto val : replaces)
|
||||
boost::replace_all(str, val.first, val.second);
|
||||
image = nsvgParse(str.data(), units, dpi);
|
||||
}
|
||||
free(data);
|
||||
return image;
|
||||
|
||||
error:
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "../GUI/GUI_App.hpp"
|
||||
#include "../GUI/I18N.hpp"
|
||||
#include "../GUI/MainFrame.hpp"
|
||||
#include "../GUI/MsgDialog.hpp"
|
||||
|
||||
#include <wx/richmsgdlg.h>
|
||||
|
||||
|
|
@ -591,7 +592,7 @@ bool take_config_snapshot_cancel_on_error(const AppConfig &app_config, Snapshot:
|
|||
SnapshotDB::singleton().take_snapshot(app_config, reason, comment);
|
||||
return true;
|
||||
} catch (std::exception &err) {
|
||||
wxRichMessageDialog dlg(static_cast<wxWindow*>(wxGetApp().mainframe),
|
||||
RichMessageDialog dlg(static_cast<wxWindow*>(wxGetApp().mainframe),
|
||||
_L("PrusaSlicer has encountered an error while taking a configuration snapshot.") + "\n\n" + from_u8(err.what()) + "\n\n" + from_u8(message),
|
||||
_L("PrusaSlicer error"),
|
||||
wxYES_NO);
|
||||
|
|
|
|||
|
|
@ -1131,29 +1131,41 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
|
|||
if (config == nullptr)
|
||||
return;
|
||||
|
||||
const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("extruder_colour"));
|
||||
if (extruders_opt == nullptr)
|
||||
return;
|
||||
|
||||
const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("filament_colour"));
|
||||
if (filamemts_opt == nullptr)
|
||||
return;
|
||||
|
||||
unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size());
|
||||
if (colors_count == 0)
|
||||
return;
|
||||
|
||||
std::vector<Color> colors(colors_count);
|
||||
|
||||
unsigned char rgb[3];
|
||||
for (unsigned int i = 0; i < colors_count; ++i) {
|
||||
const std::string& txt_color = config->opt_string("extruder_colour", i);
|
||||
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
|
||||
colors[i].set(txt_color, rgb);
|
||||
else {
|
||||
const std::string& txt_color = config->opt_string("filament_colour", i);
|
||||
std::vector<Color> colors;
|
||||
|
||||
if (static_cast<PrinterTechnology>(config->opt_int("printer_technology")) == ptSLA)
|
||||
{
|
||||
const std::string& txt_color = config->opt_string("material_colour");
|
||||
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) {
|
||||
colors.resize(1);
|
||||
colors[0].set(txt_color, rgb);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("extruder_colour"));
|
||||
if (extruders_opt == nullptr)
|
||||
return;
|
||||
|
||||
const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("filament_colour"));
|
||||
if (filamemts_opt == nullptr)
|
||||
return;
|
||||
|
||||
unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size());
|
||||
if (colors_count == 0)
|
||||
return;
|
||||
colors.resize(colors_count);
|
||||
|
||||
for (unsigned int i = 0; i < colors_count; ++i) {
|
||||
const std::string& txt_color = config->opt_string("extruder_colour", i);
|
||||
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
|
||||
colors[i].set(txt_color, rgb);
|
||||
else {
|
||||
const std::string& txt_color = config->opt_string("filament_colour", i);
|
||||
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
|
||||
colors[i].set(txt_color, rgb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -261,51 +261,28 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width,
|
|||
}
|
||||
|
||||
wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height,
|
||||
const bool grayscale/* = false*/, const bool dark_mode/* = false*/)
|
||||
const bool grayscale/* = false*/, const bool dark_mode/* = false*/, const std::string& new_color /*= ""*/)
|
||||
{
|
||||
std::string bitmap_key = bitmap_name + ( target_height !=0 ?
|
||||
"-h" + std::to_string(target_height) :
|
||||
"-w" + std::to_string(target_width))
|
||||
+ (m_scale != 1.0f ? "-s" + float_to_string_decimal_point(m_scale) : "")
|
||||
+ (grayscale ? "-gs" : "");
|
||||
+ (dark_mode ? "-dm" : "")
|
||||
+ (grayscale ? "-gs" : "")
|
||||
+ new_color;
|
||||
|
||||
/* For the Dark mode of any platform, we should draw icons in respect to OS background
|
||||
* Note: All standard(regular) icons are collected in "icons" folder,
|
||||
* SVG-icons, which have "Dark mode" variant, are collected in "icons/white" folder
|
||||
*/
|
||||
std::string folder;
|
||||
auto it = m_map.find(bitmap_key);
|
||||
if (it != m_map.end())
|
||||
return it->second;
|
||||
|
||||
// map of color replaces
|
||||
std::map<std::string, std::string> replaces;
|
||||
if (dark_mode)
|
||||
{
|
||||
#ifdef __WXMSW__
|
||||
folder = "white\\";
|
||||
#else
|
||||
folder = "white/";
|
||||
#endif
|
||||
auto it = m_map.find(folder + bitmap_key);
|
||||
if (it != m_map.end())
|
||||
return it->second;
|
||||
// It's expensive to check if the bitmap exists every time, but otherwise:
|
||||
// For the case, when application was started in Light mode and then switched to the Dark,
|
||||
// we will never get a white bitmaps, if check m_map.find(bitmap_key)
|
||||
// before boost::filesystem::exists(var(folder + bitmap_name + ".svg"))
|
||||
if (!boost::filesystem::exists(var(folder + bitmap_name + ".svg"))) {
|
||||
folder.clear();
|
||||
|
||||
it = m_map.find(bitmap_key);
|
||||
if (it != m_map.end())
|
||||
return it->second;
|
||||
}
|
||||
replaces["#808080"] = "#FFFFFF";
|
||||
if (!new_color.empty())
|
||||
replaces["#ED6B21"] = new_color;
|
||||
|
||||
bitmap_key = folder + bitmap_key;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto it = m_map.find(bitmap_key);
|
||||
if (it != m_map.end())
|
||||
return it->second;
|
||||
}
|
||||
|
||||
NSVGimage *image = ::nsvgParseFromFile(Slic3r::var(folder + bitmap_name + ".svg").c_str(), "px", 96.0f);
|
||||
NSVGimage *image = ::nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, replaces);
|
||||
if (image == nullptr)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public:
|
|||
// Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero.
|
||||
wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false);
|
||||
// Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width.
|
||||
wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false);
|
||||
wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false, const std::string& new_color = "");
|
||||
|
||||
wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false);
|
||||
wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3], bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); }
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ struct LifetimeGuard
|
|||
|
||||
BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
|
||||
: wxDialog(parent, wxID_ANY, _(L("Network lookup")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
|
||||
, list(new wxListView(this, wxID_ANY))
|
||||
, list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxSIMPLE_BORDER))
|
||||
, replies(new ReplySet)
|
||||
, label(new wxStaticText(this, wxID_ANY, ""))
|
||||
, timer(new wxTimer())
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@
|
|||
#include <wx/wupdlock.h>
|
||||
#include <wx/debug.h>
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
#include <wx/msw/dark_mode.h>
|
||||
#endif // _MSW_DARK_MODE
|
||||
|
||||
#include "libslic3r/Platform.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
|
|
@ -551,7 +555,7 @@ PagePrinters::PagePrinters(ConfigWizard *parent,
|
|||
wizard_p()->on_printer_pick(this, evt);
|
||||
});
|
||||
|
||||
append(new wxStaticLine(this));
|
||||
append(new StaticLine(this));
|
||||
|
||||
append(picker);
|
||||
printer_pickers.push_back(picker);
|
||||
|
|
@ -1202,7 +1206,7 @@ PageUpdate::PageUpdate(ConfigWizard *parent)
|
|||
boldfont.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
|
||||
auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _L("Check for application updates"));
|
||||
box_slic3r->SetValue(app_config->get("version_check") == "1");
|
||||
box_slic3r->SetValue(app_config->get("notify_release") != "none");
|
||||
append(box_slic3r);
|
||||
append_text(wxString::Format(_L(
|
||||
"If enabled, %s checks for new application versions online. When a new version becomes available, "
|
||||
|
|
@ -2693,7 +2697,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||
|
||||
app_config->set_vendors(appconfig_new);
|
||||
|
||||
app_config->set("version_check", page_update->version_check ? "1" : "0");
|
||||
app_config->set("notify_release", page_update->version_check ? "all" : "none");
|
||||
app_config->set("preset_update", page_update->preset_update ? "1" : "0");
|
||||
app_config->set("export_sources_full_pathnames", page_reload_from_disk->full_pathnames ? "1" : "0");
|
||||
|
||||
|
|
@ -2796,7 +2800,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
|||
|
||||
auto *vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *hline = new wxStaticLine(this);
|
||||
auto* hline = new StaticLine(this);
|
||||
p->btnsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling.
|
||||
|
|
@ -2872,7 +2876,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
|||
p->index->go_to(size_t{0});
|
||||
|
||||
vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN);
|
||||
vsizer->Add(hline, 0, wxEXPAND);
|
||||
vsizer->Add(hline, 0, wxEXPAND | wxLEFT | wxRIGHT, VERTICAL_SPACING);
|
||||
vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN);
|
||||
|
||||
SetSizer(vsizer);
|
||||
|
|
|
|||
|
|
@ -1320,7 +1320,7 @@ void ColourPicker::BUILD()
|
|||
if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit);
|
||||
|
||||
// Validate the color
|
||||
wxString clr_str(m_opt.get_default_value<ConfigOptionStrings>()->get_at(m_opt_idx));
|
||||
wxString clr_str(m_opt.type == coString ? m_opt.get_default_value<ConfigOptionString>()->value : m_opt.get_default_value<ConfigOptionStrings>()->get_at(m_opt_idx));
|
||||
wxColour clr(clr_str);
|
||||
if (clr_str.IsEmpty() || !clr.IsOk()) {
|
||||
clr = wxTransparentColour;
|
||||
|
|
|
|||
|
|
@ -286,8 +286,12 @@ void GCodeViewer::SequentialView::Marker::render() const
|
|||
if (width != last_window_width || length != last_text_length) {
|
||||
last_window_width = width;
|
||||
last_text_length = length;
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
imgui.set_requires_extra_frame();
|
||||
#else
|
||||
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
|
||||
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
}
|
||||
|
||||
imgui.end();
|
||||
|
|
@ -3379,8 +3383,12 @@ void GCodeViewer::render_legend(float& legend_height)
|
|||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f);
|
||||
|
||||
// to avoid the tooltip to change size when moving the mouse
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
imgui.set_requires_extra_frame();
|
||||
#else
|
||||
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
|
||||
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4115,8 +4123,12 @@ void GCodeViewer::render_legend(float& legend_height)
|
|||
if (can_show_mode_button(mode)) {
|
||||
if (imgui.button(label)) {
|
||||
m_time_estimate_mode = mode;
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
imgui.set_requires_extra_frame();
|
||||
#else
|
||||
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
|
||||
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -722,8 +722,13 @@ void GLCanvas3D::Labels::render(const std::vector<const ModelInstance*>& sorted_
|
|||
}
|
||||
|
||||
// force re-render while the windows gets to its final size (it takes several frames)
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (ImGui::GetWindowContentRegionWidth() + 2.0f * ImGui::GetStyle().WindowPadding.x != ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x)
|
||||
imgui.set_requires_extra_frame();
|
||||
#else
|
||||
if (ImGui::GetWindowContentRegionWidth() + 2.0f * ImGui::GetStyle().WindowPadding.x != ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x)
|
||||
m_canvas.request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
imgui.end();
|
||||
ImGui::PopStyleColor();
|
||||
|
|
@ -734,47 +739,48 @@ void GLCanvas3D::Labels::render(const std::vector<const ModelInstance*>& sorted_
|
|||
void GLCanvas3D::Tooltip::set_text(const std::string& text)
|
||||
{
|
||||
// If the mouse is inside an ImGUI dialog, then the tooltip is suppressed.
|
||||
const std::string &new_text = m_in_imgui ? std::string() : text;
|
||||
if (m_text != new_text) {
|
||||
if (m_text.empty())
|
||||
m_start_time = std::chrono::steady_clock::now();
|
||||
|
||||
m_text = new_text;
|
||||
}
|
||||
m_text = m_in_imgui ? std::string() : text;
|
||||
}
|
||||
|
||||
void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas) const
|
||||
void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas)
|
||||
{
|
||||
static ImVec2 size(0.0f, 0.0f);
|
||||
|
||||
auto validate_position = [](const Vec2d& position, const GLCanvas3D& canvas, const ImVec2& wnd_size) {
|
||||
Size cnv_size = canvas.get_canvas_size();
|
||||
float x = std::clamp((float)position(0), 0.0f, (float)cnv_size.get_width() - wnd_size.x);
|
||||
float y = std::clamp((float)position(1) + 16, 0.0f, (float)cnv_size.get_height() - wnd_size.y);
|
||||
const Size cnv_size = canvas.get_canvas_size();
|
||||
const float x = std::clamp((float)position.x(), 0.0f, (float)cnv_size.get_width() - wnd_size.x);
|
||||
const float y = std::clamp((float)position.y() + 16.0f, 0.0f, (float)cnv_size.get_height() - wnd_size.y);
|
||||
return Vec2f(x, y);
|
||||
};
|
||||
|
||||
if (m_text.empty())
|
||||
if (m_text.empty()) {
|
||||
m_start_time = std::chrono::steady_clock::now();
|
||||
return;
|
||||
}
|
||||
|
||||
// draw the tooltip as hidden until the delay is expired
|
||||
// use a value of alpha slightly different from 0.0f because newer imgui does not calculate properly the window size if alpha == 0.0f
|
||||
float alpha = (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_start_time).count() < 500) ? 0.01f : 1.0f;
|
||||
const float alpha = (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_start_time).count() < 500) ? 0.01f : 1.0f;
|
||||
|
||||
Vec2f position = validate_position(mouse_position, canvas, size);
|
||||
const Vec2f position = validate_position(mouse_position, canvas, size);
|
||||
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
|
||||
imgui.set_next_window_pos(position(0), position(1), ImGuiCond_Always, 0.0f, 0.0f);
|
||||
imgui.set_next_window_pos(position.x(), position.y(), ImGuiCond_Always, 0.0f, 0.0f);
|
||||
|
||||
imgui.begin(wxString("canvas_tooltip"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoFocusOnAppearing);
|
||||
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow());
|
||||
ImGui::TextUnformatted(m_text.c_str());
|
||||
|
||||
// force re-render while the windows gets to its final size (it may take several frames) or while hidden
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (alpha < 1.0f || ImGui::GetWindowContentRegionWidth() + 2.0f * ImGui::GetStyle().WindowPadding.x != ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x)
|
||||
imgui.set_requires_extra_frame();
|
||||
#else
|
||||
if (alpha < 1.0f || ImGui::GetWindowContentRegionWidth() + 2.0f * ImGui::GetStyle().WindowPadding.x != ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x)
|
||||
canvas.request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
size = ImGui::GetWindowSize();
|
||||
|
||||
|
|
@ -2250,14 +2256,29 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
|||
m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this);
|
||||
auto gizmo = wxGetApp().plater()->canvas3D()->get_gizmos_manager().get_current();
|
||||
if (gizmo != nullptr) m_dirty |= gizmo->update_items_state();
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
// ImGuiWrapper::m_requires_extra_frame may have been set by a render made outside of the OnIdle mechanism
|
||||
bool imgui_requires_extra_frame = wxGetApp().imgui()->requires_extra_frame();
|
||||
m_dirty |= imgui_requires_extra_frame;
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
if (!m_dirty)
|
||||
return;
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
// this needs to be done here.
|
||||
// during the render launched by the refresh the value may be set again
|
||||
wxGetApp().imgui()->reset_requires_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
_refresh_if_shown_on_screen();
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_extra_frame_requested || mouse3d_controller_applied || imgui_requires_extra_frame || wxGetApp().imgui()->requires_extra_frame()) {
|
||||
#else
|
||||
if (m_extra_frame_requested || mouse3d_controller_applied) {
|
||||
m_dirty = true;
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_extra_frame_requested = false;
|
||||
evt.RequestMore();
|
||||
}
|
||||
|
|
@ -4176,7 +4197,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
|
|||
shader->set_uniform("emission_factor", 0.0f);
|
||||
|
||||
for (GLVolume* vol : visible_volumes) {
|
||||
shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray);
|
||||
shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? (current_printer_technology() == ptSLA ? vol->color : orange) : gray);
|
||||
// the volume may have been deactivated by an active gizmo
|
||||
bool is_active = vol->is_active;
|
||||
vol->is_active = true;
|
||||
|
|
|
|||
|
|
@ -390,7 +390,7 @@ class GLCanvas3D
|
|||
public:
|
||||
bool is_empty() const { return m_text.empty(); }
|
||||
void set_text(const std::string& text);
|
||||
void render(const Vec2d& mouse_position, GLCanvas3D& canvas) const;
|
||||
void render(const Vec2d& mouse_position, GLCanvas3D& canvas);
|
||||
// Indicates that the mouse is inside an ImGUI dialog, therefore the tooltip should be suppressed.
|
||||
void set_in_imgui(bool b) { m_in_imgui = b; }
|
||||
bool is_in_imgui() const { return m_in_imgui; }
|
||||
|
|
|
|||
|
|
@ -79,7 +79,9 @@ std::pair<bool, std::string> GLShadersManager::init()
|
|||
// For Apple's on Arm CPU computed triangle normals inside fragment shader using dFdx and dFdy has the opposite direction.
|
||||
// Because of this, objects had darker colors inside the multi-material gizmo.
|
||||
// Based on https://stackoverflow.com/a/66206648, the similar behavior was also spotted on some other devices with Arm CPU.
|
||||
if (platform_flavor() == PlatformFlavor::OSXOnArm)
|
||||
// Since macOS 12 (Monterey), this issue with the opposite direction on Apple's Arm CPU seems to be fixed, and computed
|
||||
// triangle normals inside fragment shader have the right direction.
|
||||
if (platform_flavor() == PlatformFlavor::OSXOnArm && wxPlatformInfo::Get().GetOSMajorVersion() < 12)
|
||||
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"}, {"FLIP_TRIANGLE_NORMALS"sv});
|
||||
else
|
||||
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "GUI_Init.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "GUI_ObjectManipulation.hpp"
|
||||
#include "GUI_Factories.hpp"
|
||||
#include "format.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
|
|
@ -82,6 +83,9 @@
|
|||
#include <wx/msw/dark_mode.h>
|
||||
#endif // _MSW_DARK_MODE
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#include <boost/dll/runtime_symbol_info.hpp>
|
||||
#endif
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
|
||||
#include <boost/beast/core/detail/base64.hpp>
|
||||
|
|
@ -410,7 +414,7 @@ bool static check_old_linux_datadir(const wxString& app_name) {
|
|||
"location again.\n\n"
|
||||
"What do you want to do now?")) % SLIC3R_APP_NAME % new_path % old_path).str());
|
||||
wxString caption = from_u8((boost::format(_u8L("%s - BREAKING CHANGE")) % SLIC3R_APP_NAME).str());
|
||||
wxRichMessageDialog dlg(nullptr, msg, caption, wxYES_NO);
|
||||
RichMessageDialog dlg(nullptr, msg, caption, wxYES_NO);
|
||||
dlg.SetYesNoLabels(_L("Quit, I will move my data now"), _L("Start the application"));
|
||||
if (dlg.ShowModal() != wxID_NO)
|
||||
return false;
|
||||
|
|
@ -423,6 +427,56 @@ bool static check_old_linux_datadir(const wxString& app_name) {
|
|||
#endif
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
static bool run_updater_win()
|
||||
{
|
||||
// find updater exe
|
||||
boost::filesystem::path path_to_binary = boost::dll::program_location();
|
||||
for (const auto& dir_entry : boost::filesystem::directory_iterator(path_to_binary.parent_path())) {
|
||||
if (dir_entry.path().filename() == "prusaslicer-updater.exe") {
|
||||
// run updater. Original args: /silent -restartapp prusa-slicer.exe -startappfirst
|
||||
|
||||
// Using quoted string as mentioned in CreateProcessW docs.
|
||||
std::wstring wcmd = L"\"" + dir_entry.path().wstring() + L"\"";
|
||||
wcmd += L" /silent";
|
||||
|
||||
// additional information
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
// set the size of the structures
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
// start the program up
|
||||
if (CreateProcessW(NULL, // the path
|
||||
wcmd.data(), // Command line
|
||||
NULL, // Process handle not inheritable
|
||||
NULL, // Thread handle not inheritable
|
||||
FALSE, // Set handle inheritance to FALSE
|
||||
0, // No creation flags
|
||||
NULL, // Use parent's environment block
|
||||
NULL, // Use parent's starting directory
|
||||
&si, // Pointer to STARTUPINFO structure
|
||||
&pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses)
|
||||
)) {
|
||||
// Close process and thread handles.
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
return true;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to start prusaslicer-updater.exe with command " << wcmd;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif //_WIN32
|
||||
|
||||
|
||||
|
||||
wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
||||
{
|
||||
static const std::string defaults[FT_SIZE] = {
|
||||
|
|
@ -677,7 +731,6 @@ void GUI_App::post_init()
|
|||
this->check_updates(false);
|
||||
CallAfter([this] {
|
||||
bool cw_showed = this->config_wizard_startup();
|
||||
this->preset_updater->slic3r_update_notify();
|
||||
this->preset_updater->sync(preset_bundle);
|
||||
if (! cw_showed) {
|
||||
// The CallAfter is needed as well, without it, GL extensions did not show.
|
||||
|
|
@ -685,6 +738,15 @@ void GUI_App::post_init()
|
|||
// sees something else than "we want something" on the first start.
|
||||
show_send_system_info_dialog_if_needed();
|
||||
}
|
||||
bool updater_running =
|
||||
#ifdef _WIN32
|
||||
// Run external updater on Windows.
|
||||
run_updater_win();
|
||||
#else
|
||||
false;
|
||||
#endif // _WIN32
|
||||
if (!updater_running)
|
||||
this->preset_updater->slic3r_update_notify();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -845,7 +907,7 @@ bool GUI_App::check_older_app_config(Semver current_version, bool backup)
|
|||
return false;
|
||||
BOOST_LOG_TRIVIAL(info) << "last app config file used: " << m_older_data_dir_path;
|
||||
// ask about using older data folder
|
||||
wxRichMessageDialog msg(nullptr, backup ?
|
||||
RichMessageDialog msg(nullptr, backup ?
|
||||
wxString::Format(_L("PrusaSlicer detected another configuration folder at %s."
|
||||
"\nIts version is %s."
|
||||
"\nLast version you used in current configuration folder is %s."
|
||||
|
|
@ -864,7 +926,7 @@ bool GUI_App::check_older_app_config(Semver current_version, bool backup)
|
|||
"\n\nIf you select yes, PrusaSlicer will copy all profiles and other files from found folder to the current one."
|
||||
"\nIf you select no, you will start with clean installation with configuration wizard.")
|
||||
, m_older_data_dir_path, last_semver.to_string())
|
||||
, _L("PrusaSlicer"), wxICON_QUESTION | wxYES_NO);
|
||||
, _L("PrusaSlicer"), /*wxICON_QUESTION | */wxYES_NO);
|
||||
if (msg.ShowModal() == wxID_YES) {
|
||||
std::string snapshot_id;
|
||||
if (backup) {
|
||||
|
|
@ -935,7 +997,7 @@ bool GUI_App::on_init_inner()
|
|||
// win32 build on win64 and viceversa
|
||||
#ifdef _WIN64
|
||||
if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "") {
|
||||
wxRichMessageDialog dlg(nullptr,
|
||||
RichMessageDialog dlg(nullptr,
|
||||
_L("You have started PrusaSlicer for 64-bit architecture on 32-bit system."
|
||||
"\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/."
|
||||
"\nDo you wish to continue?"),
|
||||
|
|
@ -945,7 +1007,7 @@ bool GUI_App::on_init_inner()
|
|||
}
|
||||
#elif _WIN32
|
||||
if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "64") {
|
||||
wxRichMessageDialog dlg(nullptr,
|
||||
RichMessageDialog dlg(nullptr,
|
||||
_L("You have started PrusaSlicer for 32-bit architecture on 64-bit system."
|
||||
"\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/."
|
||||
"\nDo you wish to continue?"),
|
||||
|
|
@ -990,7 +1052,7 @@ bool GUI_App::on_init_inner()
|
|||
bool ssl_accept = app_config->get("tls_cert_store_accepted") == "yes" && ssl_cert_store == Http::tls_system_cert_store();
|
||||
|
||||
if (!msg.empty() && !ssl_accept) {
|
||||
wxRichMessageDialog
|
||||
RichMessageDialog
|
||||
dlg(nullptr,
|
||||
wxString::Format(_L("%s\nDo you want to continue?"), msg),
|
||||
"PrusaSlicer", wxICON_QUESTION | wxYES_NO);
|
||||
|
|
@ -1020,11 +1082,7 @@ bool GUI_App::on_init_inner()
|
|||
wxInitAllImageHandlers();
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
if (bool dark_mode = app_config->get("dark_color_mode") == "1") {
|
||||
NppDarkMode::InitDarkMode();
|
||||
if (dark_mode != NppDarkMode::IsDarkMode())
|
||||
NppDarkMode::SetDarkMode(dark_mode);
|
||||
}
|
||||
NppDarkMode::InitDarkMode(app_config->get("dark_color_mode") == "1", app_config->get("sys_menu_enabled") == "1");
|
||||
#endif
|
||||
SplashScreen* scrn = nullptr;
|
||||
if (app_config->get("show_splash_screen") == "1") {
|
||||
|
|
@ -1073,26 +1131,30 @@ bool GUI_App::on_init_inner()
|
|||
Bind(EVT_SLIC3R_VERSION_ONLINE, [this](const wxCommandEvent& evt) {
|
||||
app_config->set("version_online", into_u8(evt.GetString()));
|
||||
app_config->save();
|
||||
if (this->plater_ != nullptr) {
|
||||
std::string opt = app_config->get("notify_release");
|
||||
if (this->plater_ != nullptr && (opt == "all" || opt == "release")) {
|
||||
if (*Semver::parse(SLIC3R_VERSION) < *Semver::parse(into_u8(evt.GetString()))) {
|
||||
this->plater_->get_notification_manager()->push_notification(NotificationType::NewAppAvailable);
|
||||
this->plater_->get_notification_manager()->push_notification(NotificationType::NewAppAvailable
|
||||
, NotificationManager::NotificationLevel::ImportantNotificationLevel
|
||||
, Slic3r::format(_u8L("New release version %1% is available."), evt.GetString())
|
||||
, _u8L("See Download page.")
|
||||
, [](wxEvtHandler* evnthndlr) {wxGetApp().open_web_page_localized("https://www.prusa3d.com/slicerweb"); return true; }
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
Bind(EVT_SLIC3R_ALPHA_VERSION_ONLINE, [this](const wxCommandEvent& evt) {
|
||||
Bind(EVT_SLIC3R_EXPERIMENTAL_VERSION_ONLINE, [this](const wxCommandEvent& evt) {
|
||||
app_config->save();
|
||||
if (this->plater_ != nullptr && app_config->get("notify_testing_release") == "1") {
|
||||
if (*Semver::parse(SLIC3R_VERSION) < *Semver::parse(into_u8(evt.GetString()))) {
|
||||
this->plater_->get_notification_manager()->push_notification(NotificationType::NewAlphaAvailable);
|
||||
}
|
||||
}
|
||||
});
|
||||
Bind(EVT_SLIC3R_BETA_VERSION_ONLINE, [this](const wxCommandEvent& evt) {
|
||||
app_config->save();
|
||||
if (this->plater_ != nullptr && app_config->get("notify_testing_release") == "1") {
|
||||
if (*Semver::parse(SLIC3R_VERSION) < *Semver::parse(into_u8(evt.GetString()))) {
|
||||
this->plater_->get_notification_manager()->close_notification_of_type(NotificationType::NewAlphaAvailable);
|
||||
this->plater_->get_notification_manager()->push_notification(NotificationType::NewBetaAvailable);
|
||||
if (this->plater_ != nullptr && app_config->get("notify_release") == "all") {
|
||||
std::string evt_string = into_u8(evt.GetString());
|
||||
if (*Semver::parse(SLIC3R_VERSION) < *Semver::parse(evt_string)) {
|
||||
auto notif_type = (evt_string.find("beta") != std::string::npos ? NotificationType::NewBetaAvailable : NotificationType::NewAlphaAvailable);
|
||||
this->plater_->get_notification_manager()->push_notification( notif_type
|
||||
, NotificationManager::NotificationLevel::ImportantNotificationLevel
|
||||
, Slic3r::format(_u8L("New prerelease version %1% is available."), evt_string)
|
||||
, _u8L("See Releases page.")
|
||||
, [](wxEvtHandler* evnthndlr) {wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -1347,10 +1409,9 @@ void GUI_App::UpdateDVCDarkUI(wxDataViewCtrl* dvc, bool highlited/* = false*/)
|
|||
{
|
||||
#ifdef _WIN32
|
||||
UpdateDarkUI(dvc, highlited ? dark_mode() : false);
|
||||
wxItemAttr attr(dark_mode() ? m_color_highlight_default : m_color_label_default,
|
||||
m_color_window_default,
|
||||
m_normal_font);
|
||||
dvc->SetHeaderAttr(attr);
|
||||
#ifdef _MSW_DARK_MODE
|
||||
dvc->RefreshHeaderDarkMode(&m_normal_font);
|
||||
#endif //_MSW_DARK_MODE
|
||||
if (dvc->HasFlag(wxDV_ROW_LINES))
|
||||
dvc->SetAlternateRowColour(m_color_highlight_default);
|
||||
if (dvc->GetBorder() != wxBORDER_SIMPLE)
|
||||
|
|
@ -1574,12 +1635,44 @@ void fatal_error(wxWindow* parent)
|
|||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
static void update_scrolls(wxWindow* window)
|
||||
{
|
||||
wxWindowList::compatibility_iterator node = window->GetChildren().GetFirst();
|
||||
while (node)
|
||||
{
|
||||
wxWindow* win = node->GetData();
|
||||
if (dynamic_cast<wxScrollHelper*>(win) ||
|
||||
dynamic_cast<wxTreeCtrl*>(win) ||
|
||||
dynamic_cast<wxTextCtrl*>(win))
|
||||
NppDarkMode::SetDarkExplorerTheme(win->GetHWND());
|
||||
|
||||
update_scrolls(win);
|
||||
node = node->GetNext();
|
||||
}
|
||||
}
|
||||
#endif //_MSW_DARK_MODE
|
||||
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
void GUI_App::force_menu_update()
|
||||
{
|
||||
NppDarkMode::SetSystemMenuForApp(app_config->get("sys_menu_enabled") == "1");
|
||||
}
|
||||
#endif //_MSW_DARK_MODE
|
||||
|
||||
void GUI_App::force_colors_update()
|
||||
{
|
||||
#ifdef _MSW_DARK_MODE
|
||||
NppDarkMode::SetDarkMode(app_config->get("dark_color_mode") == "1");
|
||||
if (WXHWND wxHWND = wxToolTip::GetToolTipCtrl())
|
||||
NppDarkMode::SetDarkExplorerTheme((HWND)wxHWND);
|
||||
NppDarkMode::SetDarkTitleBar(mainframe->GetHWND());
|
||||
#endif //_MSW_DARK_MODE
|
||||
m_force_colors_update = true;
|
||||
}
|
||||
#endif
|
||||
#endif //_WIN32
|
||||
|
||||
// Called after the Preferences dialog is closed and the program settings are saved.
|
||||
// Update the UI based on the current preferences.
|
||||
|
|
@ -1587,11 +1680,15 @@ void GUI_App::update_ui_from_settings()
|
|||
{
|
||||
update_label_colours();
|
||||
#ifdef _WIN32
|
||||
// Upadte UU colors before Update UI from settings
|
||||
// Upadte UI colors before Update UI from settings
|
||||
if (m_force_colors_update) {
|
||||
m_force_colors_update = false;
|
||||
mainframe->force_color_changed();
|
||||
mainframe->diff_dialog.force_color_changed();
|
||||
mainframe->printhost_queue_dlg()->force_color_changed();
|
||||
#ifdef _MSW_DARK_MODE
|
||||
update_scrolls(mainframe);
|
||||
#endif //_MSW_DARK_MODE
|
||||
}
|
||||
#endif
|
||||
mainframe->update_ui_from_settings();
|
||||
|
|
@ -2000,7 +2097,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
|||
local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip);
|
||||
local_menu->Append(config_id_base + ConfigMenuSnapshots, _L("&Configuration Snapshots") + dots, _L("Inspect / activate configuration snapshots"));
|
||||
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _L("Take Configuration &Snapshot"), _L("Capture a configuration snapshot"));
|
||||
local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for updates"), _L("Check for configuration updates"));
|
||||
local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for Configuration Updates"), _L("Check for configuration updates"));
|
||||
#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION)
|
||||
//if (DesktopIntegrationDialog::integration_possible())
|
||||
local_menu->Append(config_id_base + ConfigMenuDesktopIntegration, _L("Desktop Integration"), _L("Desktop Integration"));
|
||||
|
|
@ -2824,7 +2921,7 @@ bool GUI_App::open_browser_with_warning_dialog(const wxString& url, int flags/*
|
|||
bool launch = true;
|
||||
|
||||
if (get_app_config()->get("suppress_hyperlinks").empty()) {
|
||||
wxRichMessageDialog dialog(nullptr, _L("Should we open this hyperlink in your default browser?"), _L("PrusaSlicer: Open hyperlink"), wxICON_QUESTION | wxYES_NO);
|
||||
RichMessageDialog dialog(nullptr, _L("Should we open this hyperlink in your default browser?"), _L("PrusaSlicer: Open hyperlink"), wxICON_QUESTION | wxYES_NO);
|
||||
dialog.ShowCheckBox(_L("Remember my choice"));
|
||||
int answer = dialog.ShowModal();
|
||||
launch = answer == wxID_YES;
|
||||
|
|
|
|||
|
|
@ -210,6 +210,9 @@ public:
|
|||
const wxColour& get_color_hovered_btn_label() { return m_color_hovered_btn_label; }
|
||||
const wxColour& get_color_selected_btn_bg() { return m_color_selected_btn_bg; }
|
||||
void force_colors_update();
|
||||
#ifdef _MSW_DARK_MODE
|
||||
void force_menu_update();
|
||||
#endif //_MSW_DARK_MODE
|
||||
#endif
|
||||
|
||||
const wxFont& small_font() { return m_small_font; }
|
||||
|
|
|
|||
|
|
@ -2044,8 +2044,7 @@ void ObjectList::split()
|
|||
void ObjectList::merge(bool to_multipart_object)
|
||||
{
|
||||
// merge selected objects to the multipart object
|
||||
if (to_multipart_object)
|
||||
{
|
||||
if (to_multipart_object) {
|
||||
auto get_object_idxs = [this](std::vector<int>& obj_idxs, wxDataViewItemArray& sels)
|
||||
{
|
||||
// check selections and split instances to the separated objects...
|
||||
|
|
@ -2056,8 +2055,7 @@ void ObjectList::merge(bool to_multipart_object)
|
|||
break;
|
||||
}
|
||||
|
||||
if (!instance_selection)
|
||||
{
|
||||
if (!instance_selection) {
|
||||
for (wxDataViewItem item : sels) {
|
||||
assert(m_objects_model->GetItemType(item) & itObject);
|
||||
obj_idxs.emplace_back(m_objects_model->GetIdByItem(item));
|
||||
|
|
@ -2069,8 +2067,7 @@ void ObjectList::merge(bool to_multipart_object)
|
|||
std::map<int, std::set<int>> sel_map;
|
||||
std::set<int> empty_set;
|
||||
for (wxDataViewItem item : sels) {
|
||||
if (m_objects_model->GetItemType(item) & itObject)
|
||||
{
|
||||
if (m_objects_model->GetItemType(item) & itObject) {
|
||||
int obj_idx = m_objects_model->GetIdByItem(item);
|
||||
int inst_cnt = (*m_objects)[obj_idx]->instances.size();
|
||||
if (inst_cnt == 1)
|
||||
|
|
@ -2087,8 +2084,7 @@ void ObjectList::merge(bool to_multipart_object)
|
|||
// all objects, created from the instances will be added to the end of list
|
||||
int new_objects_cnt = 0; // count of this new objects
|
||||
|
||||
for (auto map_item : sel_map)
|
||||
{
|
||||
for (auto map_item : sel_map) {
|
||||
int obj_idx = map_item.first;
|
||||
// object with just 1 instance
|
||||
if (map_item.second.empty()) {
|
||||
|
|
@ -2148,37 +2144,36 @@ void ObjectList::merge(bool to_multipart_object)
|
|||
new_object->name = _u8L("Merged");
|
||||
ModelConfig &config = new_object->config;
|
||||
|
||||
for (int obj_idx : obj_idxs)
|
||||
{
|
||||
for (int obj_idx : obj_idxs) {
|
||||
ModelObject* object = (*m_objects)[obj_idx];
|
||||
|
||||
const Geometry::Transformation& transformation = object->instances[0]->get_transformation();
|
||||
Vec3d scale = transformation.get_scaling_factor();
|
||||
Vec3d mirror = transformation.get_mirror();
|
||||
Vec3d rotation = transformation.get_rotation();
|
||||
const Vec3d scale = transformation.get_scaling_factor();
|
||||
const Vec3d mirror = transformation.get_mirror();
|
||||
const Vec3d rotation = transformation.get_rotation();
|
||||
|
||||
if (object->id() == (*m_objects)[obj_idxs.front()]->id())
|
||||
new_object->add_instance();
|
||||
Transform3d volume_offset_correction = new_object->instances[0]->get_transformation().get_matrix().inverse() * transformation.get_matrix();
|
||||
const Transform3d& volume_offset_correction = transformation.get_matrix();
|
||||
|
||||
// merge volumes
|
||||
for (const ModelVolume* volume : object->volumes) {
|
||||
ModelVolume* new_volume = new_object->add_volume(*volume);
|
||||
|
||||
//set rotation
|
||||
Vec3d vol_rot = new_volume->get_rotation() + rotation;
|
||||
const Vec3d vol_rot = new_volume->get_rotation() + rotation;
|
||||
new_volume->set_rotation(vol_rot);
|
||||
|
||||
// set scale
|
||||
Vec3d vol_sc_fact = new_volume->get_scaling_factor().cwiseProduct(scale);
|
||||
const Vec3d vol_sc_fact = new_volume->get_scaling_factor().cwiseProduct(scale);
|
||||
new_volume->set_scaling_factor(vol_sc_fact);
|
||||
|
||||
// set mirror
|
||||
Vec3d vol_mirror = new_volume->get_mirror().cwiseProduct(mirror);
|
||||
const Vec3d vol_mirror = new_volume->get_mirror().cwiseProduct(mirror);
|
||||
new_volume->set_mirror(vol_mirror);
|
||||
|
||||
// set offset
|
||||
Vec3d vol_offset = volume_offset_correction* new_volume->get_offset();
|
||||
const Vec3d vol_offset = volume_offset_correction* new_volume->get_offset();
|
||||
new_volume->set_offset(vol_offset);
|
||||
}
|
||||
new_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
|
||||
|
|
@ -2211,6 +2206,11 @@ void ObjectList::merge(bool to_multipart_object)
|
|||
for (const auto& range : object->layer_config_ranges)
|
||||
new_object->layer_config_ranges.emplace(range);
|
||||
}
|
||||
|
||||
new_object->center_around_origin();
|
||||
new_object->translate_instances(-new_object->origin_translation);
|
||||
new_object->origin_translation = Vec3d::Zero();
|
||||
|
||||
// remove selected objects
|
||||
remove();
|
||||
|
||||
|
|
@ -2221,8 +2221,7 @@ void ObjectList::merge(bool to_multipart_object)
|
|||
}
|
||||
// merge all parts to the one single object
|
||||
// all part's settings will be lost
|
||||
else
|
||||
{
|
||||
else {
|
||||
wxDataViewItem item = GetSelection();
|
||||
if (!item)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -866,6 +866,9 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
|
|||
|
||||
void ObjectManipulation::change_scale_value(int axis, double value)
|
||||
{
|
||||
if (value <= 0.0)
|
||||
return;
|
||||
|
||||
if (std::abs(m_cache.scale_rounded(axis) - value) < EPSILON)
|
||||
return;
|
||||
|
||||
|
|
@ -882,6 +885,9 @@ void ObjectManipulation::change_scale_value(int axis, double value)
|
|||
|
||||
void ObjectManipulation::change_size_value(int axis, double value)
|
||||
{
|
||||
if (value <= 0.0)
|
||||
return;
|
||||
|
||||
if (std::abs(m_cache.size_rounded(axis) - value) < EPSILON)
|
||||
return;
|
||||
|
||||
|
|
@ -947,10 +953,26 @@ void ObjectManipulation::on_change(const std::string& opt_key, int axis, double
|
|||
change_position_value(axis, new_value);
|
||||
else if (opt_key == "rotation")
|
||||
change_rotation_value(axis, new_value);
|
||||
else if (opt_key == "scale")
|
||||
change_scale_value(axis, new_value);
|
||||
else if (opt_key == "size")
|
||||
change_size_value(axis, new_value);
|
||||
else if (opt_key == "scale") {
|
||||
if (new_value > 0.0)
|
||||
change_scale_value(axis, new_value);
|
||||
else {
|
||||
new_value = m_cache.scale(axis);
|
||||
m_cache.scale(axis) = 0.0;
|
||||
m_cache.scale_rounded(axis) = DBL_MAX;
|
||||
change_scale_value(axis, new_value);
|
||||
}
|
||||
}
|
||||
else if (opt_key == "size") {
|
||||
if (new_value > 0.0)
|
||||
change_size_value(axis, new_value);
|
||||
else {
|
||||
new_value = m_cache.size(axis);
|
||||
m_cache.size(axis) = 0.0;
|
||||
m_cache.size_rounded(axis) = DBL_MAX;
|
||||
change_size_value(axis, new_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectManipulation::set_uniform_scaling(const bool new_value)
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ GalleryDialog::GalleryDialog(wxWindow* parent, bool modify_gallery/* = false*/)
|
|||
ok_btn->SetToolTip(_L("Add selected shape(s) to the bed"));
|
||||
}
|
||||
static_cast<wxButton*>(FindWindowById(wxID_CLOSE, this))->Bind(wxEVT_BUTTON, [this](wxCommandEvent&){ this->EndModal(wxID_CLOSE); });
|
||||
|
||||
this->SetEscapeId(wxID_CLOSE);
|
||||
auto add_btn = [this, buttons]( size_t pos, int& ID, wxString title, wxString tooltip,
|
||||
void (GalleryDialog::* method)(wxEvent&),
|
||||
std::function<bool()> enable_fn = []() {return true; }) {
|
||||
|
|
|
|||
|
|
@ -167,7 +167,11 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit)
|
|||
ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always);
|
||||
if (last_h != win_h || last_y != y) {
|
||||
// ask canvas for another frame to render the window in the correct position
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_imgui->set_requires_extra_frame();
|
||||
#else
|
||||
m_parent.request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (last_h != win_h)
|
||||
last_h = win_h;
|
||||
if (last_y != y)
|
||||
|
|
|
|||
|
|
@ -170,7 +170,13 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
|||
float slider_start_position = std::max(position_before_text_y, position_after_text_y - slider_height);
|
||||
ImGui::SetCursorPosY(slider_start_position);
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
wxString tooltip = format_wxstr(_L("Preselects faces by overhang angle. It is possible to restrict paintable facets to only preselected faces when "
|
||||
"the option \"%1%\" is enabled."), m_desc["on_overhangs_only"]);
|
||||
if (m_imgui->slider_float("##angle_threshold_deg", &m_highlight_by_angle_threshold_deg, 0.f, 90.f, format_str.data(), 1.0f, true, tooltip)) {
|
||||
#else
|
||||
if (m_imgui->slider_float("##angle_threshold_deg", &m_highlight_by_angle_threshold_deg, 0.f, 90.f, format_str.data())) {
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_parent.set_slope_normal_angle(90.f - m_highlight_by_angle_threshold_deg);
|
||||
if (! m_parent.is_using_slope()) {
|
||||
m_parent.use_slope(true);
|
||||
|
|
@ -182,9 +188,11 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
|||
ImGui::SetCursorPosY(std::max(position_before_text_y + slider_height, position_after_text_y));
|
||||
|
||||
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||
#if !ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(format_wxstr(_L("Preselects faces by overhang angle. It is possible to restrict paintable facets to only preselected faces when "
|
||||
"the option \"%1%\" is enabled."), m_desc["on_overhangs_only"]), max_tooltip_width);
|
||||
#endif // !ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
m_imgui->disabled_begin(m_highlight_by_angle_threshold_deg == 0.f);
|
||||
ImGui::NewLine();
|
||||
|
|
@ -267,9 +275,13 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
|||
m_imgui->text(m_desc.at("cursor_size"));
|
||||
ImGui::SameLine(sliders_left_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_left_width);
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true, _L("Alt + Mouse wheel"));
|
||||
#else
|
||||
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Alt + Mouse wheel"), max_tooltip_width);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
m_imgui->checkbox(m_desc["split_triangles"], m_triangle_splitting_enabled);
|
||||
|
||||
|
|
@ -284,14 +296,20 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
|||
|
||||
ImGui::SameLine(sliders_left_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_left_width);
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data(), 1.0f, true, _L("Alt + Mouse wheel")))
|
||||
#else
|
||||
if (m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data()))
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
for (auto &triangle_selector : m_triangle_selectors) {
|
||||
triangle_selector->seed_fill_unselect_all_triangles();
|
||||
triangle_selector->request_update_render_data();
|
||||
}
|
||||
|
||||
#if !ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Alt + Mouse wheel"), max_tooltip_width);
|
||||
#endif // !ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
|
@ -310,11 +328,16 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
|||
ImGui::SameLine(sliders_left_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_left_width);
|
||||
auto clp_dist = float(m_c->object_clipper()->get_position());
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel")))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
#else
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Ctrl + Mouse wheel"), max_tooltip_width);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_imgui->button(m_desc.at("remove_all"))) {
|
||||
|
|
|
|||
|
|
@ -441,9 +441,13 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
m_imgui->text(m_desc.at("cursor_size"));
|
||||
ImGui::SameLine(sliders_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_width);
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true, _L("Alt + Mouse wheel"));
|
||||
#else
|
||||
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Alt + Mouse wheel"), max_tooltip_width);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
m_imgui->checkbox(m_desc["split_triangles"], m_triangle_splitting_enabled);
|
||||
|
||||
|
|
@ -460,14 +464,20 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
"placed after the number with no whitespace in between.");
|
||||
ImGui::SameLine(sliders_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_width);
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data(), 1.0f, true, _L("Alt + Mouse wheel")))
|
||||
#else
|
||||
if(m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data()))
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
for (auto &triangle_selector : m_triangle_selectors) {
|
||||
triangle_selector->seed_fill_unselect_all_triangles();
|
||||
triangle_selector->request_update_render_data();
|
||||
}
|
||||
|
||||
#if !ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Alt + Mouse wheel"), max_tooltip_width);
|
||||
#endif // !ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
|
@ -484,11 +494,16 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
ImGui::SameLine(sliders_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_width);
|
||||
auto clp_dist = float(m_c->object_clipper()->get_position());
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel")))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
#else
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Ctrl + Mouse wheel"), max_tooltip_width);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_imgui->button(m_desc.at("remove_all"))) {
|
||||
|
|
|
|||
|
|
@ -512,9 +512,21 @@ GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui,
|
|||
y = std::min(y, alignment.bottom_limit - win_h);
|
||||
ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always);
|
||||
|
||||
ImGui::PushItemWidth(300.f);
|
||||
float max_text_w = 0.;
|
||||
auto padding = ImGui::GetStyle().FramePadding;
|
||||
padding.x *= 2.f;
|
||||
padding.y *= 2.f;
|
||||
|
||||
if (ImGui::BeginCombo(_L("Choose goal").c_str(), RotoptimizeJob::get_method_name(state.method_id).c_str())) {
|
||||
for (size_t i = 0; i < RotoptimizeJob::get_methods_count(); ++i) {
|
||||
float w =
|
||||
ImGui::CalcTextSize(RotoptimizeJob::get_method_name(i).c_str()).x +
|
||||
padding.x + ImGui::GetFrameHeight();
|
||||
max_text_w = std::max(w, max_text_w);
|
||||
}
|
||||
|
||||
ImGui::PushItemWidth(max_text_w);
|
||||
|
||||
if (ImGui::BeginCombo("", RotoptimizeJob::get_method_name(state.method_id).c_str())) {
|
||||
for (size_t i = 0; i < RotoptimizeJob::get_methods_count(); ++i) {
|
||||
if (ImGui::Selectable(RotoptimizeJob::get_method_name(i).c_str())) {
|
||||
state.method_id = i;
|
||||
|
|
@ -530,12 +542,18 @@ GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui,
|
|||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImVec2 sz = ImGui::GetItemRectSize();
|
||||
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", RotoptimizeJob::get_method_description(state.method_id).c_str());
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if ( imgui->button(_L("Optimize")) ) {
|
||||
auto btn_txt = _L("Apply");
|
||||
auto btn_txt_sz = ImGui::CalcTextSize(btn_txt.c_str());
|
||||
ImVec2 button_sz = {btn_txt_sz.x + padding.x, btn_txt_sz.y + padding.y};
|
||||
ImGui::SetCursorPosX(padding.x + sz.x - button_sz.x);
|
||||
if ( imgui->button(btn_txt) ) {
|
||||
wxGetApp().plater()->optimize_rotation();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,9 +128,13 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
|||
m_imgui->text(m_desc.at("cursor_size"));
|
||||
ImGui::SameLine(sliders_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_width);
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true, _L("Alt + Mouse wheel"));
|
||||
#else
|
||||
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Alt + Mouse wheel"), max_tooltip_width);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("cursor_type"));
|
||||
|
|
@ -168,11 +172,16 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
|||
ImGui::SameLine(sliders_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_width);
|
||||
auto clp_dist = float(m_c->object_clipper()->get_position());
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel")))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
#else
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Ctrl + Mouse wheel"), max_tooltip_width);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_imgui->button(m_desc.at("remove_all"))) {
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
|||
const char * format = (m_configuration.decimate_ratio > 10)? "%.0f %%":
|
||||
((m_configuration.decimate_ratio > 1)? "%.1f %%":"%.2f %%");
|
||||
|
||||
if (ImGui::SliderFloat("##decimate_ratio", &m_configuration.decimate_ratio, 0.f, 100.f, format)) {
|
||||
if(m_imgui->slider_float("##decimate_ratio", &m_configuration.decimate_ratio, 0.f, 100.f, format)){
|
||||
if (m_configuration.decimate_ratio < 0.f)
|
||||
m_configuration.decimate_ratio = 0.01f;
|
||||
if (m_configuration.decimate_ratio > 100.f)
|
||||
|
|
|
|||
|
|
@ -644,7 +644,11 @@ RENDER_AGAIN:
|
|||
if ((last_h != win_h) || (last_y != y))
|
||||
{
|
||||
// ask canvas for another frame to render the window in the correct position
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_imgui->set_requires_extra_frame();
|
||||
#else
|
||||
m_parent.request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (last_h != win_h)
|
||||
last_h = win_h;
|
||||
if (last_y != y)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/event.h>
|
||||
|
|
@ -48,7 +51,9 @@ static const std::map<const wchar_t, std::string> font_icons = {
|
|||
{ImGui::RightArrowHoverButton , "notification_right_hover" },
|
||||
{ImGui::PreferencesButton , "notification_preferences" },
|
||||
{ImGui::PreferencesHoverButton , "notification_preferences_hover"},
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
{ImGui::SliderFloatEditBtnIcon, "edit_button" },
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
};
|
||||
static const std::map<const wchar_t, std::string> font_icons_large = {
|
||||
{ImGui::CloseNotifButton , "notification_close" },
|
||||
|
|
@ -85,14 +90,6 @@ const ImVec4 ImGuiWrapper::COL_BUTTON_HOVERED = COL_ORANGE_LIGHT;
|
|||
const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = ImGuiWrapper::COL_BUTTON_HOVERED;
|
||||
|
||||
ImGuiWrapper::ImGuiWrapper()
|
||||
: m_glyph_ranges(nullptr)
|
||||
, m_font_cjk(false)
|
||||
, m_font_size(18.0)
|
||||
, m_font_texture(0)
|
||||
, m_style_scaling(1.0)
|
||||
, m_mouse_buttons(0)
|
||||
, m_disabled(false)
|
||||
, m_new_frame_open(false)
|
||||
{
|
||||
ImGui::CreateContext();
|
||||
|
||||
|
|
@ -484,6 +481,53 @@ void ImGuiWrapper::tooltip(const wxString &label, float wrap_width)
|
|||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/, bool clamp /*= true*/, const wxString& tooltip /*= ""*/, bool show_edit_btn /*= true*/)
|
||||
{
|
||||
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||
|
||||
bool ret = ImGui::SliderFloat(label, v, v_min, v_max, format, power);
|
||||
if (!tooltip.empty() && ImGui::IsItemHovered())
|
||||
this->tooltip(into_u8(tooltip).c_str(), max_tooltip_width);
|
||||
|
||||
if (clamp)
|
||||
*v = std::clamp(*v, v_min, v_max);
|
||||
|
||||
if (show_edit_btn) {
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y });
|
||||
ImGui::SameLine();
|
||||
|
||||
std::wstring btn_name;
|
||||
btn_name = ImGui::SliderFloatEditBtnIcon + boost::nowide::widen(std::string(label));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 1.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.5f, 0.5f, 0.5f, 1.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.5f, 0.5f, 0.5f, 1.0f });
|
||||
if (ImGui::Button(into_u8(btn_name).c_str())) {
|
||||
ImGui::SetKeyboardFocusHere(-1);
|
||||
this->set_requires_extra_frame();
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
if (ImGui::IsItemHovered())
|
||||
this->tooltip(into_u8(_L("Edit")).c_str(), max_tooltip_width);
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/, bool clamp /*= true*/, const wxString& tooltip /*= ""*/, bool show_edit_btn /*= true*/)
|
||||
{
|
||||
return this->slider_float(label.c_str(), v, v_min, v_max, format, power, clamp, tooltip, show_edit_btn);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/, bool clamp /*= true*/, const wxString& tooltip /*= ""*/, bool show_edit_btn /*= true*/)
|
||||
{
|
||||
auto label_utf8 = into_u8(label);
|
||||
return this->slider_float(label_utf8.c_str(), v, v_min, v_max, format, power, clamp, tooltip, show_edit_btn);
|
||||
}
|
||||
#else
|
||||
bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/, bool clamp /*= true*/)
|
||||
{
|
||||
bool ret = ImGui::SliderFloat(label, v, v_min, v_max, format, power);
|
||||
|
|
@ -502,6 +546,7 @@ bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, fl
|
|||
auto label_utf8 = into_u8(label);
|
||||
return this->slider_float(label_utf8.c_str(), v, v_min, v_max, format, power, clamp);
|
||||
}
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection)
|
||||
{
|
||||
|
|
@ -972,15 +1017,7 @@ std::vector<unsigned char> ImGuiWrapper::load_svg(const std::string& bitmap_name
|
|||
{
|
||||
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);
|
||||
NSVGimage* image = ::nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, { { "#808080", "#FFFFFF" } });
|
||||
if (image == nullptr)
|
||||
return empty_vector;
|
||||
|
||||
|
|
@ -1152,6 +1189,7 @@ void ImGuiWrapper::init_input()
|
|||
io.KeyMap[ImGuiKey_Backspace] = WXK_BACK;
|
||||
io.KeyMap[ImGuiKey_Space] = WXK_SPACE;
|
||||
io.KeyMap[ImGuiKey_Enter] = WXK_RETURN;
|
||||
io.KeyMap[ImGuiKey_KeyPadEnter] = WXK_NUMPAD_ENTER;
|
||||
io.KeyMap[ImGuiKey_Escape] = WXK_ESCAPE;
|
||||
io.KeyMap[ImGuiKey_A] = 'A';
|
||||
io.KeyMap[ImGuiKey_C] = 'C';
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
|
||||
namespace Slic3r {namespace Search {
|
||||
|
|
@ -22,15 +24,18 @@ namespace GUI {
|
|||
|
||||
class ImGuiWrapper
|
||||
{
|
||||
const ImWchar *m_glyph_ranges;
|
||||
const ImWchar* m_glyph_ranges{ nullptr };
|
||||
// Chinese, Japanese, Korean
|
||||
bool m_font_cjk;
|
||||
float m_font_size;
|
||||
unsigned m_font_texture;
|
||||
float m_style_scaling;
|
||||
unsigned m_mouse_buttons;
|
||||
bool m_disabled;
|
||||
bool m_new_frame_open;
|
||||
bool m_font_cjk{ false };
|
||||
float m_font_size{ 18.0 };
|
||||
unsigned m_font_texture{ 0 };
|
||||
float m_style_scaling{ 1.0 };
|
||||
unsigned m_mouse_buttons{ 0 };
|
||||
bool m_disabled{ false };
|
||||
bool m_new_frame_open{ false };
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
bool m_requires_extra_frame{ false };
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
std::string m_clipboard_text;
|
||||
|
||||
public:
|
||||
|
|
@ -90,9 +95,15 @@ public:
|
|||
void tooltip(const wxString &label, float wrap_width);
|
||||
|
||||
// Float sliders: Manually inserted values aren't clamped by ImGui.Using this wrapper function does (when clamp==true).
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
bool slider_float(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = "", bool show_edit_btn = true);
|
||||
bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = "", bool show_edit_btn = true);
|
||||
bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = "", bool show_edit_btn = true);
|
||||
#else
|
||||
bool slider_float(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true);
|
||||
bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true);
|
||||
bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
bool combo(const wxString& label, const std::vector<std::string>& 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);
|
||||
|
|
@ -108,6 +119,12 @@ public:
|
|||
bool want_text_input() const;
|
||||
bool want_any_input() const;
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
bool requires_extra_frame() const { return m_requires_extra_frame; }
|
||||
void set_requires_extra_frame() { m_requires_extra_frame = true; }
|
||||
void reset_requires_extra_frame() { m_requires_extra_frame = false; }
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
static const ImVec4 COL_GREY_DARK;
|
||||
static const ImVec4 COL_GREY_LIGHT;
|
||||
static const ImVec4 COL_ORANGE_DARK;
|
||||
|
|
|
|||
|
|
@ -122,7 +122,9 @@ public:
|
|||
std::string err;
|
||||
ConfigSubstitutions config_substitutions;
|
||||
|
||||
priv(Plater *plt) : plater{plt} {}
|
||||
ImportDlg import_dlg;
|
||||
|
||||
priv(Plater *plt) : plater{plt}, import_dlg{plt} {}
|
||||
};
|
||||
|
||||
SLAImportJob::SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
|
||||
|
|
@ -176,14 +178,12 @@ void SLAImportJob::prepare()
|
|||
{
|
||||
reset();
|
||||
|
||||
ImportDlg dlg{p->plater};
|
||||
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
auto path = dlg.get_path();
|
||||
if (p->import_dlg.ShowModal() == wxID_OK) {
|
||||
auto path = p->import_dlg.get_path();
|
||||
auto nm = wxFileName(path);
|
||||
p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : nm.GetFullPath();
|
||||
p->sel = dlg.get_selection();
|
||||
p->win = dlg.get_marchsq_windowsize();
|
||||
p->sel = p->import_dlg.get_selection();
|
||||
p->win = p->import_dlg.get_marchsq_windowsize();
|
||||
p->config_substitutions.clear();
|
||||
} else {
|
||||
p->path = "";
|
||||
|
|
@ -236,7 +236,7 @@ void SLAImportJob::finalize()
|
|||
|
||||
if (!p->mesh.empty()) {
|
||||
bool is_centered = false;
|
||||
p->plater->sidebar().obj_list()->load_mesh_object(TriangleMesh{p->mesh},
|
||||
p->plater->sidebar().obj_list()->load_mesh_object(TriangleMesh{std::move(p->mesh)},
|
||||
name, is_centered);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ namespace Slic3r {
|
|||
namespace GUI {
|
||||
|
||||
|
||||
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id, wxBitmap bitmap)
|
||||
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, long style, wxBitmap bitmap)
|
||||
: wxDialog(parent ? parent : dynamic_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
, boldfont(wxGetApp().normal_font()/*wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)*/)
|
||||
, boldfont(wxGetApp().normal_font())
|
||||
, content_sizer(new wxBoxSizer(wxVERTICAL))
|
||||
, btn_sizer(new wxBoxSizer(wxHORIZONTAL))
|
||||
{
|
||||
|
|
@ -36,6 +36,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
|
|||
this->SetFont(wxGetApp().normal_font());
|
||||
this->CenterOnParent();
|
||||
|
||||
auto *main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *rightsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
|
|
@ -46,46 +47,41 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
|
|||
rightsizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
rightsizer->Add(content_sizer, 1, wxEXPAND);
|
||||
btn_sizer->AddStretchSpacer();
|
||||
|
||||
if (button_id != wxID_NONE) {
|
||||
auto *button = new wxButton(this, button_id);
|
||||
button->SetFocus();
|
||||
btn_sizer->Add(button);
|
||||
}
|
||||
|
||||
rightsizer->Add(btn_sizer, 0, wxALIGN_RIGHT);
|
||||
|
||||
if (! bitmap.IsOk()) {
|
||||
bitmap = create_scaled_bitmap("PrusaSlicer_192px.png", this, 192);
|
||||
}
|
||||
|
||||
logo = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap);
|
||||
logo = new wxStaticBitmap(this, wxID_ANY, bitmap.IsOk() ? bitmap : wxNullBitmap);
|
||||
|
||||
topsizer->Add(logo, 0, wxALL, BORDER);
|
||||
topsizer->Add(rightsizer, 1, wxTOP | wxBOTTOM | wxRIGHT | wxEXPAND, BORDER);
|
||||
|
||||
SetSizerAndFit(topsizer);
|
||||
main_sizer->Add(topsizer, 1, wxEXPAND);
|
||||
main_sizer->Add(new StaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT, HORIZ_SPACING);
|
||||
main_sizer->Add(btn_sizer, 0, wxALL | wxEXPAND, VERT_SPACING);
|
||||
|
||||
apply_style(style);
|
||||
|
||||
SetSizerAndFit(main_sizer);
|
||||
}
|
||||
|
||||
void MsgDialog::add_btn(wxWindowID btn_id, bool set_focus /*= false*/)
|
||||
void MsgDialog::add_btn(wxWindowID btn_id, bool set_focus /*= false*/, const wxString& label/* = wxString()*/)
|
||||
{
|
||||
wxButton* btn = new wxButton(this, btn_id);
|
||||
wxButton* btn = new wxButton(this, btn_id, label);
|
||||
if (set_focus)
|
||||
btn->SetFocus();
|
||||
btn_sizer->Add(btn, 0, wxRIGHT, HORIZ_SPACING);
|
||||
btn_sizer->Add(btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, HORIZ_SPACING);
|
||||
btn->Bind(wxEVT_BUTTON, [this, btn_id](wxCommandEvent&) { this->EndModal(btn_id); });
|
||||
};
|
||||
|
||||
void MsgDialog::apply_style(long style)
|
||||
{
|
||||
if (style & wxOK) add_btn(wxID_OK, true);
|
||||
if (style & wxYES) add_btn(wxID_YES);
|
||||
if (style & wxYES) add_btn(wxID_YES, true);
|
||||
if (style & wxNO) add_btn(wxID_NO);
|
||||
if (style & wxCANCEL) add_btn(wxID_CANCEL);
|
||||
|
||||
logo->SetBitmap(create_scaled_bitmap(style & wxICON_WARNING ? "exclamation" :
|
||||
style & wxICON_INFORMATION ? "info" :
|
||||
style & wxICON_QUESTION ? "question" : "PrusaSlicer"/*"_192px_grayscale.png"*/, this, 84));
|
||||
logo->SetBitmap( create_scaled_bitmap(style & wxICON_WARNING ? "exclamation" :
|
||||
style & wxICON_INFORMATION ? "info" :
|
||||
style & wxICON_QUESTION ? "question" : "PrusaSlicer", this, 64, style & wxICON_ERROR));
|
||||
}
|
||||
|
||||
void MsgDialog::finalize()
|
||||
|
|
@ -122,7 +118,7 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin
|
|||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
wxFont monospace = wxGetApp().code_font();
|
||||
wxColour text_clr = wxGetApp().get_label_clr_default();
|
||||
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
wxColour bgr_clr = parent->GetBackgroundColour(); //wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
|
||||
auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
|
||||
const int font_size = font.GetPointSize();
|
||||
|
|
@ -161,18 +157,17 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin
|
|||
msg_escaped = std::string("<pre><code>") + msg_escaped + "</code></pre>";
|
||||
html->SetPage("<html><body bgcolor=\"" + bgr_clr_str + "\"><font color=\"" + text_clr_str + "\">" + wxString::FromUTF8(msg_escaped.data()) + "</font></body></html>");
|
||||
content_sizer->Add(html, 1, wxEXPAND);
|
||||
wxGetApp().UpdateDarkUI(html);
|
||||
}
|
||||
|
||||
// ErrorDialog
|
||||
|
||||
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg, bool monospaced_font)
|
||||
: MsgDialog(parent, wxString::Format(_(L("%s error")), SLIC3R_APP_NAME),
|
||||
wxString::Format(_(L("%s has encountered an error")), SLIC3R_APP_NAME),
|
||||
wxID_NONE)
|
||||
wxString::Format(_(L("%s has encountered an error")), SLIC3R_APP_NAME), wxOK)
|
||||
, msg(msg)
|
||||
{
|
||||
add_msg_content(this, content_sizer, msg, monospaced_font);
|
||||
add_btn(wxID_OK, true);
|
||||
|
||||
// Use a small bitmap with monospaced font, as the error text will not be wrapped.
|
||||
logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, monospaced_font ? 48 : /*1*/84));
|
||||
|
|
@ -189,10 +184,9 @@ WarningDialog::WarningDialog(wxWindow *parent,
|
|||
const wxString& caption/* = wxEmptyString*/,
|
||||
long style/* = wxOK*/)
|
||||
: MsgDialog(parent, caption.IsEmpty() ? wxString::Format(_L("%s warning"), SLIC3R_APP_NAME) : caption,
|
||||
wxString::Format(_L("%s has a warning")+":", SLIC3R_APP_NAME), wxID_NONE)
|
||||
wxString::Format(_L("%s has a warning")+":", SLIC3R_APP_NAME), style)
|
||||
{
|
||||
add_msg_content(this, content_sizer, message);
|
||||
apply_style(style);
|
||||
finalize();
|
||||
}
|
||||
|
||||
|
|
@ -203,50 +197,51 @@ MessageDialog::MessageDialog(wxWindow* parent,
|
|||
const wxString& message,
|
||||
const wxString& caption/* = wxEmptyString*/,
|
||||
long style/* = wxOK*/)
|
||||
: MsgDialog(parent, caption.IsEmpty() ? wxString::Format(_L("%s info"), SLIC3R_APP_NAME) : caption, wxEmptyString, wxID_NONE)
|
||||
: MsgDialog(parent, caption.IsEmpty() ? wxString::Format(_L("%s info"), SLIC3R_APP_NAME) : caption, wxEmptyString, style)
|
||||
{
|
||||
add_msg_content(this, content_sizer, message);
|
||||
apply_style(style);
|
||||
finalize();
|
||||
}
|
||||
|
||||
|
||||
// RichMessageDialog
|
||||
|
||||
RichMessageDialog::RichMessageDialog(wxWindow* parent,
|
||||
const wxString& message,
|
||||
const wxString& caption/* = wxEmptyString*/,
|
||||
long style/* = wxOK*/)
|
||||
: MsgDialog(parent, caption.IsEmpty() ? wxString::Format(_L("%s info"), SLIC3R_APP_NAME) : caption, wxEmptyString, style)
|
||||
{
|
||||
add_msg_content(this, content_sizer, message);
|
||||
|
||||
m_checkBox = new wxCheckBox(this, wxID_ANY, m_checkBoxText);
|
||||
wxGetApp().UpdateDarkUI(m_checkBox);
|
||||
m_checkBox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) { m_checkBoxValue = m_checkBox->GetValue(); });
|
||||
|
||||
btn_sizer->Insert(0, m_checkBox, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
finalize();
|
||||
}
|
||||
|
||||
int RichMessageDialog::ShowModal()
|
||||
{
|
||||
if (m_checkBoxText.IsEmpty())
|
||||
m_checkBox->Hide();
|
||||
else
|
||||
m_checkBox->SetLabelText(m_checkBoxText);
|
||||
Layout();
|
||||
|
||||
return wxDialog::ShowModal();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// MessageWithCheckDialog
|
||||
|
||||
MessageWithCheckDialog::MessageWithCheckDialog( wxWindow* parent,
|
||||
const wxString& message,
|
||||
const wxString& checkbox_label,
|
||||
const wxString& caption/* = wxEmptyString*/,
|
||||
long style/* = wxOK*/)
|
||||
: MsgDialog(parent, caption.IsEmpty() ? wxString::Format(_L("%s info"), SLIC3R_APP_NAME) : caption, wxEmptyString, wxID_NONE)
|
||||
{
|
||||
add_msg_content(this, content_sizer, message);
|
||||
|
||||
m_check = new wxCheckBox(this, wxID_ANY, checkbox_label);
|
||||
content_sizer->Add(m_check, 0, wxTOP, 10);
|
||||
|
||||
apply_style(style);
|
||||
finalize();
|
||||
}
|
||||
|
||||
bool MessageWithCheckDialog::GetCheckVal()
|
||||
{
|
||||
if (m_check)
|
||||
return m_check->GetValue();
|
||||
return false;
|
||||
}
|
||||
|
||||
// InfoDialog
|
||||
|
||||
InfoDialog::InfoDialog(wxWindow* parent, const wxString &title, const wxString& msg)
|
||||
: MsgDialog(parent, wxString::Format(_L("%s information"), SLIC3R_APP_NAME), title)
|
||||
: MsgDialog(parent, wxString::Format(_L("%s information"), SLIC3R_APP_NAME), title, wxOK | wxICON_INFORMATION)
|
||||
, msg(msg)
|
||||
{
|
||||
add_msg_content(this, content_sizer, msg);
|
||||
// Set info bitmap
|
||||
logo->SetBitmap(create_scaled_bitmap("info", this, 84));
|
||||
|
||||
finalize();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
#include <wx/font.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/richmsgdlg.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/statline.h>
|
||||
|
||||
class wxBoxSizer;
|
||||
class wxCheckBox;
|
||||
|
|
@ -17,7 +20,6 @@ namespace Slic3r {
|
|||
|
||||
namespace GUI {
|
||||
|
||||
|
||||
// A message / query dialog with a bitmap on the left and any content on the right
|
||||
// with buttons underneath.
|
||||
struct MsgDialog : wxDialog
|
||||
|
|
@ -39,10 +41,9 @@ protected:
|
|||
HORIZ_SPACING = 5,
|
||||
};
|
||||
|
||||
// button_id is an id of a button that can be added by default, use wxID_NONE to disable
|
||||
MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id = wxID_OK, wxBitmap bitmap = wxNullBitmap);
|
||||
MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, long style = wxOK, wxBitmap bitmap = wxNullBitmap);
|
||||
|
||||
void add_btn(wxWindowID btn_id, bool set_focus = false);
|
||||
void add_btn(wxWindowID btn_id, bool set_focus = false, const wxString& label = wxString());
|
||||
void apply_style(long style);
|
||||
void finalize();
|
||||
|
||||
|
|
@ -87,6 +88,23 @@ public:
|
|||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
// Generic static line, used intead of wxStaticLine
|
||||
class StaticLine: public wxTextCtrl
|
||||
{
|
||||
public:
|
||||
StaticLine( wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxLI_HORIZONTAL,
|
||||
const wxString& name = wxString::FromAscii(wxTextCtrlNameStr))
|
||||
: wxTextCtrl(parent, id, wxEmptyString, pos, size!=wxDefaultSize ? size : (style == wxLI_HORIZONTAL ? wxSize(10, 1) : wxSize(1, 10)), wxSIMPLE_BORDER, wxDefaultValidator, name)
|
||||
{
|
||||
this->Enable(false);
|
||||
}
|
||||
~StaticLine() {}
|
||||
};
|
||||
|
||||
// Generic message dialog, used intead of wxMessageDialog
|
||||
class MessageDialog : public MsgDialog
|
||||
{
|
||||
|
|
@ -101,7 +119,158 @@ public:
|
|||
MessageDialog &operator=(const MessageDialog&) = delete;
|
||||
virtual ~MessageDialog() = default;
|
||||
};
|
||||
|
||||
// Generic rich message dialog, used intead of wxRichMessageDialog
|
||||
class RichMessageDialog : public MsgDialog
|
||||
{
|
||||
wxCheckBox* m_checkBox{ nullptr };
|
||||
wxString m_checkBoxText;
|
||||
bool m_checkBoxValue{ false };
|
||||
|
||||
public:
|
||||
RichMessageDialog( wxWindow *parent,
|
||||
const wxString& message,
|
||||
const wxString& caption = wxEmptyString,
|
||||
long style = wxOK);
|
||||
RichMessageDialog(RichMessageDialog&&) = delete;
|
||||
RichMessageDialog(const RichMessageDialog&) = delete;
|
||||
RichMessageDialog &operator=(RichMessageDialog&&) = delete;
|
||||
RichMessageDialog &operator=(const RichMessageDialog&) = delete;
|
||||
virtual ~RichMessageDialog() = default;
|
||||
|
||||
int ShowModal() override;
|
||||
|
||||
void ShowCheckBox(const wxString& checkBoxText, bool checked = false)
|
||||
{
|
||||
m_checkBoxText = checkBoxText;
|
||||
m_checkBoxValue = checked;
|
||||
}
|
||||
|
||||
wxString GetCheckBoxText() const { return m_checkBoxText; }
|
||||
bool IsCheckBoxChecked() const { return m_checkBoxValue; }
|
||||
|
||||
// This part o fcode isported from the "wx\msgdlg.h"
|
||||
using wxMD = wxMessageDialogBase;
|
||||
// customization of the message box buttons
|
||||
virtual bool SetYesNoLabels(const wxMD::ButtonLabel& yes, const wxMD::ButtonLabel& no)
|
||||
{
|
||||
DoSetCustomLabel(m_yes, yes);
|
||||
DoSetCustomLabel(m_no, no);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool SetYesNoCancelLabels(const wxMD::ButtonLabel& yes,
|
||||
const wxMD::ButtonLabel& no,
|
||||
const wxMD::ButtonLabel& cancel)
|
||||
{
|
||||
DoSetCustomLabel(m_yes, yes);
|
||||
DoSetCustomLabel(m_no, no);
|
||||
DoSetCustomLabel(m_cancel, cancel);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool SetOKLabel(const wxMD::ButtonLabel& ok)
|
||||
{
|
||||
DoSetCustomLabel(m_ok, ok);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool SetOKCancelLabels(const wxMD::ButtonLabel& ok,
|
||||
const wxMD::ButtonLabel& cancel)
|
||||
{
|
||||
DoSetCustomLabel(m_ok, ok);
|
||||
DoSetCustomLabel(m_cancel, cancel);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool SetHelpLabel(const wxMD::ButtonLabel& help)
|
||||
{
|
||||
DoSetCustomLabel(m_help, help);
|
||||
return true;
|
||||
}
|
||||
// test if any custom labels were set
|
||||
bool HasCustomLabels() const
|
||||
{
|
||||
return !(m_ok.empty() && m_cancel.empty() && m_help.empty() &&
|
||||
m_yes.empty() && m_no.empty());
|
||||
}
|
||||
|
||||
// these functions return the label to be used for the button which is
|
||||
// either a custom label explicitly set by the user or the default label,
|
||||
// i.e. they always return a valid string
|
||||
wxString GetYesLabel() const
|
||||
{
|
||||
return m_yes.empty() ? GetDefaultYesLabel() : m_yes;
|
||||
}
|
||||
wxString GetNoLabel() const
|
||||
{
|
||||
return m_no.empty() ? GetDefaultNoLabel() : m_no;
|
||||
}
|
||||
wxString GetOKLabel() const
|
||||
{
|
||||
return m_ok.empty() ? GetDefaultOKLabel() : m_ok;
|
||||
}
|
||||
wxString GetCancelLabel() const
|
||||
{
|
||||
return m_cancel.empty() ? GetDefaultCancelLabel() : m_cancel;
|
||||
}
|
||||
wxString GetHelpLabel() const
|
||||
{
|
||||
return m_help.empty() ? GetDefaultHelpLabel() : m_help;
|
||||
}
|
||||
|
||||
protected:
|
||||
// this function is called by our public SetXXXLabels() and should assign
|
||||
// the value to var with possibly some transformation (e.g. Cocoa version
|
||||
// currently uses this to remove any accelerators from the button strings
|
||||
// while GTK+ one handles stock items specifically here)
|
||||
void DoSetCustomLabel(wxString& var, const wxMD::ButtonLabel& label)
|
||||
{
|
||||
var = label.GetAsString();
|
||||
}
|
||||
|
||||
// these functions return the custom label or empty string and should be
|
||||
// used only in specific circumstances such as creating the buttons with
|
||||
// these labels (in which case it makes sense to only use a custom label if
|
||||
// it was really given and fall back on stock label otherwise), use the
|
||||
// Get{Yes,No,OK,Cancel}Label() methods above otherwise
|
||||
const wxString& GetCustomYesLabel() const { return m_yes; }
|
||||
const wxString& GetCustomNoLabel() const { return m_no; }
|
||||
const wxString& GetCustomOKLabel() const { return m_ok; }
|
||||
const wxString& GetCustomHelpLabel() const { return m_help; }
|
||||
const wxString& GetCustomCancelLabel() const { return m_cancel; }
|
||||
|
||||
private:
|
||||
// these functions may be overridden to provide different defaults for the
|
||||
// default button labels (this is used by wxGTK)
|
||||
virtual wxString GetDefaultYesLabel() const { return wxGetTranslation("Yes"); }
|
||||
virtual wxString GetDefaultNoLabel() const { return wxGetTranslation("No"); }
|
||||
virtual wxString GetDefaultOKLabel() const { return wxGetTranslation("OK"); }
|
||||
virtual wxString GetDefaultCancelLabel() const { return wxGetTranslation("Cancel"); }
|
||||
virtual wxString GetDefaultHelpLabel() const { return wxGetTranslation("Help"); }
|
||||
|
||||
// labels for the buttons, initially empty meaning that the defaults should
|
||||
// be used, use GetYes/No/OK/CancelLabel() to access them
|
||||
wxString m_yes,
|
||||
m_no,
|
||||
m_ok,
|
||||
m_cancel,
|
||||
m_help;
|
||||
};
|
||||
#else
|
||||
// just a wrapper for wxStaticLine to use the same code on all platforms
|
||||
class StaticLine : public wxStaticLine
|
||||
{
|
||||
public:
|
||||
StaticLine(wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxLI_HORIZONTAL,
|
||||
const wxString& name = wxString::FromAscii(wxStaticLineNameStr))
|
||||
: wxStaticLine(parent, id, pos, size, style, name) {}
|
||||
~StaticLine() {}
|
||||
};
|
||||
// just a wrapper to wxMessageBox to use the same code on all platforms
|
||||
class MessageDialog : public wxMessageDialog
|
||||
{
|
||||
|
|
@ -113,25 +282,19 @@ public:
|
|||
: wxMessageDialog(parent, message, caption, style) {}
|
||||
~MessageDialog() {}
|
||||
};
|
||||
#endif
|
||||
|
||||
class MessageWithCheckDialog : public MsgDialog
|
||||
// just a wrapper to wxRichMessageBox to use the same code on all platforms
|
||||
class RichMessageDialog : public wxRichMessageDialog
|
||||
{
|
||||
wxCheckBox* m_check{ nullptr };
|
||||
public:
|
||||
MessageWithCheckDialog(wxWindow* parent,
|
||||
RichMessageDialog(wxWindow* parent,
|
||||
const wxString& message,
|
||||
const wxString& checkbox_label,
|
||||
const wxString& caption = wxEmptyString,
|
||||
long style = wxOK);
|
||||
MessageWithCheckDialog(MessageWithCheckDialog&&) = delete;
|
||||
MessageWithCheckDialog(const MessageWithCheckDialog&) = delete;
|
||||
MessageWithCheckDialog& operator=(MessageWithCheckDialog&&) = delete;
|
||||
MessageWithCheckDialog& operator=(const MessageWithCheckDialog&) = delete;
|
||||
virtual ~MessageWithCheckDialog() = default;
|
||||
|
||||
bool GetCheckVal();
|
||||
long style = wxOK)
|
||||
: wxRichMessageDialog(parent, message, caption, style) {}
|
||||
~RichMessageDialog() {}
|
||||
};
|
||||
#endif
|
||||
|
||||
// Generic info dialog, used for displaying exceptions
|
||||
class InfoDialog : public MsgDialog
|
||||
|
|
|
|||
|
|
@ -41,12 +41,6 @@ const NotificationManager::NotificationData NotificationManager::basic_notificat
|
|||
return true;
|
||||
}
|
||||
},
|
||||
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
|
||||
wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
|
||||
{NotificationType::NewAlphaAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New alpha release is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
|
||||
wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
|
||||
{NotificationType::NewBetaAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New beta release is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
|
||||
wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
|
||||
{NotificationType::EmptyColorChangeCode, NotificationLevel::PrintInfoNotificationLevel, 10,
|
||||
_u8L("You have just added a G-code for color change, but its value is empty.\n"
|
||||
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
|
||||
|
|
@ -61,6 +55,8 @@ const NotificationManager::NotificationData NotificationManager::basic_notificat
|
|||
{NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotificationLevel, 10,
|
||||
_u8L("Undo desktop integration failed.") },
|
||||
{NotificationType::ExportOngoing, NotificationLevel::RegularNotificationLevel, 0, _u8L("Exporting.") },
|
||||
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
|
||||
// wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
|
||||
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
|
||||
//{NotificationType::LoadingFailed, NotificationLevel::RegularNotificationLevel, 20, _u8L("Loading of model has Failed") },
|
||||
//{NotificationType::DeviceEjected, NotificationLevel::RegularNotificationLevel, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ public:
|
|||
// Push a NotificationType::CustomNotification with NotificationLevel::RegularNotificationLevel and 10s fade out interval.
|
||||
void push_notification(const std::string& text, int timestamp = 0);
|
||||
// Push a NotificationType::CustomNotification with provided notification level and 10s for RegularNotificationLevel.
|
||||
// ErrorNotificationLevel and ImportantNotificationLevel are never faded out.
|
||||
// ErrorNotificationLevel are never faded out.
|
||||
void push_notification(NotificationType type, NotificationLevel level, const std::string& text, const std::string& hypertext = "",
|
||||
std::function<bool(wxEvtHandler*)> callback = std::function<bool(wxEvtHandler*)>(), int timestamp = 0);
|
||||
// Pushes basic_notification with delay. See push_delayed_notification_data.
|
||||
|
|
@ -720,7 +720,7 @@ private:
|
|||
|
||||
case NotificationLevel::ErrorNotificationLevel: return 0;
|
||||
case NotificationLevel::WarningNotificationLevel: return 0;
|
||||
case NotificationLevel::ImportantNotificationLevel: return 0;
|
||||
case NotificationLevel::ImportantNotificationLevel: return 20;
|
||||
case NotificationLevel::ProgressBarNotificationLevel: return 2;
|
||||
case NotificationLevel::PrintInfoShortNotificationLevel: return 5;
|
||||
case NotificationLevel::RegularNotificationLevel: return 10;
|
||||
|
|
|
|||
|
|
@ -534,7 +534,7 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change)
|
|||
[model_id](const VendorProfile::PrinterModel& model) { return model.id == model_id; });
|
||||
if (it != models.end() && (it->family == "MK3" || it->family == "MINI"))
|
||||
continue;
|
||||
} else if (!preset->vendor && (boost::ends_with(model_id, "MK3") || boost::ends_with(model_id, "MINI"))) {
|
||||
} else if (!preset->vendor && (boost::starts_with(model_id, "MK3") || boost::starts_with(model_id, "MINI"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1087,6 +1087,8 @@ void Sidebar::msw_rescale()
|
|||
p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height));
|
||||
|
||||
p->scrolled->Layout();
|
||||
|
||||
p->searcher.dlg_msw_rescale();
|
||||
}
|
||||
|
||||
void Sidebar::sys_color_changed()
|
||||
|
|
@ -1125,6 +1127,8 @@ void Sidebar::sys_color_changed()
|
|||
p->btn_export_gcode_removable->msw_rescale();
|
||||
|
||||
p->scrolled->Layout();
|
||||
|
||||
p->searcher.dlg_sys_color_changed();
|
||||
}
|
||||
|
||||
void Sidebar::search()
|
||||
|
|
@ -1916,7 +1920,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
|
||||
"brim_width", "brim_separation", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
|
||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width",
|
||||
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology",
|
||||
"extruder_colour", "filament_colour", "material_colour", "max_print_height", "printer_model", "printer_technology",
|
||||
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
|
||||
"layer_height", "first_layer_height", "min_layer_height", "max_layer_height",
|
||||
"brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers",
|
||||
|
|
@ -2478,15 +2482,15 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
model.convert_from_meters(true);
|
||||
};
|
||||
if (answer_convert_from_meters == wxOK_DEFAULT) {
|
||||
MessageWithCheckDialog dlg(q, format_wxstr(_L_PLURAL(
|
||||
RichMessageDialog dlg(q, format_wxstr(_L_PLURAL(
|
||||
"The dimensions of the object from file %s seem to be defined in meters.\n"
|
||||
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of the object?",
|
||||
"The dimensions of some objects from file %s seem to be defined in meters.\n"
|
||||
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of these objects?", model.objects.size()), from_path(filename)) + "\n",
|
||||
_L("Apply to all the remaining small objects being loaded."),
|
||||
_L("The object is too small"), wxICON_WARNING | wxYES | wxNO);
|
||||
_L("The object is too small"), wxICON_QUESTION | wxYES_NO);
|
||||
dlg.ShowCheckBox(_L("Apply to all the remaining small objects being loaded."));
|
||||
int answer = dlg.ShowModal();
|
||||
if (dlg.GetCheckVal())
|
||||
if (dlg.IsCheckBoxChecked())
|
||||
answer_convert_from_meters = answer;
|
||||
else
|
||||
convert_model_if(model, answer == wxID_YES);
|
||||
|
|
@ -2500,15 +2504,15 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
convert_from_imperial_units(model, true);
|
||||
};
|
||||
if (answer_convert_from_imperial_units == wxOK_DEFAULT) {
|
||||
MessageWithCheckDialog dlg(q, format_wxstr(_L_PLURAL(
|
||||
RichMessageDialog dlg(q, format_wxstr(_L_PLURAL(
|
||||
"The dimensions of the object from file %s seem to be defined in inches.\n"
|
||||
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of the object?",
|
||||
"The dimensions of some objects from file %s seem to be defined in inches.\n"
|
||||
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of these objects?", model.objects.size()), from_path(filename)) + "\n",
|
||||
_L("Apply to all the remaining small objects being loaded."),
|
||||
_L("The object is too small"), wxICON_WARNING | wxYES | wxNO);
|
||||
_L("The object is too small"), wxICON_QUESTION | wxYES_NO);
|
||||
dlg.ShowCheckBox(_L("Apply to all the remaining small objects being loaded."));
|
||||
int answer = dlg.ShowModal();
|
||||
if (dlg.GetCheckVal())
|
||||
if (dlg.IsCheckBoxChecked())
|
||||
answer_convert_from_imperial_units = answer;
|
||||
else
|
||||
convert_model_if(model, answer == wxID_YES);
|
||||
|
|
@ -5055,6 +5059,7 @@ void Plater::new_project()
|
|||
Plater::SuppressSnapshots suppress(this);
|
||||
reset();
|
||||
reset_project_dirty_initial_presets();
|
||||
wxGetApp().update_saved_preset_from_current_preset();
|
||||
update_project_dirty_from_presets();
|
||||
}
|
||||
|
||||
|
|
@ -6217,6 +6222,9 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
|||
continue;
|
||||
}
|
||||
}
|
||||
if (opt_key == "material_colour") {
|
||||
update_scheduled = true; // update should be scheduled (for update 3DScene)
|
||||
}
|
||||
|
||||
p->config->set_key_value(opt_key, config.option(opt_key)->clone());
|
||||
if (opt_key == "printer_technology") {
|
||||
|
|
|
|||
|
|
@ -141,14 +141,6 @@ void PreferencesDialog::build(size_t selected_tab)
|
|||
option = Option(def, "background_processing");
|
||||
m_optgroup_general->append_single_option_line(option);
|
||||
|
||||
// Please keep in sync with ConfigWizard
|
||||
def.label = L("Check for application updates");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("If enabled, PrusaSlicer will check for the new versions of itself online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done.");
|
||||
def.set_default_value(new ConfigOptionBool(app_config->get("version_check") == "1"));
|
||||
option = Option(def, "version_check");
|
||||
m_optgroup_general->append_single_option_line(option);
|
||||
|
||||
m_optgroup_general->append_separator();
|
||||
|
||||
// Please keep in sync with ConfigWizard
|
||||
|
|
@ -350,8 +342,6 @@ void PreferencesDialog::build(size_t selected_tab)
|
|||
tabs->Layout();
|
||||
this->layout();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
def.label = L("Sequential slider applied only to top layer");
|
||||
|
|
@ -395,16 +385,6 @@ void PreferencesDialog::build(size_t selected_tab)
|
|||
m_optgroup_gui->append_single_option_line(option);
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
}
|
||||
def.label = L("Use Dark color mode (experimental)");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("If enabled, UI will use Dark mode colors. "
|
||||
"If disabled, old UI will be used.");
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->get("dark_color_mode") == "1" });
|
||||
option = Option(def, "dark_color_mode");
|
||||
m_optgroup_gui->append_single_option_line(option);
|
||||
|
||||
if (is_editor) {
|
||||
def.label = L("Set settings tabs as menu items (experimental)");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("If enabled, Settings Tabs will be placed as menu items. "
|
||||
|
|
@ -484,6 +464,36 @@ void PreferencesDialog::build(size_t selected_tab)
|
|||
}
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
|
||||
#ifdef _WIN32
|
||||
// Add "Dark Mode" tab
|
||||
if (is_editor) {
|
||||
// Add "Dark Mode" tab
|
||||
m_optgroup_dark_mode = create_options_tab(_L("Dark mode (experimental)"), tabs);
|
||||
m_optgroup_dark_mode->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
|
||||
};
|
||||
|
||||
def.label = L("Enable dark mode");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("If enabled, UI will use Dark mode colors. "
|
||||
"If disabled, old UI will be used.");
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->get("dark_color_mode") == "1" });
|
||||
option = Option(def, "dark_color_mode");
|
||||
m_optgroup_dark_mode->append_single_option_line(option);
|
||||
|
||||
def.label = L("Use system menu for application");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("If enabled, application will use standart Windows system menu,\n"
|
||||
"but on some combination od display scales it can looks ugly. "
|
||||
"If disabled, old UI will be used.");
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->get("sys_menu_enabled") == "1" });
|
||||
option = Option(def, "sys_menu_enabled");
|
||||
m_optgroup_dark_mode->append_single_option_line(option);
|
||||
|
||||
activate_options_tab(m_optgroup_dark_mode);
|
||||
}
|
||||
#endif //_WIN32
|
||||
|
||||
// update alignment of the controls for all tabs
|
||||
update_ctrls_alignment();
|
||||
|
||||
|
|
@ -525,7 +535,7 @@ void PreferencesDialog::accept(wxEvent&)
|
|||
// if (m_values.find("no_defaults") != m_values.end()
|
||||
// warning_catcher(this, wxString::Format(_L("You need to restart %s to make the changes effective."), SLIC3R_APP_NAME));
|
||||
|
||||
std::vector<std::string> options_to_recreate_GUI = { "no_defaults", "tabs_as_menu" };
|
||||
std::vector<std::string> options_to_recreate_GUI = { "no_defaults", "tabs_as_menu", "sys_menu_enabled" };
|
||||
|
||||
for (const std::string& option : options_to_recreate_GUI) {
|
||||
if (m_values.find(option) != m_values.end()) {
|
||||
|
|
@ -588,11 +598,14 @@ void PreferencesDialog::accept(wxEvent&)
|
|||
|
||||
EndModal(wxID_OK);
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
#ifdef _WIN32
|
||||
if (m_values.find("dark_color_mode") != m_values.end())
|
||||
wxGetApp().force_colors_update();
|
||||
#endif
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
if (m_values.find("sys_menu_enabled") != m_values.end())
|
||||
wxGetApp().force_menu_update();
|
||||
#endif //_MSW_DARK_MODE
|
||||
#endif // _WIN32
|
||||
if (m_settings_layout_changed)
|
||||
;// application will be recreated after Preference dialog will be destroyed
|
||||
else
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ class PreferencesDialog : public DPIDialog
|
|||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_general;
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_camera;
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_gui;
|
||||
#ifdef _WIN32
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_dark_mode;
|
||||
#endif //_WIN32
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_render;
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
|
|
|
|||
|
|
@ -419,7 +419,7 @@ wxString PresetComboBox::separator(const std::string& label)
|
|||
|
||||
wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, const std::string& main_icon_name,
|
||||
bool is_compatible/* = true*/, bool is_system/* = false*/, bool is_single_bar/* = false*/,
|
||||
std::string filament_rgb/* = ""*/, std::string extruder_rgb/* = ""*/)
|
||||
const std::string& filament_rgb/* = ""*/, const std::string& extruder_rgb/* = ""*/, const std::string& material_rgb/* = ""*/)
|
||||
{
|
||||
// If the filament preset is not compatible and there is a "red flag" icon loaded, show it left
|
||||
// to the filament color image.
|
||||
|
|
@ -431,6 +431,7 @@ wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, con
|
|||
bool dark_mode = wxGetApp().dark_mode();
|
||||
if (dark_mode)
|
||||
bitmap_key += ",dark";
|
||||
bitmap_key += material_rgb;
|
||||
|
||||
wxBitmap* bmp = bitmap_cache().find(bitmap_key);
|
||||
if (bmp == nullptr) {
|
||||
|
|
@ -457,7 +458,10 @@ wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, con
|
|||
{
|
||||
// Paint the color bars.
|
||||
bmps.emplace_back(bitmap_cache().mkclear(thin_space_icon_width, icon_height));
|
||||
bmps.emplace_back(create_scaled_bitmap(main_icon_name));
|
||||
if (m_type == Preset::TYPE_SLA_MATERIAL)
|
||||
bmps.emplace_back(create_scaled_bitmap(main_icon_name, this, 16, false, material_rgb));
|
||||
else
|
||||
bmps.emplace_back(create_scaled_bitmap(main_icon_name));
|
||||
// Paint a lock at the system presets.
|
||||
bmps.emplace_back(bitmap_cache().mkclear(wide_space_icon_width, icon_height));
|
||||
}
|
||||
|
|
@ -789,7 +793,7 @@ void PlaterPresetComboBox::update()
|
|||
if (!preset.is_visible || (!preset.is_compatible && !is_selected))
|
||||
continue;
|
||||
|
||||
std::string bitmap_key, filament_rgb, extruder_rgb;
|
||||
std::string bitmap_key, filament_rgb, extruder_rgb, material_rgb;
|
||||
std::string bitmap_type_name = bitmap_key = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name;
|
||||
|
||||
bool single_bar = false;
|
||||
|
|
@ -803,10 +807,12 @@ void PlaterPresetComboBox::update()
|
|||
|
||||
bitmap_key += single_bar ? filament_rgb : filament_rgb + extruder_rgb;
|
||||
}
|
||||
else if (m_type == Preset::TYPE_SLA_MATERIAL)
|
||||
material_rgb = is_selected ? m_preset_bundle->sla_materials.get_edited_preset().config.opt_string("material_colour") : preset.config.opt_string("material_colour");
|
||||
|
||||
wxBitmap* bmp = get_bmp(bitmap_key, wide_icons, bitmap_type_name,
|
||||
preset.is_compatible, preset.is_system || preset.is_default,
|
||||
single_bar, filament_rgb, extruder_rgb);
|
||||
single_bar, filament_rgb, extruder_rgb, material_rgb);
|
||||
assert(bmp);
|
||||
|
||||
const std::string name = preset.alias.empty() ? preset.name : preset.alias;
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ protected:
|
|||
|
||||
wxBitmap* get_bmp( std::string bitmap_key, bool wide_icons, const std::string& main_icon_name,
|
||||
bool is_compatible = true, bool is_system = false, bool is_single_bar = false,
|
||||
std::string filament_rgb = "", std::string extruder_rgb = "");
|
||||
const std::string& filament_rgb = "", const std::string& extruder_rgb = "", const std::string& material_rgb = "");
|
||||
|
||||
wxBitmap* get_bmp( std::string bitmap_key, const std::string& main_icon_name, const std::string& next_icon_name,
|
||||
bool is_enabled = true, bool is_compatible = true, bool is_system = false);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ static const char *CONFIG_KEY_PRINT = "printhost_print";
|
|||
static const char *CONFIG_KEY_GROUP = "printhost_group";
|
||||
|
||||
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_print, const wxArrayString &groups)
|
||||
: MsgDialog(static_cast<wxWindow*>(wxGetApp().mainframe), _L("Send G-Code to printer host"), _L("Upload to Printer Host with the following filename:"), wxID_NONE)
|
||||
: MsgDialog(static_cast<wxWindow*>(wxGetApp().mainframe), _L("Send G-Code to printer host"), _L("Upload to Printer Host with the following filename:"), wxOK | wxCANCEL)
|
||||
, txt_filename(new wxTextCtrl(this, wxID_ANY))
|
||||
, box_print(can_start_print ? new wxCheckBox(this, wxID_ANY, _L("Start printing after upload")) : nullptr)
|
||||
, combo_groups(!groups.IsEmpty() ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, groups, wxCB_READONLY) : nullptr)
|
||||
|
|
@ -70,10 +70,6 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
|
|||
combo_groups->SetValue(recent_group);
|
||||
}
|
||||
|
||||
auto* szr = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
|
||||
auto* btn_ok = szr->GetAffirmativeButton();
|
||||
btn_sizer->Add(szr);
|
||||
|
||||
wxString recent_path = from_u8(app_config->get("recent", CONFIG_KEY_PATH));
|
||||
if (recent_path.Length() > 0 && recent_path[recent_path.Length() - 1] != '/') {
|
||||
recent_path += '/';
|
||||
|
|
@ -88,21 +84,19 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
|
|||
|
||||
wxString suffix = recent_path.substr(recent_path.find_last_of('.'));
|
||||
|
||||
btn_ok->Bind(wxEVT_BUTTON, [this, suffix](wxCommandEvent&) {
|
||||
static_cast<wxButton*>(FindWindowById(wxID_OK, this))->Bind(wxEVT_BUTTON, [this, suffix](wxCommandEvent&) {
|
||||
wxString path = txt_filename->GetValue();
|
||||
// .gcode suffix control
|
||||
if (!path.Lower().EndsWith(suffix.Lower()))
|
||||
{
|
||||
//wxMessageDialog msg_wingow(this, wxString::Format(_L("Upload filename doesn't end with \"%s\". Do you wish to continue?"), suffix), wxString(SLIC3R_APP_NAME), wxYES | wxNO);
|
||||
MessageDialog msg_wingow(this, wxString::Format(_L("Upload filename doesn't end with \"%s\". Do you wish to continue?"), suffix), wxString(SLIC3R_APP_NAME), wxYES | wxNO);
|
||||
if (msg_wingow.ShowModal() == wxID_NO)
|
||||
return;
|
||||
}
|
||||
EndDialog(wxID_OK);
|
||||
});
|
||||
});
|
||||
|
||||
Fit();
|
||||
CenterOnParent();
|
||||
finalize();
|
||||
|
||||
#ifdef __linux__
|
||||
// On Linux with GTK2 when text control lose the focus then selection (colored background) disappears but text color stay white
|
||||
|
|
@ -331,6 +325,14 @@ void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect)
|
|||
save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS);
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_sys_color_changed()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
wxGetApp().UpdateDVCDarkUI(job_list);
|
||||
#endif
|
||||
}
|
||||
|
||||
PrintHostQueueDialog::JobState PrintHostQueueDialog::get_state(int idx)
|
||||
{
|
||||
wxCHECK_MSG(idx >= 0 && idx < job_list->GetItemCount(), ST_ERROR, "Out of bounds access to job list");
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ public:
|
|||
}
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
void on_sys_color_changed() override;
|
||||
|
||||
private:
|
||||
enum Column {
|
||||
|
|
|
|||
|
|
@ -401,6 +401,18 @@ void OptionsSearcher::show_dialog()
|
|||
search_dialog->Popup();
|
||||
}
|
||||
|
||||
void OptionsSearcher::dlg_sys_color_changed()
|
||||
{
|
||||
if (search_dialog)
|
||||
search_dialog->on_sys_color_changed();
|
||||
}
|
||||
|
||||
void OptionsSearcher::dlg_msw_rescale()
|
||||
{
|
||||
if (search_dialog)
|
||||
search_dialog->msw_rescale();
|
||||
}
|
||||
|
||||
void OptionsSearcher::add_key(const std::string& opt_key, Preset::Type type, const wxString& group, const wxString& category)
|
||||
{
|
||||
groups_and_categories[get_key(opt_key, type)] = GroupAndCategory{group, category};
|
||||
|
|
@ -666,7 +678,7 @@ void SearchDialog::OnLeftDown(wxMouseEvent& event)
|
|||
ProcessSelection(search_list->GetSelection());
|
||||
}
|
||||
|
||||
void SearchDialog::on_dpi_changed(const wxRect& suggested_rect)
|
||||
void SearchDialog::msw_rescale()
|
||||
{
|
||||
const int& em = em_unit();
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,8 @@ public:
|
|||
void sort_options_by_label() { sort_options(); }
|
||||
|
||||
void show_dialog();
|
||||
void dlg_sys_color_changed();
|
||||
void dlg_msw_rescale();
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -180,9 +182,11 @@ public:
|
|||
void Popup(wxPoint position = wxDefaultPosition);
|
||||
void ProcessSelection(wxDataViewItem selection);
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
void msw_rescale();
|
||||
void on_sys_color_changed() override;
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override { msw_rescale(); }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "SendSystemInfoDialog.hpp"
|
||||
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "libslic3r/BlacklistedLibraryCheck.hpp"
|
||||
#include "libslic3r/Platform.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
|
|
@ -479,6 +480,26 @@ static std::string generate_system_info_json()
|
|||
hw_node.add_child("Monitors", monitors_node);
|
||||
data_node.add_child("Hardware", hw_node);
|
||||
|
||||
#ifdef _WIN32
|
||||
{
|
||||
pt::ptree blacklisted_node;
|
||||
std::vector<std::wstring> blacklisted_libraries;
|
||||
BlacklistedLibraryCheck::get_instance().get_blacklisted(blacklisted_libraries);
|
||||
for (const std::wstring& wstr : blacklisted_libraries) {
|
||||
std::string utf8 = boost::nowide::narrow(wstr);
|
||||
if (size_t last_bs_pos = utf8.find_last_of("\\"); last_bs_pos < utf8.size() - 1) {
|
||||
// Remove anything before last backslash so we don't send the path to the DLL.
|
||||
utf8.erase(0, last_bs_pos + 1);
|
||||
}
|
||||
pt::ptree node; // Create an unnamed node containing the value
|
||||
node.put("", utf8);
|
||||
blacklisted_node.push_back(std::make_pair("", node)); // Add this node to the list.
|
||||
}
|
||||
if (! blacklisted_libraries.empty())
|
||||
data_node.add_child("Blacklisted libraries", blacklisted_node);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
pt::ptree opengl_node;
|
||||
opengl_node.put("Version", OpenGLManager::get_gl_info().get_version());
|
||||
opengl_node.put("GLSLVersion", OpenGLManager::get_gl_info().get_glsl_version());
|
||||
|
|
|
|||
|
|
@ -4157,6 +4157,7 @@ void TabSLAMaterial::build()
|
|||
auto page = add_options_page(L("Material"), "resin");
|
||||
|
||||
auto optgroup = page->new_optgroup(L("Material"));
|
||||
optgroup->append_single_option_line("material_colour");
|
||||
optgroup->append_single_option_line("bottle_cost");
|
||||
optgroup->append_single_option_line("bottle_volume");
|
||||
optgroup->append_single_option_line("bottle_weight");
|
||||
|
|
@ -4164,6 +4165,12 @@ void TabSLAMaterial::build()
|
|||
|
||||
optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value)
|
||||
{
|
||||
if (opt_key == "material_colour") {
|
||||
update_dirty();
|
||||
on_value_change(opt_key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
|
||||
if (opt_key == "bottle_volume") {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace GUI {
|
|||
|
||||
|
||||
static const char* URL_CHANGELOG = "https://files.prusa3d.com/?latest=slicer-stable&lng=%1%";
|
||||
static const char* URL_DOWNLOAD = "https://www.prusa3d.com/downloads&lng=%1%";
|
||||
static const char* URL_DOWNLOAD = "https://www.prusa3d.com/slicerweb&lng=%1%";
|
||||
static const char* URL_DEV = "https://github.com/prusa3d/PrusaSlicer/releases/tag/version_%1%";
|
||||
|
||||
static const std::string CONFIG_UPDATE_WIKI_URL("https://github.com/prusa3d/PrusaSlicer/wiki/Slic3r-PE-1.40-configuration-update");
|
||||
|
|
@ -89,7 +89,7 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates, bool force_
|
|||
MsgDialog(nullptr, force_before_wizard ? _L("Opening Configuration Wizard") : _L("Configuration update"),
|
||||
force_before_wizard ? _L("PrusaSlicer is not using the newest configuration available.\n"
|
||||
"Configuration Wizard may not offer the latest printers, filaments and SLA materials to be installed. ") :
|
||||
_L("Configuration update is available"), wxID_NONE)
|
||||
_L("Configuration update is available"), wxICON_ERROR)
|
||||
{
|
||||
auto *text = new wxStaticText(this, wxID_ANY, _(L(
|
||||
"Would you like to install it?\n\n"
|
||||
|
|
@ -133,22 +133,14 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates, bool force_
|
|||
content_sizer->Add(versions);
|
||||
content_sizer->AddSpacer(2*VERT_SPACING);
|
||||
|
||||
auto* btn_ok = new wxButton(this, wxID_OK, force_before_wizard ? _L("Install") : "OK");
|
||||
btn_sizer->Add(btn_ok);
|
||||
btn_sizer->AddSpacer(HORIZ_SPACING);
|
||||
add_btn(wxID_OK, true, force_before_wizard ? _L("Install") : "OK");
|
||||
if (force_before_wizard) {
|
||||
auto* btn_no_install = new wxButton(this, wxID_ANY, "Don't install");
|
||||
btn_no_install->Bind(wxEVT_BUTTON, [this](wxEvent&) { this->EndModal(wxID_CLOSE); });
|
||||
btn_sizer->Add(btn_no_install);
|
||||
btn_sizer->AddSpacer(HORIZ_SPACING);
|
||||
add_btn(wxID_CLOSE, false, _L("Don't install"));
|
||||
static_cast<wxButton*>(FindWindowById(wxID_CLOSE, this))->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) { this->EndModal(wxID_CLOSE); });
|
||||
}
|
||||
auto* btn_cancel = new wxButton(this, wxID_CANCEL);
|
||||
btn_sizer->Add(btn_cancel);
|
||||
btn_ok->SetFocus();
|
||||
add_btn(wxID_CANCEL);
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
|
||||
Fit();
|
||||
finalize();
|
||||
}
|
||||
|
||||
MsgUpdateConfig::~MsgUpdateConfig() {}
|
||||
|
|
@ -156,7 +148,7 @@ MsgUpdateConfig::~MsgUpdateConfig() {}
|
|||
//MsgUpdateForced
|
||||
|
||||
MsgUpdateForced::MsgUpdateForced(const std::vector<Update>& updates) :
|
||||
MsgDialog(nullptr, wxString::Format(_(L("%s incompatibility")), SLIC3R_APP_NAME), _(L("You must install a configuration update.")) + " ", wxID_NONE)
|
||||
MsgDialog(nullptr, wxString::Format(_(L("%s incompatibility")), SLIC3R_APP_NAME), _(L("You must install a configuration update.")) + " ", wxOK | wxICON_ERROR)
|
||||
{
|
||||
auto* text = new wxStaticText(this, wxID_ANY, wxString::Format(_(L(
|
||||
"%s will now start updates. Otherwise it won't be able to start.\n\n"
|
||||
|
|
@ -165,7 +157,6 @@ MsgUpdateForced::MsgUpdateForced(const std::vector<Update>& updates) :
|
|||
"Updated configuration bundles:"
|
||||
)), SLIC3R_APP_NAME));
|
||||
|
||||
logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192));
|
||||
|
||||
text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit());
|
||||
content_sizer->Add(text);
|
||||
|
|
@ -198,19 +189,12 @@ MsgUpdateForced::MsgUpdateForced(const std::vector<Update>& updates) :
|
|||
|
||||
content_sizer->Add(versions);
|
||||
content_sizer->AddSpacer(2 * VERT_SPACING);
|
||||
|
||||
auto* btn_exit = new wxButton(this, wxID_EXIT, wxString::Format(_(L("Exit %s")), SLIC3R_APP_NAME));
|
||||
btn_sizer->Add(btn_exit);
|
||||
btn_sizer->AddSpacer(HORIZ_SPACING);
|
||||
auto* btn_ok = new wxButton(this, wxID_OK);
|
||||
btn_sizer->Add(btn_ok);
|
||||
btn_ok->SetFocus();
|
||||
|
||||
auto exiter = [this](const wxCommandEvent& evt) { this->EndModal(evt.GetId()); };
|
||||
btn_exit->Bind(wxEVT_BUTTON, exiter);
|
||||
btn_ok->Bind(wxEVT_BUTTON, exiter);
|
||||
add_btn(wxID_EXIT, false, wxString::Format(_L("Exit %s"), SLIC3R_APP_NAME));
|
||||
for (auto ID : { wxID_EXIT, wxID_OK })
|
||||
static_cast<wxButton*>(FindWindowById(ID, this))->Bind(wxEVT_BUTTON, [this](const wxCommandEvent& evt) { this->EndModal(evt.GetId()); });
|
||||
|
||||
Fit();
|
||||
finalize();
|
||||
}
|
||||
|
||||
MsgUpdateForced::~MsgUpdateForced() {}
|
||||
|
|
@ -219,10 +203,8 @@ MsgUpdateForced::~MsgUpdateForced() {}
|
|||
|
||||
MsgDataIncompatible::MsgDataIncompatible(const std::unordered_map<std::string, wxString> &incompats) :
|
||||
MsgDialog(nullptr, wxString::Format(_(L("%s incompatibility")), SLIC3R_APP_NAME),
|
||||
wxString::Format(_(L("%s configuration is incompatible")), SLIC3R_APP_NAME), wxID_NONE)
|
||||
wxString::Format(_(L("%s configuration is incompatible")), SLIC3R_APP_NAME), wxICON_ERROR)
|
||||
{
|
||||
logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192));
|
||||
|
||||
auto *text = new wxStaticText(this, wxID_ANY, wxString::Format(_(L(
|
||||
"This version of %s is not compatible with currently installed configuration bundles.\n"
|
||||
"This probably happened as a result of running an older %s after using a newer one.\n\n"
|
||||
|
|
@ -254,18 +236,13 @@ MsgDataIncompatible::MsgDataIncompatible(const std::unordered_map<std::string, w
|
|||
content_sizer->Add(versions);
|
||||
content_sizer->AddSpacer(2*VERT_SPACING);
|
||||
|
||||
auto *btn_exit = new wxButton(this, wxID_EXIT, wxString::Format(_(L("Exit %s")), SLIC3R_APP_NAME));
|
||||
btn_sizer->Add(btn_exit);
|
||||
btn_sizer->AddSpacer(HORIZ_SPACING);
|
||||
auto *btn_reconf = new wxButton(this, wxID_REPLACE, _(L("Re-configure")));
|
||||
btn_sizer->Add(btn_reconf);
|
||||
btn_exit->SetFocus();
|
||||
add_btn(wxID_REPLACE, true, _L("Re-configure"));
|
||||
add_btn(wxID_EXIT, false, wxString::Format(_L("Exit %s"), SLIC3R_APP_NAME));
|
||||
|
||||
auto exiter = [this](const wxCommandEvent& evt) { this->EndModal(evt.GetId()); };
|
||||
btn_exit->Bind(wxEVT_BUTTON, exiter);
|
||||
btn_reconf->Bind(wxEVT_BUTTON, exiter);
|
||||
for (auto ID : {wxID_EXIT, wxID_REPLACE})
|
||||
static_cast<wxButton*>(FindWindowById(ID, this))->Bind(wxEVT_BUTTON, [this](const wxCommandEvent& evt) { this->EndModal(evt.GetId()); });
|
||||
|
||||
Fit();
|
||||
finalize();
|
||||
}
|
||||
|
||||
MsgDataIncompatible::~MsgDataIncompatible() {}
|
||||
|
|
@ -303,9 +280,7 @@ MsgDataLegacy::MsgDataLegacy() :
|
|||
content_sizer->Add(link);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
|
||||
Fit();
|
||||
finalize();
|
||||
}
|
||||
|
||||
MsgDataLegacy::~MsgDataLegacy() {}
|
||||
|
|
@ -314,7 +289,7 @@ MsgDataLegacy::~MsgDataLegacy() {}
|
|||
// MsgNoUpdate
|
||||
|
||||
MsgNoUpdates::MsgNoUpdates() :
|
||||
MsgDialog(nullptr, _(L("Configuration updates")), _(L("No updates available")))
|
||||
MsgDialog(nullptr, _(L("Configuration updates")), _(L("No updates available")), wxICON_ERROR | wxOK)
|
||||
{
|
||||
|
||||
auto* text = new wxStaticText(this, wxID_ANY, wxString::Format(
|
||||
|
|
@ -327,11 +302,7 @@ MsgNoUpdates::MsgNoUpdates() :
|
|||
content_sizer->Add(text);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192));
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
|
||||
Fit();
|
||||
finalize();
|
||||
}
|
||||
|
||||
MsgNoUpdates::~MsgNoUpdates() {}
|
||||
|
|
|
|||
|
|
@ -419,7 +419,7 @@ int mode_icon_px_size()
|
|||
|
||||
wxBitmap create_menu_bitmap(const std::string& bmp_name)
|
||||
{
|
||||
return create_scaled_bitmap(bmp_name, nullptr, 16, false, true);
|
||||
return create_scaled_bitmap(bmp_name, nullptr, 16, false, "", true);
|
||||
}
|
||||
|
||||
// win is used to get a correct em_unit value
|
||||
|
|
@ -429,6 +429,7 @@ wxBitmap create_scaled_bitmap( const std::string& bmp_name_in,
|
|||
wxWindow *win/* = nullptr*/,
|
||||
const int px_cnt/* = 16*/,
|
||||
const bool grayscale/* = false*/,
|
||||
const std::string& new_color/* = std::string()*/, // color witch will used instead of orange
|
||||
const bool menu_bitmap/* = false*/)
|
||||
{
|
||||
static Slic3r::GUI::BitmapCache cache;
|
||||
|
|
@ -446,7 +447,7 @@ wxBitmap create_scaled_bitmap( const std::string& bmp_name_in,
|
|||
Slic3r::GUI::wxGetApp().dark_mode();
|
||||
|
||||
// Try loading an SVG first, then PNG if SVG is not found:
|
||||
wxBitmap *bmp = cache.load_svg(bmp_name, width, height, grayscale, dark_mode);
|
||||
wxBitmap *bmp = cache.load_svg(bmp_name, width, height, grayscale, dark_mode, new_color);
|
||||
if (bmp == nullptr) {
|
||||
bmp = cache.load_png(bmp_name, width, height, grayscale);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ int mode_icon_px_size();
|
|||
wxBitmap create_menu_bitmap(const std::string& bmp_name);
|
||||
|
||||
wxBitmap create_scaled_bitmap(const std::string& bmp_name, wxWindow *win = nullptr,
|
||||
const int px_cnt = 16, const bool grayscale = false, const bool menu_bitmap = false);
|
||||
const int px_cnt = 16, const bool grayscale = false,
|
||||
const std::string& new_color = std::string(), // color witch will used instead of orange
|
||||
const bool menu_bitmap = false);
|
||||
|
||||
std::vector<wxBitmap*> get_extruder_color_icons(bool thin_icon = false);
|
||||
|
||||
|
|
|
|||
|
|
@ -137,8 +137,7 @@ struct Updates
|
|||
|
||||
|
||||
wxDEFINE_EVENT(EVT_SLIC3R_VERSION_ONLINE, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_SLIC3R_ALPHA_VERSION_ONLINE, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_SLIC3R_BETA_VERSION_ONLINE, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_SLIC3R_EXPERIMENTAL_VERSION_ONLINE, wxCommandEvent);
|
||||
|
||||
struct PresetUpdater::priv
|
||||
{
|
||||
|
|
@ -189,7 +188,7 @@ PresetUpdater::priv::priv()
|
|||
// Pull relevant preferences from AppConfig
|
||||
void PresetUpdater::priv::set_download_prefs(AppConfig *app_config)
|
||||
{
|
||||
enabled_version_check = app_config->get("version_check") == "1";
|
||||
enabled_version_check = app_config->get("notify_release") != "none";
|
||||
version_check_url = app_config->version_check_url();
|
||||
enabled_config_update = app_config->get("preset_update") == "1" && !app_config->legacy_datadir();
|
||||
}
|
||||
|
|
@ -276,8 +275,9 @@ void PresetUpdater::priv::parse_version_string(const std::string& body) const
|
|||
version = body.substr(0, first_nl_pos);
|
||||
else
|
||||
version = body;
|
||||
if (!Semver::parse(version)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << format("Received invalid contents from `%1%`: Not a correct semver: `%2%`", SLIC3R_APP_NAME, version);
|
||||
boost::optional<Semver> release_version = Semver::parse(version);
|
||||
if (!release_version) {
|
||||
BOOST_LOG_TRIVIAL(error) << format("Received invalid contents from `%1%`: Not a correct semver: `%2%`", SLIC3R_APP_NAME, version);
|
||||
return;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << format("Got %1% online version: `%2%`. Sending to GUI thread...", SLIC3R_APP_NAME, version);
|
||||
|
|
@ -286,6 +286,7 @@ void PresetUpdater::priv::parse_version_string(const std::string& body) const
|
|||
GUI::wxGetApp().QueueEvent(evt);
|
||||
|
||||
// alpha / beta version
|
||||
std::vector<std::string> prerelease_versions;
|
||||
size_t nexn_nl_pos = first_nl_pos;
|
||||
while (nexn_nl_pos != std::string::npos && body.size() > nexn_nl_pos + 1) {
|
||||
const auto last_nl_pos = nexn_nl_pos;
|
||||
|
|
@ -300,28 +301,36 @@ void PresetUpdater::priv::parse_version_string(const std::string& body) const
|
|||
if (line.substr(0, 6) == "alpha=") {
|
||||
version = line.substr(6);
|
||||
if (!Semver::parse(version)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << format("Received invalid contents for alpha release from `%1%`: Not a correct semver: `%2%`", SLIC3R_APP_NAME, version);
|
||||
BOOST_LOG_TRIVIAL(error) << format("Received invalid contents for alpha release from `%1%`: Not a correct semver: `%2%`", SLIC3R_APP_NAME, version);
|
||||
return;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << format("Got %1% online version of alpha release: `%2%`. Sending to GUI thread...", SLIC3R_APP_NAME, version);
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_ALPHA_VERSION_ONLINE);
|
||||
evt->SetString(GUI::from_u8(version));
|
||||
GUI::wxGetApp().QueueEvent(evt);
|
||||
|
||||
prerelease_versions.emplace_back(version);
|
||||
// beta
|
||||
}
|
||||
else if (line.substr(0, 5) == "beta=") {
|
||||
version = line.substr(5);
|
||||
if (!Semver::parse(version)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << format("Received invalid contents for beta release from `%1%`: Not a correct semver: `%2%`", SLIC3R_APP_NAME, version);
|
||||
BOOST_LOG_TRIVIAL(error) << format("Received invalid contents for beta release from `%1%`: Not a correct semver: `%2%`", SLIC3R_APP_NAME, version);
|
||||
return;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << format("Got %1% online version of beta release: `%2%`. Sending to GUI thread...", SLIC3R_APP_NAME, version);
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_BETA_VERSION_ONLINE);
|
||||
evt->SetString(GUI::from_u8(version));
|
||||
GUI::wxGetApp().QueueEvent(evt);
|
||||
prerelease_versions.emplace_back(version);
|
||||
}
|
||||
}
|
||||
// find recent version that is newer than last full release.
|
||||
boost::optional<Semver> recent_version;
|
||||
for (const std::string& ver_string : prerelease_versions) {
|
||||
boost::optional<Semver> ver = Semver::parse(ver_string);
|
||||
if (ver && *release_version < *ver && ((recent_version && *recent_version < *ver) || !recent_version)) {
|
||||
recent_version = ver;
|
||||
version = ver_string;
|
||||
}
|
||||
}
|
||||
if (recent_version) {
|
||||
BOOST_LOG_TRIVIAL(info) << format("Got %1% online version: `%2%`. Sending to GUI thread...", SLIC3R_APP_NAME, version);
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_EXPERIMENTAL_VERSION_ONLINE);
|
||||
evt->SetString(GUI::from_u8(version));
|
||||
GUI::wxGetApp().QueueEvent(evt);
|
||||
}
|
||||
}
|
||||
|
||||
// Download vendor indices. Also download new bundles if an index indicates there's a new one available.
|
||||
|
|
@ -741,8 +750,8 @@ void PresetUpdater::sync(PresetBundle *preset_bundle)
|
|||
|
||||
void PresetUpdater::slic3r_update_notify()
|
||||
{
|
||||
if (! p->enabled_version_check) { return; }
|
||||
|
||||
if (! p->enabled_version_check)
|
||||
return;
|
||||
auto* app_config = GUI::wxGetApp().app_config;
|
||||
const auto ver_online_str = app_config->get("version_online");
|
||||
const auto ver_online = Semver::parse(ver_online_str);
|
||||
|
|
@ -754,7 +763,7 @@ void PresetUpdater::slic3r_update_notify()
|
|||
GUI::MsgUpdateSlic3r notification(Slic3r::SEMVER, *ver_online);
|
||||
notification.ShowModal();
|
||||
if (notification.disable_version_check()) {
|
||||
app_config->set("version_check", "0");
|
||||
app_config->set("notify_release", "none");
|
||||
p->enabled_version_check = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ private:
|
|||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_SLIC3R_VERSION_ONLINE, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_SLIC3R_ALPHA_VERSION_ONLINE, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_SLIC3R_BETA_VERSION_ONLINE, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_SLIC3R_EXPERIMENTAL_VERSION_ONLINE, wxCommandEvent);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Error
|
|||
|
||||
bool res = true;
|
||||
|
||||
auto url = make_url((boost::format("printer/model/%1%") % port).str());
|
||||
auto url = upload_data.start_print?make_url((boost::format("printer/job/%1%") % port).str()):make_url((boost::format("printer/model/%1%") % port).str());
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%, group: %7%")
|
||||
% name
|
||||
|
|
@ -125,6 +125,10 @@ bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Error
|
|||
http.form_add("group", upload_data.group);
|
||||
}
|
||||
|
||||
if(upload_data.start_print) {
|
||||
http.form_add("name", upload_filename.string());
|
||||
}
|
||||
|
||||
http.form_add("a", "upload")
|
||||
.form_add_file("filename", upload_data.source_path.string(), upload_filename.string())
|
||||
.on_complete([&](std::string body, unsigned status) {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public:
|
|||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
|
||||
bool has_auto_discovery() const override { return false; }
|
||||
bool can_test() const override { return true; }
|
||||
bool can_start_print() const override { return false; }
|
||||
bool can_start_print() const override { return true; }
|
||||
bool supports_multiple_printers() const override { return true; }
|
||||
std::string get_host() const override { return host; }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue