Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_imgui_slider
This commit is contained in:
commit
67533da405
24 changed files with 523 additions and 255 deletions
|
@ -135,7 +135,7 @@ ArchiveData extract_sla_archive(const std::string &zipfname,
|
||||||
ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
|
ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
|
||||||
double px_w, double px_h)
|
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) {
|
for (const marchsq::Ring &ring : rings) {
|
||||||
Polygon poly; Points &pts = poly.points;
|
Polygon poly; Points &pts = poly.points;
|
||||||
|
@ -147,7 +147,7 @@ ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
|
||||||
polys.emplace_back(poly);
|
polys.emplace_back(poly);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reverse the raster transformations
|
// TODO: Is a union necessary?
|
||||||
return union_ex(polys);
|
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()};
|
png::ReadBuf rb{arch.images[i].buf.data(), arch.images[i].buf.size()};
|
||||||
if (!png::decode_png(rb, img)) return;
|
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);
|
ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h);
|
||||||
|
|
||||||
// Invert the raster transformations indicated in
|
// Invert the raster transformations indicated in the profile metadata
|
||||||
// the profile metadata
|
|
||||||
invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height);
|
invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height);
|
||||||
|
|
||||||
slices[i] = std::move(expolys);
|
slices[i] = std::move(expolys);
|
||||||
|
@ -310,7 +310,24 @@ ConfigSubstitutions import_sla_archive(
|
||||||
std::string exclude_entries{"thumbnail"};
|
std::string exclude_entries{"thumbnail"};
|
||||||
ArchiveData arch = extract_sla_archive(zipfname, exclude_entries);
|
ArchiveData arch = extract_sla_archive(zipfname, exclude_entries);
|
||||||
DynamicPrintConfig profile_in, profile_use;
|
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
|
// 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.
|
// then replace it with the readed profile to report that it was empty.
|
||||||
|
|
|
@ -1263,10 +1263,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
||||||
instances[instance]->get_mirror()
|
instances[instance]->get_mirror()
|
||||||
);
|
);
|
||||||
|
|
||||||
z -= instances[instance]->get_offset()(2);
|
z -= instances[instance]->get_offset().z();
|
||||||
|
|
||||||
// Lower part per-instance bounding boxes
|
// Displacement (in instance coordinates) to be applied to place the upper parts
|
||||||
std::vector<BoundingBoxf3> lower_bboxes { instances.size() };
|
Vec3d local_displace = Vec3d::Zero();
|
||||||
|
|
||||||
for (ModelVolume *volume : volumes) {
|
for (ModelVolume *volume : volumes) {
|
||||||
const auto volume_matrix = volume->get_matrix();
|
const auto volume_matrix = volume->get_matrix();
|
||||||
|
@ -1287,7 +1287,6 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
||||||
lower->add_volume(*volume);
|
lower->add_volume(*volume);
|
||||||
}
|
}
|
||||||
else if (! volume->mesh().empty()) {
|
else if (! volume->mesh().empty()) {
|
||||||
|
|
||||||
// Transform the mesh by the combined transformation matrix.
|
// Transform the mesh by the combined transformation matrix.
|
||||||
// Flip the triangles in case the composite transformation is left handed.
|
// Flip the triangles in case the composite transformation is left handed.
|
||||||
TriangleMesh mesh(volume->mesh());
|
TriangleMesh mesh(volume->mesh());
|
||||||
|
@ -1327,13 +1326,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
||||||
assert(vol->config.id() != volume->config.id());
|
assert(vol->config.id() != volume->config.id());
|
||||||
vol->set_material(volume->material_id(), *volume->material());
|
vol->set_material(volume->material_id(), *volume->material());
|
||||||
|
|
||||||
// Compute the lower part instances' bounding boxes to figure out where to place
|
// Compute the displacement (in instance coordinates) to be applied to place the upper parts
|
||||||
// the upper part
|
// The upper part displacement is set to half of the lower part bounding box
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
|
// this is done in hope at least a part of the upper part will always be visible and draggable
|
||||||
for (size_t i = 0; i < instances.size(); i++) {
|
local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
|
||||||
lower_bboxes[i].merge(instances[i]->transform_mesh_bounding_box(lower_mesh, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1341,17 +1337,18 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
||||||
ModelObjectPtrs res;
|
ModelObjectPtrs res;
|
||||||
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) {
|
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) {
|
||||||
upper->invalidate_bounding_box();
|
if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
|
||||||
upper->center_around_origin();
|
upper->center_around_origin();
|
||||||
|
upper->translate_instances(-upper->origin_translation);
|
||||||
|
upper->origin_translation = Vec3d::Zero();
|
||||||
|
}
|
||||||
|
|
||||||
// Reset instance transformation except offset and Z-rotation
|
// 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];
|
auto &instance = upper->instances[i];
|
||||||
const Vec3d offset = instance->get_offset();
|
const Vec3d offset = instance->get_offset();
|
||||||
const double rot_z = instance->get_rotation()(2);
|
const double rot_z = instance->get_rotation().z();
|
||||||
// The upper part displacement is set to half of the lower part bounding box
|
const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), instance->get_rotation()) * local_displace;
|
||||||
// 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));
|
|
||||||
|
|
||||||
instance->set_transformation(Geometry::Transformation());
|
instance->set_transformation(Geometry::Transformation());
|
||||||
instance->set_offset(offset + displace);
|
instance->set_offset(offset + displace);
|
||||||
|
@ -1361,14 +1358,16 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
||||||
res.push_back(upper);
|
res.push_back(upper);
|
||||||
}
|
}
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) {
|
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) {
|
||||||
lower->invalidate_bounding_box();
|
if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
|
||||||
lower->center_around_origin();
|
lower->center_around_origin();
|
||||||
|
lower->translate_instances(-lower->origin_translation);
|
||||||
|
lower->origin_translation = Vec3d::Zero();
|
||||||
|
}
|
||||||
|
|
||||||
// Reset instance transformation except offset and Z-rotation
|
// Reset instance transformation except offset and Z-rotation
|
||||||
for (auto *instance : lower->instances) {
|
for (auto *instance : lower->instances) {
|
||||||
const Vec3d offset = instance->get_offset();
|
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_transformation(Geometry::Transformation());
|
||||||
instance->set_offset(offset);
|
instance->set_offset(offset);
|
||||||
instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, rot_z));
|
instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, rot_z));
|
||||||
|
|
|
@ -1113,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,
|
// 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.
|
// start selection always left most edges for every node to construct CCW polygons.
|
||||||
// Assumes that graph is planar (without self-intersection edges)
|
// 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);
|
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
|
// When there is no next arc, then is returned original_arc or edge with is marked as used
|
||||||
|
@ -1153,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]; });
|
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) {
|
for (size_t node_idx = 0; node_idx < graph.all_border_points; ++node_idx) {
|
||||||
const MMU_Graph::Node &node = graph.nodes[node_idx];
|
const MMU_Graph::Node &node = graph.nodes[node_idx];
|
||||||
|
|
||||||
|
@ -1183,12 +1183,11 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MM
|
||||||
p_arc = &next;
|
p_arc = &next;
|
||||||
} while (graph.nodes[p_arc->to_idx].point != start_p || !all_arc_used(graph.nodes[p_arc->to_idx]));
|
} 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 (Polygon poly = to_polygon(face_lines); poly.is_counter_clockwise() && poly.is_valid())
|
||||||
if (poly.is_counter_clockwise() && poly.is_valid())
|
expolygons_segments[arc.color].emplace_back(std::move(poly));
|
||||||
polygons_segments.emplace_back(poly, arc.color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return polygons_segments;
|
return expolygons_segments;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used in remove_multiple_edges_in_vertices()
|
// Used in remove_multiple_edges_in_vertices()
|
||||||
|
@ -1270,7 +1269,7 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cut_segmented_layers(const std::vector<ExPolygons> &input_expolygons,
|
static void cut_segmented_layers(const std::vector<ExPolygons> &input_expolygons,
|
||||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> &segmented_regions,
|
std::vector<std::vector<ExPolygons>> &segmented_regions,
|
||||||
const float cut_width,
|
const float cut_width,
|
||||||
const std::function<void()> &throw_on_cancel_callback)
|
const std::function<void()> &throw_on_cancel_callback)
|
||||||
{
|
{
|
||||||
|
@ -1278,12 +1277,11 @@ static void cut_segmented_layers(const std::vector<ExPolygons>
|
||||||
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) {
|
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) {
|
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||||
throw_on_cancel_callback();
|
throw_on_cancel_callback();
|
||||||
std::vector<std::pair<ExPolygon, size_t>> segmented_regions_cuts;
|
const size_t num_extruders_plus_one = segmented_regions[layer_idx].size();
|
||||||
for (const std::pair<ExPolygon, size_t> &colored_expoly : segmented_regions[layer_idx]) {
|
std::vector<ExPolygons> segmented_regions_cuts(num_extruders_plus_one); // Indexed by extruder_id
|
||||||
ExPolygons cut_colored_expoly = diff_ex(colored_expoly.first, offset_ex(input_expolygons[layer_idx], cut_width));
|
for (size_t extruder_idx = 0; extruder_idx < num_extruders_plus_one; ++extruder_idx)
|
||||||
for (ExPolygon &expoly : cut_colored_expoly)
|
if (const ExPolygons &ex_polygons = segmented_regions[layer_idx][extruder_idx]; !ex_polygons.empty())
|
||||||
segmented_regions_cuts.emplace_back(std::move(expoly), colored_expoly.second);
|
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);
|
segmented_regions[layer_idx] = std::move(segmented_regions_cuts);
|
||||||
}
|
}
|
||||||
}); // end of parallel_for
|
}); // end of parallel_for
|
||||||
|
@ -1323,7 +1321,7 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
||||||
// Project upwards pointing painted triangles over top surfaces,
|
// Project upwards pointing painted triangles over top surfaces,
|
||||||
// project downards pointing painted triangles over bottom surfaces.
|
// project downards pointing painted triangles over bottom surfaces.
|
||||||
std::vector<std::vector<Polygons>> top_raw(num_extruders), bottom_raw(num_extruders);
|
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();
|
Transform3d object_trafo = print_object.trafo_centered();
|
||||||
|
|
||||||
#ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
|
#ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
|
||||||
|
@ -1532,31 +1530,42 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
||||||
return triangles_by_color_merged;
|
return triangles_by_color_merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<std::vector<std::pair<ExPolygon, size_t>>> merge_segmented_layers(
|
static std::vector<std::vector<ExPolygons>> merge_segmented_layers(
|
||||||
const std::vector<std::vector<std::pair<ExPolygon, size_t>>> &segmented_regions,
|
const std::vector<std::vector<ExPolygons>> &segmented_regions,
|
||||||
std::vector<std::vector<ExPolygons>> &&top_and_bottom_layers,
|
std::vector<std::vector<ExPolygons>> &&top_and_bottom_layers,
|
||||||
|
const size_t num_extruders,
|
||||||
const std::function<void()> &throw_on_cancel_callback)
|
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";
|
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 (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();
|
throw_on_cancel_callback();
|
||||||
// Zero is the default color of the volume.
|
if (!segmented_regions[layer_idx][extruder_id].empty()) {
|
||||||
if(colored_expoly.second == 0)
|
ExPolygons segmented_regions_trimmed = segmented_regions[layer_idx][extruder_id];
|
||||||
continue;
|
for (const std::vector<ExPolygons> &top_and_bottom_by_extruder : top_and_bottom_layers)
|
||||||
ExPolygons cut_colored_expoly = {colored_expoly.first};
|
if (!top_and_bottom_by_extruder[layer_idx].empty() && !segmented_regions_trimmed.empty())
|
||||||
for (const std::vector<ExPolygons> &top_and_bottom_layer : top_and_bottom_layers)
|
segmented_regions_trimmed = diff_ex(segmented_regions_trimmed, top_and_bottom_by_extruder[layer_idx]);
|
||||||
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][extruder_id - 1] = std::move(segmented_regions_trimmed);
|
||||||
segmented_regions_merged[layer_idx].emplace_back(std::move(ex_poly), colored_expoly.second - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t color_idx = 1; color_idx < top_and_bottom_layers.size(); ++color_idx)
|
if (!top_and_bottom_layers[extruder_id][layer_idx].empty()) {
|
||||||
for (ExPolygon &expoly : top_and_bottom_layers[color_idx][layer_idx])
|
bool was_top_and_bottom_empty = segmented_regions_merged[layer_idx][extruder_id - 1].empty();
|
||||||
segmented_regions_merged[layer_idx].emplace_back(std::move(expoly), color_idx - 1);
|
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
|
}); // end of parallel_for
|
||||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging segmented layers in parallel - end";
|
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging segmented layers in parallel - end";
|
||||||
|
@ -1565,7 +1574,7 @@ static std::vector<std::vector<std::pair<ExPolygon, size_t>>> merge_segmented_la
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
#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"};
|
const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "yellow"};
|
||||||
coordf_t stroke_width = scale_(0.05);
|
coordf_t stroke_width = scale_(0.05);
|
||||||
|
@ -1574,12 +1583,12 @@ static void export_regions_to_svg(const std::string &path, const std::vector<std
|
||||||
::Slic3r::SVG svg(path.c_str(), bbox);
|
::Slic3r::SVG svg(path.c_str(), bbox);
|
||||||
|
|
||||||
svg.draw_outline(lslices, "green", "lime", stroke_width);
|
svg.draw_outline(lslices, "green", "lime", stroke_width);
|
||||||
for (const std::pair<ExPolygon, size_t> ®ion : regions) {
|
for (const ExPolygons &by_extruder : regions) {
|
||||||
int region_color = int(region.second);
|
size_t extrude_idx = &by_extruder - ®ions.front();
|
||||||
if (region_color >= 0 && region_color < int(colors.size()))
|
if (extrude_idx >= 0 && extrude_idx < int(colors.size()))
|
||||||
svg.draw(region.first, colors[region_color]);
|
svg.draw(by_extruder, colors[extrude_idx], stroke_width);
|
||||||
else
|
else
|
||||||
svg.draw(region.first, "black");
|
svg.draw(by_extruder, "black", stroke_width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // MMU_SEGMENTATION_DEBUG_REGIONS
|
#endif // MMU_SEGMENTATION_DEBUG_REGIONS
|
||||||
|
@ -1667,20 +1676,23 @@ static bool has_layer_only_one_color(const std::vector<std::vector<ColoredLine>>
|
||||||
return true;
|
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());
|
const size_t num_extruders = print_object.print()->config().nozzle_diameter.size();
|
||||||
std::vector<std::vector<PaintedLine>> painted_lines(print_object.layers().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::array<std::mutex, 64> painted_lines_mutex;
|
||||||
std::vector<EdgeGrid::Grid> edge_grids(print_object.layers().size());
|
std::vector<EdgeGrid::Grid> edge_grids(num_layers);
|
||||||
const ConstLayerPtrsAdaptor layers = print_object.layers();
|
const ConstLayerPtrsAdaptor layers = print_object.layers();
|
||||||
std::vector<ExPolygons> input_expolygons(layers.size());
|
std::vector<ExPolygons> input_expolygons(num_layers);
|
||||||
|
|
||||||
throw_on_cancel_callback();
|
throw_on_cancel_callback();
|
||||||
|
|
||||||
// Merge all regions and remove small holes
|
// Merge all regions and remove small holes
|
||||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - slices preparation in parallel - begin";
|
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) {
|
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||||
throw_on_cancel_callback();
|
throw_on_cancel_callback();
|
||||||
ExPolygons ex_polygons;
|
ExPolygons ex_polygons;
|
||||||
|
@ -1711,7 +1723,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||||
}); // end of parallel_for
|
}); // end of parallel_for
|
||||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - slices preparation in parallel - end";
|
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();
|
throw_on_cancel_callback();
|
||||||
BoundingBox bbox(get_extents(layers[layer_idx]->regions()));
|
BoundingBox bbox(get_extents(layers[layer_idx]->regions()));
|
||||||
bbox.merge(get_extents(input_expolygons[layer_idx]));
|
bbox.merge(get_extents(input_expolygons[layer_idx]));
|
||||||
|
@ -1723,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";
|
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - projection of painted triangles - begin";
|
||||||
for (const ModelVolume *mv : print_object.model_object()->volumes) {
|
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 + 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) {
|
||||||
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) {
|
|
||||||
for (size_t extruder_idx = range.begin(); extruder_idx < range.end(); ++extruder_idx) {
|
for (size_t extruder_idx = range.begin(); extruder_idx < range.end(); ++extruder_idx) {
|
||||||
throw_on_cancel_callback();
|
throw_on_cancel_callback();
|
||||||
const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx));
|
const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx));
|
||||||
|
@ -1732,7 +1743,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const Transform3f tr = print_object.trafo().cast<float>() * mv->get_matrix().cast<float>();
|
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) {
|
for (size_t facet_idx = range.begin(); facet_idx < range.end(); ++facet_idx) {
|
||||||
float min_z = std::numeric_limits<float>::max();
|
float min_z = std::numeric_limits<float>::max();
|
||||||
float max_z = std::numeric_limits<float>::lowest();
|
float max_z = std::numeric_limits<float>::lowest();
|
||||||
|
@ -1748,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(); });
|
std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); });
|
||||||
|
|
||||||
// Find lowest slice not below the triangle.
|
// 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; });
|
[](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; });
|
[](float z, const Layer *l1) { return z < l1->slice_z; });
|
||||||
--last_layer;
|
--last_layer;
|
||||||
|
|
||||||
for (auto layer_it = first_layer; layer_it != (last_layer + 1); ++layer_it) {
|
for (auto layer_it = first_layer; layer_it != (last_layer + 1); ++layer_it) {
|
||||||
const Layer *layer = *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())
|
if (input_expolygons[layer_idx].empty() || facet[0].z() > layer->slice_z || layer->slice_z > facet[2].z())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1799,7 +1810,7 @@ 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(); });
|
<< 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";
|
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) {
|
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||||
throw_on_cancel_callback();
|
throw_on_cancel_callback();
|
||||||
if (!painted_lines[layer_idx].empty()) {
|
if (!painted_lines[layer_idx].empty()) {
|
||||||
|
@ -1832,8 +1843,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||||
assert(!color_poly.front().empty());
|
assert(!color_poly.front().empty());
|
||||||
if (has_layer_only_one_color(color_poly)) {
|
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.
|
// 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][size_t(color_poly.front().front().color)] = input_expolygons[layer_idx];
|
||||||
segmented_regions[layer_idx].emplace_back(ex_polygon, size_t(color_poly.front().front().color));
|
|
||||||
} else {
|
} else {
|
||||||
MMU_Graph graph = build_graph(layer_idx, color_poly);
|
MMU_Graph graph = build_graph(layer_idx, color_poly);
|
||||||
remove_multiple_edges_in_vertices(graph, color_poly);
|
remove_multiple_edges_in_vertices(graph, color_poly);
|
||||||
|
@ -1846,9 +1856,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||||
}
|
}
|
||||||
#endif // MMU_SEGMENTATION_DEBUG_GRAPH
|
#endif // MMU_SEGMENTATION_DEBUG_GRAPH
|
||||||
|
|
||||||
std::vector<std::pair<Polygon, size_t>> segmentation = extract_colored_segments(graph);
|
segmented_regions[layer_idx] = extract_colored_segments(graph, num_extruders);
|
||||||
for (std::pair<Polygon, size_t> ®ion : segmentation)
|
|
||||||
segmented_regions[layer_idx].emplace_back(std::move(region));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||||
|
@ -1868,11 +1876,11 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||||
throw_on_cancel_callback();
|
throw_on_cancel_callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
// return segmented_regions;
|
// 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);
|
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();
|
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();
|
throw_on_cancel_callback();
|
||||||
|
|
||||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||||
|
|
|
@ -11,7 +11,7 @@ class PrintObject;
|
||||||
class ExPolygon;
|
class ExPolygon;
|
||||||
|
|
||||||
// Returns MMU segmentation based on painting in MMU segmentation gizmo
|
// 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
|
} // 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 {
|
static std::vector<std::string> s_Preset_sla_material_options {
|
||||||
|
"material_colour",
|
||||||
"material_type",
|
"material_type",
|
||||||
"initial_layer_height",
|
"initial_layer_height",
|
||||||
"bottle_cost",
|
"bottle_cost",
|
||||||
|
|
|
@ -3163,6 +3163,13 @@ void PrintConfigDef::init_sla_params()
|
||||||
|
|
||||||
|
|
||||||
// SLA Material settings.
|
// SLA Material settings.
|
||||||
|
|
||||||
|
def = this->add("material_colour", coStrings);
|
||||||
|
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 ConfigOptionStrings{ "#29B2B2" });
|
||||||
|
|
||||||
def = this->add("material_type", coString);
|
def = this->add("material_type", coString);
|
||||||
def->label = L("SLA material type");
|
def->label = L("SLA material type");
|
||||||
def->tooltip = 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)
|
static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_cancel)
|
||||||
{
|
{
|
||||||
// Returns MMU segmentation based on painting in MMU segmentation gizmo
|
// 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());
|
assert(segmentation.size() == print_object.layer_count());
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
tbb::blocked_range<size_t>(0, segmentation.size(), std::max(segmentation.size() / 128, size_t(1))),
|
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;
|
bool layer_split = false;
|
||||||
for (size_t extruder_id = 0; extruder_id < num_extruders; ++ extruder_id) {
|
for (size_t extruder_id = 0; extruder_id < num_extruders; ++ extruder_id) {
|
||||||
ByExtruder ®ion = by_extruder[extruder_id];
|
ByExtruder ®ion = by_extruder[extruder_id];
|
||||||
for (const std::pair<ExPolygon, size_t> &colored_polygon : segmentation[layer_id])
|
append(region.expolygons, std::move(segmentation[layer_id][extruder_id]));
|
||||||
if (colored_polygon.second == extruder_id)
|
|
||||||
region.expolygons.emplace_back(std::move(colored_polygon.first));
|
|
||||||
if (! region.expolygons.empty()) {
|
if (! region.expolygons.empty()) {
|
||||||
region.bbox = get_extents(region.expolygons);
|
region.bbox = get_extents(region.expolygons);
|
||||||
layer_split = true;
|
layer_split = true;
|
||||||
|
@ -632,6 +630,13 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
|
||||||
if (mine.empty())
|
if (mine.empty())
|
||||||
break;
|
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()) {
|
if (! mine.empty()) {
|
||||||
ByRegion &dst = by_region[layerm.region().print_object_region_id()];
|
ByRegion &dst = by_region[layerm.region().print_object_region_id()];
|
||||||
if (dst.expolygons.empty()) {
|
if (dst.expolygons.empty()) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
#include "SlicesToTriangleMesh.hpp"
|
#include "SlicesToTriangleMesh.hpp"
|
||||||
|
|
||||||
|
@ -22,11 +23,16 @@ inline indexed_triangle_set wall_strip(const Polygon &poly,
|
||||||
|
|
||||||
ret.vertices.reserve(ret.vertices.size() + 2 *offs);
|
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)
|
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)
|
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) {
|
for (size_t i = startidx + 1; i < startidx + offs; ++i) {
|
||||||
ret.indices.emplace_back(i - 1, i, i + offs - 1);
|
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 &upper = slices[i + 1];
|
||||||
const ExPolygons &lower = slices[i];
|
const ExPolygons &lower = slices[i];
|
||||||
|
|
||||||
ExPolygons dff1 = diff_ex(lower, upper);
|
// Small 0 area artefacts can be created by diff_ex, and the
|
||||||
ExPolygons dff2 = diff_ex(upper, lower);
|
// tesselation also can create 0 area triangles. These will be removed
|
||||||
its_merge(layers[i], triangulate_expolygons_3d(dff1, grid[i], NORMALS_UP));
|
// by its_remove_degenerate_faces.
|
||||||
its_merge(layers[i], triangulate_expolygons_3d(dff2, grid[i], NORMALS_DOWN));
|
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]));
|
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 ) {
|
auto merge_fn = []( const indexed_triangle_set &a, const indexed_triangle_set &b ) {
|
||||||
|
@ -99,22 +107,17 @@ indexed_triangle_set slices_to_mesh(
|
||||||
auto ret = execution::reduce(ex_tbb, layers.begin(), layers.end(),
|
auto ret = execution::reduce(ex_tbb, layers.begin(), layers.end(),
|
||||||
indexed_triangle_set{}, merge_fn);
|
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, triangulate_expolygons_3d(slices.front(), zmin, NORMALS_DOWN));
|
||||||
its_merge(ret, straight_walls(slices.front(), zmin, grid.front()));
|
its_merge(ret, straight_walls(slices.front(), zmin, grid.front()));
|
||||||
its_merge(ret, triangulate_expolygons_3d(slices.back(), grid.back(), NORMALS_UP));
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,11 +127,9 @@ void slices_to_mesh(indexed_triangle_set & mesh,
|
||||||
double lh,
|
double lh,
|
||||||
double ilh)
|
double ilh)
|
||||||
{
|
{
|
||||||
std::vector<indexed_triangle_set> wall_meshes(slices.size());
|
|
||||||
std::vector<float> grid(slices.size(), zmin + ilh);
|
std::vector<float> grid(slices.size(), zmin + ilh);
|
||||||
|
|
||||||
for (size_t i = 1; i < grid.size(); ++i)
|
for (size_t i = 1; i < grid.size(); ++i) grid[i] = grid[i - 1] + lh;
|
||||||
grid[i] = grid[i - 1] + lh;
|
|
||||||
|
|
||||||
indexed_triangle_set cntr = slices_to_mesh(slices, zmin, grid);
|
indexed_triangle_set cntr = slices_to_mesh(slices, zmin, grid);
|
||||||
its_merge(mesh, cntr);
|
its_merge(mesh, cntr);
|
||||||
|
|
|
@ -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 its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit)
|
||||||
{
|
{
|
||||||
int last = 0;
|
auto it = std::remove_if(its.indices.begin(), its.indices.end(), [](auto &face) {
|
||||||
for (int i = 0; i < int(its.indices.size()); ++ i) {
|
return face(0) == face(1) || face(0) == face(2) || face(1) == face(2);
|
||||||
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)
|
int removed = std::distance(it, its.indices.end());
|
||||||
its.indices[last] = its.indices[i];
|
its.indices.erase(it, its.indices.end());
|
||||||
++ last;
|
|
||||||
}
|
if (removed && shrink_to_fit)
|
||||||
}
|
|
||||||
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();
|
its.indices.shrink_to_fit();
|
||||||
}
|
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "../GUI/GUI_App.hpp"
|
#include "../GUI/GUI_App.hpp"
|
||||||
#include "../GUI/I18N.hpp"
|
#include "../GUI/I18N.hpp"
|
||||||
#include "../GUI/MainFrame.hpp"
|
#include "../GUI/MainFrame.hpp"
|
||||||
|
#include "../GUI/MsgDialog.hpp"
|
||||||
|
|
||||||
#include <wx/richmsgdlg.h>
|
#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);
|
SnapshotDB::singleton().take_snapshot(app_config, reason, comment);
|
||||||
return true;
|
return true;
|
||||||
} catch (std::exception &err) {
|
} 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 has encountered an error while taking a configuration snapshot.") + "\n\n" + from_u8(err.what()) + "\n\n" + from_u8(message),
|
||||||
_L("PrusaSlicer error"),
|
_L("PrusaSlicer error"),
|
||||||
wxYES_NO);
|
wxYES_NO);
|
||||||
|
|
|
@ -1131,6 +1131,23 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
|
||||||
if (config == nullptr)
|
if (config == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
unsigned char rgb[3];
|
||||||
|
std::vector<Color> colors;
|
||||||
|
|
||||||
|
if (static_cast<PrinterTechnology>(config->opt_int("printer_technology")) == ptSLA)
|
||||||
|
{
|
||||||
|
const ConfigOptionStrings* resin_clr = dynamic_cast<const ConfigOptionStrings*>(config->option("material_colour"));
|
||||||
|
if (resin_clr == nullptr)
|
||||||
|
return;
|
||||||
|
assert(resin_clr->values.size() == 1);
|
||||||
|
colors.resize(1);
|
||||||
|
|
||||||
|
const std::string& txt_color = config->opt_string("material_colour", 0);
|
||||||
|
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
|
||||||
|
colors[0].set(txt_color, rgb);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("extruder_colour"));
|
const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("extruder_colour"));
|
||||||
if (extruders_opt == nullptr)
|
if (extruders_opt == nullptr)
|
||||||
return;
|
return;
|
||||||
|
@ -1142,10 +1159,8 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
|
||||||
unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size());
|
unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size());
|
||||||
if (colors_count == 0)
|
if (colors_count == 0)
|
||||||
return;
|
return;
|
||||||
|
colors.resize(colors_count);
|
||||||
|
|
||||||
std::vector<Color> colors(colors_count);
|
|
||||||
|
|
||||||
unsigned char rgb[3];
|
|
||||||
for (unsigned int i = 0; i < colors_count; ++i) {
|
for (unsigned int i = 0; i < colors_count; ++i) {
|
||||||
const std::string& txt_color = config->opt_string("extruder_colour", i);
|
const std::string& txt_color = config->opt_string("extruder_colour", i);
|
||||||
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
|
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
|
||||||
|
@ -1156,6 +1171,7 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
|
||||||
colors[i].set(txt_color, rgb);
|
colors[i].set(txt_color, rgb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (GLVolume* volume : volumes) {
|
for (GLVolume* volume : volumes) {
|
||||||
if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || (volume->volume_idx() < 0))
|
if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || (volume->volume_idx() < 0))
|
||||||
|
|
|
@ -555,7 +555,7 @@ PagePrinters::PagePrinters(ConfigWizard *parent,
|
||||||
wizard_p()->on_printer_pick(this, evt);
|
wizard_p()->on_printer_pick(this, evt);
|
||||||
});
|
});
|
||||||
|
|
||||||
append(new wxStaticLine(this));
|
append(new StaticLine(this));
|
||||||
|
|
||||||
append(picker);
|
append(picker);
|
||||||
printer_pickers.push_back(picker);
|
printer_pickers.push_back(picker);
|
||||||
|
@ -2800,11 +2800,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
||||||
|
|
||||||
auto *vsizer = new wxBoxSizer(wxVERTICAL);
|
auto *vsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
|
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
wxStaticLine* hline = nullptr;
|
auto* hline = new StaticLine(this);
|
||||||
#ifdef _MSW_DARK_MODE
|
|
||||||
if (!NppDarkMode::IsEnabled())
|
|
||||||
#endif //_MSW_DARK_MODE
|
|
||||||
hline = new wxStaticLine(this);
|
|
||||||
p->btnsizer = new wxBoxSizer(wxHORIZONTAL);
|
p->btnsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
// Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling.
|
// Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling.
|
||||||
|
@ -2880,8 +2876,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
||||||
p->index->go_to(size_t{0});
|
p->index->go_to(size_t{0});
|
||||||
|
|
||||||
vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN);
|
vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN);
|
||||||
if (hline)
|
vsizer->Add(hline, 0, wxEXPAND | wxLEFT | wxRIGHT, VERTICAL_SPACING);
|
||||||
vsizer->Add(hline, 0, wxEXPAND);
|
|
||||||
vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN);
|
vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN);
|
||||||
|
|
||||||
SetSizer(vsizer);
|
SetSizer(vsizer);
|
||||||
|
|
|
@ -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.
|
// 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.
|
// 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.
|
// 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});
|
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"}, {"FLIP_TRIANGLE_NORMALS"sv});
|
||||||
else
|
else
|
||||||
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"});
|
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"});
|
||||||
|
|
|
@ -411,7 +411,7 @@ bool static check_old_linux_datadir(const wxString& app_name) {
|
||||||
"location again.\n\n"
|
"location again.\n\n"
|
||||||
"What do you want to do now?")) % SLIC3R_APP_NAME % new_path % old_path).str());
|
"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());
|
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"));
|
dlg.SetYesNoLabels(_L("Quit, I will move my data now"), _L("Start the application"));
|
||||||
if (dlg.ShowModal() != wxID_NO)
|
if (dlg.ShowModal() != wxID_NO)
|
||||||
return false;
|
return false;
|
||||||
|
@ -846,7 +846,7 @@ bool GUI_App::check_older_app_config(Semver current_version, bool backup)
|
||||||
return false;
|
return false;
|
||||||
BOOST_LOG_TRIVIAL(info) << "last app config file used: " << m_older_data_dir_path;
|
BOOST_LOG_TRIVIAL(info) << "last app config file used: " << m_older_data_dir_path;
|
||||||
// ask about using older data folder
|
// ask about using older data folder
|
||||||
wxRichMessageDialog msg(nullptr, backup ?
|
RichMessageDialog msg(nullptr, backup ?
|
||||||
wxString::Format(_L("PrusaSlicer detected another configuration folder at %s."
|
wxString::Format(_L("PrusaSlicer detected another configuration folder at %s."
|
||||||
"\nIts version is %s."
|
"\nIts version is %s."
|
||||||
"\nLast version you used in current configuration folder is %s."
|
"\nLast version you used in current configuration folder is %s."
|
||||||
|
@ -936,7 +936,7 @@ bool GUI_App::on_init_inner()
|
||||||
// win32 build on win64 and viceversa
|
// win32 build on win64 and viceversa
|
||||||
#ifdef _WIN64
|
#ifdef _WIN64
|
||||||
if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "") {
|
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."
|
_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/."
|
"\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/."
|
||||||
"\nDo you wish to continue?"),
|
"\nDo you wish to continue?"),
|
||||||
|
@ -946,7 +946,7 @@ bool GUI_App::on_init_inner()
|
||||||
}
|
}
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "64") {
|
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."
|
_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/."
|
"\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/."
|
||||||
"\nDo you wish to continue?"),
|
"\nDo you wish to continue?"),
|
||||||
|
@ -991,7 +991,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();
|
bool ssl_accept = app_config->get("tls_cert_store_accepted") == "yes" && ssl_cert_store == Http::tls_system_cert_store();
|
||||||
|
|
||||||
if (!msg.empty() && !ssl_accept) {
|
if (!msg.empty() && !ssl_accept) {
|
||||||
wxRichMessageDialog
|
RichMessageDialog
|
||||||
dlg(nullptr,
|
dlg(nullptr,
|
||||||
wxString::Format(_L("%s\nDo you want to continue?"), msg),
|
wxString::Format(_L("%s\nDo you want to continue?"), msg),
|
||||||
"PrusaSlicer", wxICON_QUESTION | wxYES_NO);
|
"PrusaSlicer", wxICON_QUESTION | wxYES_NO);
|
||||||
|
@ -1620,6 +1620,7 @@ void GUI_App::update_ui_from_settings()
|
||||||
m_force_colors_update = false;
|
m_force_colors_update = false;
|
||||||
mainframe->force_color_changed();
|
mainframe->force_color_changed();
|
||||||
mainframe->diff_dialog.force_color_changed();
|
mainframe->diff_dialog.force_color_changed();
|
||||||
|
mainframe->printhost_queue_dlg()->force_color_changed();
|
||||||
#ifdef _MSW_DARK_MODE
|
#ifdef _MSW_DARK_MODE
|
||||||
update_scrolls(mainframe);
|
update_scrolls(mainframe);
|
||||||
#endif //_MSW_DARK_MODE
|
#endif //_MSW_DARK_MODE
|
||||||
|
@ -2855,7 +2856,7 @@ bool GUI_App::open_browser_with_warning_dialog(const wxString& url, int flags/*
|
||||||
bool launch = true;
|
bool launch = true;
|
||||||
|
|
||||||
if (get_app_config()->get("suppress_hyperlinks").empty()) {
|
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"));
|
dialog.ShowCheckBox(_L("Remember my choice"));
|
||||||
int answer = dialog.ShowModal();
|
int answer = dialog.ShowModal();
|
||||||
launch = answer == wxID_YES;
|
launch = answer == wxID_YES;
|
||||||
|
|
|
@ -2044,8 +2044,7 @@ void ObjectList::split()
|
||||||
void ObjectList::merge(bool to_multipart_object)
|
void ObjectList::merge(bool to_multipart_object)
|
||||||
{
|
{
|
||||||
// merge selected objects to the 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)
|
auto get_object_idxs = [this](std::vector<int>& obj_idxs, wxDataViewItemArray& sels)
|
||||||
{
|
{
|
||||||
// check selections and split instances to the separated objects...
|
// check selections and split instances to the separated objects...
|
||||||
|
@ -2056,8 +2055,7 @@ void ObjectList::merge(bool to_multipart_object)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!instance_selection)
|
if (!instance_selection) {
|
||||||
{
|
|
||||||
for (wxDataViewItem item : sels) {
|
for (wxDataViewItem item : sels) {
|
||||||
assert(m_objects_model->GetItemType(item) & itObject);
|
assert(m_objects_model->GetItemType(item) & itObject);
|
||||||
obj_idxs.emplace_back(m_objects_model->GetIdByItem(item));
|
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::map<int, std::set<int>> sel_map;
|
||||||
std::set<int> empty_set;
|
std::set<int> empty_set;
|
||||||
for (wxDataViewItem item : sels) {
|
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 obj_idx = m_objects_model->GetIdByItem(item);
|
||||||
int inst_cnt = (*m_objects)[obj_idx]->instances.size();
|
int inst_cnt = (*m_objects)[obj_idx]->instances.size();
|
||||||
if (inst_cnt == 1)
|
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
|
// all objects, created from the instances will be added to the end of list
|
||||||
int new_objects_cnt = 0; // count of this new objects
|
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;
|
int obj_idx = map_item.first;
|
||||||
// object with just 1 instance
|
// object with just 1 instance
|
||||||
if (map_item.second.empty()) {
|
if (map_item.second.empty()) {
|
||||||
|
@ -2148,37 +2144,36 @@ void ObjectList::merge(bool to_multipart_object)
|
||||||
new_object->name = _u8L("Merged");
|
new_object->name = _u8L("Merged");
|
||||||
ModelConfig &config = new_object->config;
|
ModelConfig &config = new_object->config;
|
||||||
|
|
||||||
for (int obj_idx : obj_idxs)
|
for (int obj_idx : obj_idxs) {
|
||||||
{
|
|
||||||
ModelObject* object = (*m_objects)[obj_idx];
|
ModelObject* object = (*m_objects)[obj_idx];
|
||||||
|
|
||||||
const Geometry::Transformation& transformation = object->instances[0]->get_transformation();
|
const Geometry::Transformation& transformation = object->instances[0]->get_transformation();
|
||||||
Vec3d scale = transformation.get_scaling_factor();
|
const Vec3d scale = transformation.get_scaling_factor();
|
||||||
Vec3d mirror = transformation.get_mirror();
|
const Vec3d mirror = transformation.get_mirror();
|
||||||
Vec3d rotation = transformation.get_rotation();
|
const Vec3d rotation = transformation.get_rotation();
|
||||||
|
|
||||||
if (object->id() == (*m_objects)[obj_idxs.front()]->id())
|
if (object->id() == (*m_objects)[obj_idxs.front()]->id())
|
||||||
new_object->add_instance();
|
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
|
// merge volumes
|
||||||
for (const ModelVolume* volume : object->volumes) {
|
for (const ModelVolume* volume : object->volumes) {
|
||||||
ModelVolume* new_volume = new_object->add_volume(*volume);
|
ModelVolume* new_volume = new_object->add_volume(*volume);
|
||||||
|
|
||||||
//set rotation
|
//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);
|
new_volume->set_rotation(vol_rot);
|
||||||
|
|
||||||
// set scale
|
// 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);
|
new_volume->set_scaling_factor(vol_sc_fact);
|
||||||
|
|
||||||
// set mirror
|
// 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);
|
new_volume->set_mirror(vol_mirror);
|
||||||
|
|
||||||
// set offset
|
// 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_volume->set_offset(vol_offset);
|
||||||
}
|
}
|
||||||
new_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
|
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)
|
for (const auto& range : object->layer_config_ranges)
|
||||||
new_object->layer_config_ranges.emplace(range);
|
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 selected objects
|
||||||
remove();
|
remove();
|
||||||
|
|
||||||
|
@ -2221,8 +2221,7 @@ void ObjectList::merge(bool to_multipart_object)
|
||||||
}
|
}
|
||||||
// merge all parts to the one single object
|
// merge all parts to the one single object
|
||||||
// all part's settings will be lost
|
// all part's settings will be lost
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
wxDataViewItem item = GetSelection();
|
wxDataViewItem item = GetSelection();
|
||||||
if (!item)
|
if (!item)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -866,6 +866,9 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
|
||||||
|
|
||||||
void ObjectManipulation::change_scale_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)
|
if (std::abs(m_cache.scale_rounded(axis) - value) < EPSILON)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -882,6 +885,9 @@ void ObjectManipulation::change_scale_value(int axis, double value)
|
||||||
|
|
||||||
void ObjectManipulation::change_size_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)
|
if (std::abs(m_cache.size_rounded(axis) - value) < EPSILON)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -947,10 +953,26 @@ void ObjectManipulation::on_change(const std::string& opt_key, int axis, double
|
||||||
change_position_value(axis, new_value);
|
change_position_value(axis, new_value);
|
||||||
else if (opt_key == "rotation")
|
else if (opt_key == "rotation")
|
||||||
change_rotation_value(axis, new_value);
|
change_rotation_value(axis, new_value);
|
||||||
else if (opt_key == "scale")
|
else if (opt_key == "scale") {
|
||||||
|
if (new_value > 0.0)
|
||||||
change_scale_value(axis, new_value);
|
change_scale_value(axis, new_value);
|
||||||
else if (opt_key == "size")
|
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);
|
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)
|
void ObjectManipulation::set_uniform_scaling(const bool new_value)
|
||||||
|
|
|
@ -122,7 +122,9 @@ public:
|
||||||
std::string err;
|
std::string err;
|
||||||
ConfigSubstitutions config_substitutions;
|
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)
|
SLAImportJob::SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
|
||||||
|
@ -176,14 +178,12 @@ void SLAImportJob::prepare()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
ImportDlg dlg{p->plater};
|
if (p->import_dlg.ShowModal() == wxID_OK) {
|
||||||
|
auto path = p->import_dlg.get_path();
|
||||||
if (dlg.ShowModal() == wxID_OK) {
|
|
||||||
auto path = dlg.get_path();
|
|
||||||
auto nm = wxFileName(path);
|
auto nm = wxFileName(path);
|
||||||
p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : nm.GetFullPath();
|
p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : nm.GetFullPath();
|
||||||
p->sel = dlg.get_selection();
|
p->sel = p->import_dlg.get_selection();
|
||||||
p->win = dlg.get_marchsq_windowsize();
|
p->win = p->import_dlg.get_marchsq_windowsize();
|
||||||
p->config_substitutions.clear();
|
p->config_substitutions.clear();
|
||||||
} else {
|
} else {
|
||||||
p->path = "";
|
p->path = "";
|
||||||
|
@ -236,7 +236,7 @@ void SLAImportJob::finalize()
|
||||||
|
|
||||||
if (!p->mesh.empty()) {
|
if (!p->mesh.empty()) {
|
||||||
bool is_centered = false;
|
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);
|
name, is_centered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ 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, wxWindowID button_id, wxBitmap bitmap)
|
||||||
: wxDialog(parent ? parent : dynamic_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
: 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))
|
, content_sizer(new wxBoxSizer(wxVERTICAL))
|
||||||
, btn_sizer(new wxBoxSizer(wxHORIZONTAL))
|
, 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->SetFont(wxGetApp().normal_font());
|
||||||
this->CenterOnParent();
|
this->CenterOnParent();
|
||||||
|
|
||||||
|
auto *main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
|
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
auto *rightsizer = new wxBoxSizer(wxVERTICAL);
|
auto *rightsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
@ -46,6 +47,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
|
||||||
rightsizer->AddSpacer(VERT_SPACING);
|
rightsizer->AddSpacer(VERT_SPACING);
|
||||||
|
|
||||||
rightsizer->Add(content_sizer, 1, wxEXPAND);
|
rightsizer->Add(content_sizer, 1, wxEXPAND);
|
||||||
|
btn_sizer->AddStretchSpacer();
|
||||||
|
|
||||||
if (button_id != wxID_NONE) {
|
if (button_id != wxID_NONE) {
|
||||||
auto *button = new wxButton(this, button_id);
|
auto *button = new wxButton(this, button_id);
|
||||||
|
@ -53,8 +55,6 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
|
||||||
btn_sizer->Add(button);
|
btn_sizer->Add(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
rightsizer->Add(btn_sizer, 0, wxALIGN_RIGHT);
|
|
||||||
|
|
||||||
if (! bitmap.IsOk()) {
|
if (! bitmap.IsOk()) {
|
||||||
bitmap = create_scaled_bitmap("PrusaSlicer_192px.png", this, 192);
|
bitmap = create_scaled_bitmap("PrusaSlicer_192px.png", this, 192);
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,11 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
|
||||||
topsizer->Add(logo, 0, wxALL, BORDER);
|
topsizer->Add(logo, 0, wxALL, BORDER);
|
||||||
topsizer->Add(rightsizer, 1, wxTOP | wxBOTTOM | wxRIGHT | wxEXPAND, 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);
|
||||||
|
|
||||||
|
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*/)
|
||||||
|
@ -72,7 +76,7 @@ void MsgDialog::add_btn(wxWindowID btn_id, bool set_focus /*= false*/)
|
||||||
wxButton* btn = new wxButton(this, btn_id);
|
wxButton* btn = new wxButton(this, btn_id);
|
||||||
if (set_focus)
|
if (set_focus)
|
||||||
btn->SetFocus();
|
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); });
|
btn->Bind(wxEVT_BUTTON, [this, btn_id](wxCommandEvent&) { this->EndModal(btn_id); });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -209,33 +213,38 @@ MessageDialog::MessageDialog(wxWindow* parent,
|
||||||
apply_style(style);
|
apply_style(style);
|
||||||
finalize();
|
finalize();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// MessageWithCheckDialog
|
// RichMessageDialog
|
||||||
|
|
||||||
MessageWithCheckDialog::MessageWithCheckDialog( wxWindow* parent,
|
RichMessageDialog::RichMessageDialog(wxWindow* parent,
|
||||||
const wxString& message,
|
const wxString& message,
|
||||||
const wxString& checkbox_label,
|
|
||||||
const wxString& caption/* = wxEmptyString*/,
|
const wxString& caption/* = wxEmptyString*/,
|
||||||
long style/* = wxOK*/)
|
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, wxID_NONE)
|
||||||
{
|
{
|
||||||
add_msg_content(this, content_sizer, message);
|
add_msg_content(this, content_sizer, message);
|
||||||
|
|
||||||
m_check = new wxCheckBox(this, wxID_ANY, checkbox_label);
|
m_checkBox = new wxCheckBox(this, wxID_ANY, m_checkBoxText);
|
||||||
content_sizer->Add(m_check, 0, wxTOP, 10);
|
m_checkBox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) { m_checkBoxValue = m_checkBox->GetValue(); });
|
||||||
|
|
||||||
|
btn_sizer->Insert(0, m_checkBox, wxALIGN_CENTER_VERTICAL);
|
||||||
|
|
||||||
apply_style(style);
|
apply_style(style);
|
||||||
finalize();
|
finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MessageWithCheckDialog::GetCheckVal()
|
int RichMessageDialog::ShowModal()
|
||||||
{
|
{
|
||||||
if (m_check)
|
if (m_checkBoxText.IsEmpty())
|
||||||
return m_check->GetValue();
|
m_checkBox->Hide();
|
||||||
return false;
|
else
|
||||||
|
m_checkBox->SetLabelText(m_checkBoxText);
|
||||||
|
Layout();
|
||||||
|
|
||||||
|
return wxDialog::ShowModal();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// InfoDialog
|
// InfoDialog
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
#include <wx/font.h>
|
#include <wx/font.h>
|
||||||
#include <wx/bitmap.h>
|
#include <wx/bitmap.h>
|
||||||
#include <wx/msgdlg.h>
|
#include <wx/msgdlg.h>
|
||||||
|
#include <wx/richmsgdlg.h>
|
||||||
|
#include <wx/textctrl.h>
|
||||||
|
#include <wx/statline.h>
|
||||||
|
|
||||||
class wxBoxSizer;
|
class wxBoxSizer;
|
||||||
class wxCheckBox;
|
class wxCheckBox;
|
||||||
|
@ -17,7 +20,6 @@ namespace Slic3r {
|
||||||
|
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
|
|
||||||
// A message / query dialog with a bitmap on the left and any content on the right
|
// A message / query dialog with a bitmap on the left and any content on the right
|
||||||
// with buttons underneath.
|
// with buttons underneath.
|
||||||
struct MsgDialog : wxDialog
|
struct MsgDialog : wxDialog
|
||||||
|
@ -87,6 +89,23 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#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
|
// Generic message dialog, used intead of wxMessageDialog
|
||||||
class MessageDialog : public MsgDialog
|
class MessageDialog : public MsgDialog
|
||||||
{
|
{
|
||||||
|
@ -101,7 +120,158 @@ public:
|
||||||
MessageDialog &operator=(const MessageDialog&) = delete;
|
MessageDialog &operator=(const MessageDialog&) = delete;
|
||||||
virtual ~MessageDialog() = default;
|
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
|
#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
|
// just a wrapper to wxMessageBox to use the same code on all platforms
|
||||||
class MessageDialog : public wxMessageDialog
|
class MessageDialog : public wxMessageDialog
|
||||||
{
|
{
|
||||||
|
@ -113,25 +283,19 @@ public:
|
||||||
: wxMessageDialog(parent, message, caption, style) {}
|
: wxMessageDialog(parent, message, caption, style) {}
|
||||||
~MessageDialog() {}
|
~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:
|
public:
|
||||||
MessageWithCheckDialog(wxWindow* parent,
|
RichMessageDialog(wxWindow* parent,
|
||||||
const wxString& message,
|
const wxString& message,
|
||||||
const wxString& checkbox_label,
|
|
||||||
const wxString& caption = wxEmptyString,
|
const wxString& caption = wxEmptyString,
|
||||||
long style = wxOK);
|
long style = wxOK)
|
||||||
MessageWithCheckDialog(MessageWithCheckDialog&&) = delete;
|
: wxRichMessageDialog(parent, message, caption, style) {}
|
||||||
MessageWithCheckDialog(const MessageWithCheckDialog&) = delete;
|
~RichMessageDialog() {}
|
||||||
MessageWithCheckDialog& operator=(MessageWithCheckDialog&&) = delete;
|
|
||||||
MessageWithCheckDialog& operator=(const MessageWithCheckDialog&) = delete;
|
|
||||||
virtual ~MessageWithCheckDialog() = default;
|
|
||||||
|
|
||||||
bool GetCheckVal();
|
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
// Generic info dialog, used for displaying exceptions
|
// Generic info dialog, used for displaying exceptions
|
||||||
class InfoDialog : public MsgDialog
|
class InfoDialog : public MsgDialog
|
||||||
|
|
|
@ -1920,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",
|
"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",
|
"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",
|
"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.
|
// 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",
|
"layer_height", "first_layer_height", "min_layer_height", "max_layer_height",
|
||||||
"brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers",
|
"brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers",
|
||||||
|
@ -2482,15 +2482,15 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||||
model.convert_from_meters(true);
|
model.convert_from_meters(true);
|
||||||
};
|
};
|
||||||
if (answer_convert_from_meters == wxOK_DEFAULT) {
|
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 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 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 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",
|
"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_WARNING | wxYES | wxNO);
|
||||||
|
dlg.ShowCheckBox(_L("Apply to all the remaining small objects being loaded."));
|
||||||
int answer = dlg.ShowModal();
|
int answer = dlg.ShowModal();
|
||||||
if (dlg.GetCheckVal())
|
if (dlg.IsCheckBoxChecked())
|
||||||
answer_convert_from_meters = answer;
|
answer_convert_from_meters = answer;
|
||||||
else
|
else
|
||||||
convert_model_if(model, answer == wxID_YES);
|
convert_model_if(model, answer == wxID_YES);
|
||||||
|
@ -2504,15 +2504,15 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||||
convert_from_imperial_units(model, true);
|
convert_from_imperial_units(model, true);
|
||||||
};
|
};
|
||||||
if (answer_convert_from_imperial_units == wxOK_DEFAULT) {
|
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 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 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 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",
|
"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_WARNING | wxYES | wxNO);
|
||||||
|
dlg.ShowCheckBox(_L("Apply to all the remaining small objects being loaded."));
|
||||||
int answer = dlg.ShowModal();
|
int answer = dlg.ShowModal();
|
||||||
if (dlg.GetCheckVal())
|
if (dlg.IsCheckBoxChecked())
|
||||||
answer_convert_from_imperial_units = answer;
|
answer_convert_from_imperial_units = answer;
|
||||||
else
|
else
|
||||||
convert_model_if(model, answer == wxID_YES);
|
convert_model_if(model, answer == wxID_YES);
|
||||||
|
@ -6222,6 +6222,15 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opt_key == "material_colour") {
|
||||||
|
update_scheduled = true; // update should be scheduled (for update 3DScene)
|
||||||
|
|
||||||
|
// update material color in full config
|
||||||
|
std::vector<std::string> material_colors = { config.opt_string("material_colour", (unsigned)0) };
|
||||||
|
p->config->option<ConfigOptionStrings>("material_colour")->values = material_colors;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
p->config->set_key_value(opt_key, config.option(opt_key)->clone());
|
p->config->set_key_value(opt_key, config.option(opt_key)->clone());
|
||||||
if (opt_key == "printer_technology") {
|
if (opt_key == "printer_technology") {
|
||||||
this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key));
|
this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key));
|
||||||
|
|
|
@ -101,6 +101,8 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
|
||||||
EndDialog(wxID_OK);
|
EndDialog(wxID_OK);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
wxGetApp().UpdateDlgDarkUI(this);
|
||||||
|
|
||||||
Fit();
|
Fit();
|
||||||
CenterOnParent();
|
CenterOnParent();
|
||||||
|
|
||||||
|
@ -331,6 +333,14 @@ void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||||
save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS);
|
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)
|
PrintHostQueueDialog::JobState PrintHostQueueDialog::get_state(int idx)
|
||||||
{
|
{
|
||||||
wxCHECK_MSG(idx >= 0 && idx < job_list->GetItemCount(), ST_ERROR, "Out of bounds access to job list");
|
wxCHECK_MSG(idx >= 0 && idx < job_list->GetItemCount(), ST_ERROR, "Out of bounds access to job list");
|
||||||
|
|
|
@ -72,6 +72,7 @@ public:
|
||||||
}
|
}
|
||||||
protected:
|
protected:
|
||||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||||
|
void on_sys_color_changed() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum Column {
|
enum Column {
|
||||||
|
|
|
@ -4157,6 +4157,7 @@ void TabSLAMaterial::build()
|
||||||
auto page = add_options_page(L("Material"), "resin");
|
auto page = add_options_page(L("Material"), "resin");
|
||||||
|
|
||||||
auto optgroup = page->new_optgroup(L("Material"));
|
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_cost");
|
||||||
optgroup->append_single_option_line("bottle_volume");
|
optgroup->append_single_option_line("bottle_volume");
|
||||||
optgroup->append_single_option_line("bottle_weight");
|
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)
|
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;
|
DynamicPrintConfig new_conf = *m_config;
|
||||||
|
|
||||||
if (opt_key == "bottle_volume") {
|
if (opt_key == "bottle_volume") {
|
||||||
|
|
Loading…
Reference in a new issue