Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_labels

This commit is contained in:
Enrico Turri 2020-02-03 08:23:55 +01:00
commit 251a7d1329
40 changed files with 3008 additions and 2809 deletions

View file

@ -0,0 +1,7 @@
min_slic3r_version = 2.2.0-alpha3
0.0.2-alpha0 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer.
# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer,
# so they will see the print bed.
max_slic3r_version = 2.2.0-alpha2
min_slic3r_version = 2.2.0-alpha0
0.0.1 Initial version

View file

@ -5,7 +5,7 @@
name = Creality name = Creality
# Configuration version of this file. Config file will only be installed, if the config_version differs. # Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded. # This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 0.0.1 config_version = 0.0.2-alpha0
# Where to get the updates from? # Where to get the updates from?
config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Creality/ config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Creality/
# changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1% # changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1%

View file

@ -958,7 +958,7 @@ namespace DoExport {
skirts.emplace_back(std::move(s)); skirts.emplace_back(std::move(s));
} }
ooze_prevention.enable = true; ooze_prevention.enable = true;
ooze_prevention.standby_points = offset(Slic3r::Geometry::convex_hull(skirts), scale_(3.f)).front().equally_spaced_points(scale_(10.)); ooze_prevention.standby_points = offset(Slic3r::Geometry::convex_hull(skirts), float(scale_(3.))).front().equally_spaced_points(float(scale_(10.)));
#if 0 #if 0
require "Slic3r/SVG.pm"; require "Slic3r/SVG.pm";
Slic3r::SVG::output( Slic3r::SVG::output(
@ -1091,7 +1091,7 @@ namespace DoExport {
static inline std::vector<const PrintInstance*> sort_object_instances_by_max_z(const Print &print) static inline std::vector<const PrintInstance*> sort_object_instances_by_max_z(const Print &print)
{ {
std::vector<const PrintObject*> objects(print.objects().begin(), print.objects().end()); std::vector<const PrintObject*> objects(print.objects().begin(), print.objects().end());
std::sort(objects.begin(), objects.end(), [](const PrintObject *po1, const PrintObject *po2) { return po1->size(2) < po2->size(2); }); std::sort(objects.begin(), objects.end(), [](const PrintObject *po1, const PrintObject *po2) { return po1->size()(2) < po2->size()(2); });
std::vector<const PrintInstance*> instances; std::vector<const PrintInstance*> instances;
instances.reserve(objects.size()); instances.reserve(objects.size());
for (const PrintObject *object : objects) for (const PrintObject *object : objects)

View file

@ -907,10 +907,8 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const
const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true); const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true);
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
{
if (v->is_model_part()) if (v->is_model_part())
m_raw_bounding_box.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix())); m_raw_bounding_box.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix()));
}
} }
return m_raw_bounding_box; return m_raw_bounding_box;
} }
@ -1115,7 +1113,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_upper) { if (keep_upper) {
upper->set_model(nullptr); upper->set_model(nullptr);
upper->sla_support_points.clear(); upper->sla_support_points.clear();
lower->sla_drain_holes.clear(); upper->sla_drain_holes.clear();
upper->sla_points_status = sla::PointsStatus::NoPoints; upper->sla_points_status = sla::PointsStatus::NoPoints;
upper->clear_volumes(); upper->clear_volumes();
upper->input_file = ""; upper->input_file = "";

View file

@ -836,7 +836,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Update the ModelObject instance, possibly invalidate the linked PrintObjects. // Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved); assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
// Check whether a model part volume was added or removed, their transformations or order changed. // Check whether a model part volume was added or removed, their transformations or order changed.
// Only volume IDs, volume types and their order are checked, configuration and other parameters are NOT checked. // Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART); bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER); bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER); bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER);
@ -899,10 +899,14 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
model_object.instances.emplace_back(new ModelInstance(*model_instance)); model_object.instances.emplace_back(new ModelInstance(*model_instance));
model_object.instances.back()->set_model_object(&model_object); model_object.instances.back()->set_model_object(&model_object);
} }
} else { } else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
// Just synchronize the content of the instances. This avoids memory allocation and it does not invalidate ModelInstance pointers, [](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
// which may be accessed by G-code export in the meanwhile to deduce sequential print order. l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) {
auto new_instance = model_object_new.instances.begin(); // If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid.
// This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only.
model_object.invalidate_bounding_box();
// Synchronize the content of instances.
auto new_instance = model_object_new.instances.begin();
for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) { for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) {
(*old_instance)->set_transformation((*new_instance)->get_transformation()); (*old_instance)->set_transformation((*new_instance)->get_transformation());
(*old_instance)->print_volume_state = (*new_instance)->print_volume_state; (*old_instance)->print_volume_state = (*new_instance)->print_volume_state;
@ -1197,7 +1201,7 @@ std::string Print::validate() const
{ {
std::vector<coord_t> object_height; std::vector<coord_t> object_height;
for (const PrintObject *object : m_objects) for (const PrintObject *object : m_objects)
object_height.insert(object_height.end(), object->instances().size(), object->size(2)); object_height.insert(object_height.end(), object->instances().size(), object->size()(2));
std::sort(object_height.begin(), object_height.end()); std::sort(object_height.begin(), object_height.end());
// Ignore the tallest *copy* (this is why we repeat height for all of them): // Ignore the tallest *copy* (this is why we repeat height for all of them):
// it will be printed as last one so its height doesn't matter. // it will be printed as last one so its height doesn't matter.
@ -1429,7 +1433,7 @@ BoundingBox Print::bounding_box() const
for (const PrintObject *object : m_objects) for (const PrintObject *object : m_objects)
for (const PrintInstance &instance : object->instances()) { for (const PrintInstance &instance : object->instances()) {
bb.merge(instance.shift); bb.merge(instance.shift);
bb.merge(instance.shift + to_2d(object->size)); bb.merge(instance.shift + to_2d(object->size()));
} }
return bb; return bb;
} }

View file

@ -120,17 +120,17 @@ public:
// so that next call to make_perimeters() performs a union() before computing loops // so that next call to make_perimeters() performs a union() before computing loops
bool typed_slices; bool typed_slices;
Vec3crd size; // XYZ in scaled coordinates // XYZ in scaled coordinates
const Vec3crd& size() const { return m_size; }
const PrintObjectConfig& config() const { return m_config; } const PrintObjectConfig& config() const { return m_config; }
const LayerPtrs& layers() const { return m_layers; } const LayerPtrs& layers() const { return m_layers; }
const SupportLayerPtrs& support_layers() const { return m_support_layers; } const SupportLayerPtrs& support_layers() const { return m_support_layers; }
const Transform3d& trafo() const { return m_trafo; } const Transform3d& trafo() const { return m_trafo; }
const PrintInstances& instances() const { return m_instances; } const PrintInstances& instances() const { return m_instances; }
const Point instance_center(size_t idx) const { return m_instances[idx].shift + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); } const Point instance_center(size_t idx) const { return m_instances[idx].shift + m_copies_shift + Point(this->size().x() / 2, this->size().y() / 2); }
// since the object is aligned to origin, bounding box coincides with size // since the object is aligned to origin, bounding box coincides with size
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); } BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size())); }
// adds region_id, too, if necessary // adds region_id, too, if necessary
void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) { void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) {
@ -235,6 +235,8 @@ private:
void combine_infill(); void combine_infill();
void _generate_support_material(); void _generate_support_material();
// XYZ in scaled coordinates
Vec3crd m_size;
PrintObjectConfig m_config; PrintObjectConfig m_config;
// Translation in Z + Rotation + Scaling / Mirroring. // Translation in Z + Rotation + Scaling / Mirroring.
Transform3d m_trafo = Transform3d::Identity(); Transform3d m_trafo = Transform3d::Identity();

View file

@ -43,7 +43,7 @@ namespace Slic3r {
PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_instances) : PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_instances) :
PrintObjectBaseWithState(print, model_object), PrintObjectBaseWithState(print, model_object),
typed_slices(false), typed_slices(false),
size(Vec3crd::Zero()) m_size(Vec3crd::Zero())
{ {
// Compute the translation to be applied to our meshes so that we work with smaller coordinates // Compute the translation to be applied to our meshes so that we work with smaller coordinates
{ {
@ -56,7 +56,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
const BoundingBoxf3 modobj_bbox = model_object->raw_bounding_box(); const BoundingBoxf3 modobj_bbox = model_object->raw_bounding_box();
m_copies_shift = Point::new_scale(modobj_bbox.min(0), modobj_bbox.min(1)); m_copies_shift = Point::new_scale(modobj_bbox.min(0), modobj_bbox.min(1));
// Scale the object size and store it // Scale the object size and store it
this->size = (modobj_bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>(); this->m_size = (modobj_bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
} }
if (add_instances) { if (add_instances) {
@ -1450,7 +1450,7 @@ void PrintObject::update_slicing_parameters()
{ {
if (! m_slicing_params.valid) if (! m_slicing_params.valid)
m_slicing_params = SlicingParameters::create_from_config( m_slicing_params = SlicingParameters::create_from_config(
this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders()); this->print()->config(), m_config, unscale<double>(this->size()(2)), this->object_extruders());
} }
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z) SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)

View file

@ -678,7 +678,7 @@ void SLAPrint::process()
// We want to first process all objects... // We want to first process all objects...
std::vector<SLAPrintObjectStep> level1_obj_steps = { std::vector<SLAPrintObjectStep> level1_obj_steps = {
slaposHollowing, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad slaposHollowing, slaposDrillHoles, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad
}; };
// and then slice all supports to allow preview to be displayed ASAP // and then slice all supports to allow preview to be displayed ASAP
@ -984,10 +984,10 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step)
// propagate to dependent steps // propagate to dependent steps
if (step == slaposHollowing) { if (step == slaposHollowing) {
invalidated |= this->invalidate_all_steps(); invalidated |= this->invalidate_all_steps();
} else if (step == slaposObjectSlice) { } else if (step == slaposDrillHoles) {
invalidated |= this->invalidate_steps({ slaposDrillHolesIfHollowed, slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports }); invalidated |= this->invalidate_steps({ slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports });
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
} else if (step == slaposDrillHolesIfHollowed) { } else if (step == slaposObjectSlice) {
invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports }); invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports });
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
} else if (step == slaposSupportPoints) { } else if (step == slaposSupportPoints) {

View file

@ -20,8 +20,8 @@ enum SLAPrintStep : unsigned int {
enum SLAPrintObjectStep : unsigned int { enum SLAPrintObjectStep : unsigned int {
slaposHollowing, slaposHollowing,
slaposDrillHoles,
slaposObjectSlice, slaposObjectSlice,
slaposDrillHolesIfHollowed,
slaposSupportPoints, slaposSupportPoints,
slaposSupportTree, slaposSupportTree,
slaposPad, slaposPad,

View file

@ -26,9 +26,9 @@ namespace Slic3r {
namespace { namespace {
const std::array<unsigned, slaposCount> OBJ_STEP_LEVELS = { const std::array<unsigned, slaposCount> OBJ_STEP_LEVELS = {
5, // slaposHollowing, 10, // slaposHollowing,
20, // slaposObjectSlice, 10, // slaposDrillHolesIfHollowed
5, // slaposDrillHolesIfHollowed 10, // slaposObjectSlice,
20, // slaposSupportPoints, 20, // slaposSupportPoints,
10, // slaposSupportTree, 10, // slaposSupportTree,
10, // slaposPad, 10, // slaposPad,
@ -38,9 +38,9 @@ const std::array<unsigned, slaposCount> OBJ_STEP_LEVELS = {
std::string OBJ_STEP_LABELS(size_t idx) std::string OBJ_STEP_LABELS(size_t idx)
{ {
switch (idx) { switch (idx) {
case slaposHollowing: return L("Hollowing and drilling holes"); case slaposHollowing: return L("Hollowing model");
case slaposDrillHoles: return L("Drilling holes into hollowed model.");
case slaposObjectSlice: return L("Slicing model"); case slaposObjectSlice: return L("Slicing model");
case slaposDrillHolesIfHollowed: return L("Drilling holes into hollowed model.");
case slaposSupportPoints: return L("Generating support points"); case slaposSupportPoints: return L("Generating support points");
case slaposSupportTree: return L("Generating support tree"); case slaposSupportTree: return L("Generating support tree");
case slaposPad: return L("Generating pad"); case slaposPad: return L("Generating pad");
@ -80,57 +80,69 @@ SLAPrint::Steps::Steps(SLAPrint *print)
void SLAPrint::Steps::hollow_model(SLAPrintObject &po) void SLAPrint::Steps::hollow_model(SLAPrintObject &po)
{ {
po.m_hollowing_data.reset(); po.m_hollowing_data.reset();
if (! po.m_config.hollowing_enable.getBool()) if (! po.m_config.hollowing_enable.getBool()) {
BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!"; BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!";
else { return;
BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!";
double thickness = po.m_config.hollowing_min_thickness.getFloat();
double quality = po.m_config.hollowing_quality.getFloat();
double closing_d = po.m_config.hollowing_closing_distance.getFloat();
sla::HollowingConfig hlwcfg{thickness, quality, closing_d};
auto meshptr = generate_interior(po.transformed_mesh(), hlwcfg);
if (meshptr->empty())
BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!";
else {
po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
po.m_hollowing_data->interior = *meshptr;
auto &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes;
hollowed_mesh = po.transformed_mesh();
hollowed_mesh.merge(po.m_hollowing_data->interior);
hollowed_mesh.require_shared_vertices();
}
} }
BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!";
// Drill holes into the hollowed/original mesh. double thickness = po.m_config.hollowing_min_thickness.getFloat();
if (po.m_model_object->sla_drain_holes.empty()) double quality = po.m_config.hollowing_quality.getFloat();
BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes)."; double closing_d = po.m_config.hollowing_closing_distance.getFloat();
sla::HollowingConfig hlwcfg{thickness, quality, closing_d};
auto meshptr = generate_interior(po.transformed_mesh(), hlwcfg);
if (meshptr->empty())
BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!";
else { else {
BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes."; po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
sla::DrainHoles drainholes = po.transformed_drainhole_points(); po.m_hollowing_data->interior = *meshptr;
auto &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes;
TriangleMesh holes_mesh; hollowed_mesh = po.transformed_mesh();
hollowed_mesh.merge(po.m_hollowing_data->interior);
for (const sla::DrainHole &holept : drainholes)
holes_mesh.merge(sla::to_triangle_mesh(holept.to_mesh()));
holes_mesh.require_shared_vertices();
MeshBoolean::self_union(holes_mesh); //FIXME-fix and use the cgal version
// If there is no hollowed mesh yet, copy the original mesh.
if (! po.m_hollowing_data) {
po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
po.m_hollowing_data->hollow_mesh_with_holes = po.transformed_mesh();
}
TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes;
hollowed_mesh = po.get_mesh_to_print();
MeshBoolean::cgal::minus(hollowed_mesh, holes_mesh);
hollowed_mesh.require_shared_vertices(); hollowed_mesh.require_shared_vertices();
} }
} }
void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
{
// Drill holes into the hollowed/original mesh.
if (po.m_model_object->sla_drain_holes.empty()) {
BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes).";
return;
}
BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes.";
sla::DrainHoles drainholes = po.transformed_drainhole_points();
TriangleMesh holes_mesh;
for (const sla::DrainHole &holept : drainholes)
holes_mesh.merge(sla::to_triangle_mesh(holept.to_mesh()));
holes_mesh.require_shared_vertices();
MeshBoolean::cgal::self_union(holes_mesh); //FIXME-fix and use the cgal version
// If there is no hollowed mesh yet, copy the original mesh.
if (! po.m_hollowing_data) {
po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
po.m_hollowing_data->hollow_mesh_with_holes = po.transformed_mesh();
}
TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes;
try {
MeshBoolean::cgal::minus(hollowed_mesh, holes_mesh);
} catch (const std::runtime_error &ex) {
throw std::runtime_error(L(
"Drilling holes into the mesh failed. "
"This is usually caused by broken model. Try to fix it first."));
}
hollowed_mesh.require_shared_vertices();
}
// The slicing will be performed on an imaginary 1D grid which starts from // The slicing will be performed on an imaginary 1D grid which starts from
// the bottom of the bounding box created around the supported model. So // the bottom of the bounding box created around the supported model. So
// the first layer which is usually thicker will be part of the supports // the first layer which is usually thicker will be part of the supports
@ -850,8 +862,8 @@ void SLAPrint::Steps::execute(SLAPrintObjectStep step, SLAPrintObject &obj)
{ {
switch(step) { switch(step) {
case slaposHollowing: hollow_model(obj); break; case slaposHollowing: hollow_model(obj); break;
case slaposDrillHoles: drill_holes(obj); break;
case slaposObjectSlice: slice_model(obj); break; case slaposObjectSlice: slice_model(obj); break;
case slaposDrillHolesIfHollowed: break;
case slaposSupportPoints: support_points(obj); break; case slaposSupportPoints: support_points(obj); break;
case slaposSupportTree: support_tree(obj); break; case slaposSupportTree: support_tree(obj); break;
case slaposPad: generate_pad(obj); break; case slaposPad: generate_pad(obj); break;

View file

@ -44,6 +44,7 @@ public:
Steps(SLAPrint *print); Steps(SLAPrint *print);
void hollow_model(SLAPrintObject &po); void hollow_model(SLAPrintObject &po);
void drill_holes (SLAPrintObject &po);
void slice_model(SLAPrintObject& po); void slice_model(SLAPrintObject& po);
void support_points(SLAPrintObject& po); void support_points(SLAPrintObject& po);
void support_tree(SLAPrintObject& po); void support_tree(SLAPrintObject& po);

View file

@ -147,6 +147,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Mouse3DController.hpp GUI/Mouse3DController.hpp
GUI/DoubleSlider.cpp GUI/DoubleSlider.cpp
GUI/DoubleSlider.hpp GUI/DoubleSlider.hpp
GUI/ObjectDataViewModel.cpp
GUI/ObjectDataViewModel.hpp
Utils/Http.cpp Utils/Http.cpp
Utils/Http.hpp Utils/Http.hpp
Utils/FixModelByWin10.cpp Utils/FixModelByWin10.cpp

View file

@ -10,7 +10,7 @@
#include "libslic3r/SLAPrint.hpp" #include "libslic3r/SLAPrint.hpp"
#include "libslic3r/Slicing.hpp" #include "libslic3r/Slicing.hpp"
#include "libslic3r/GCode/Analyzer.hpp" #include "libslic3r/GCode/Analyzer.hpp"
#include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/BitmapCache.hpp"
#include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/STL.hpp"
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
@ -792,14 +792,14 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
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 (PresetBundle::parse_color(txt_color, rgb)) if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
{ {
colors[i].set(txt_color, rgb); colors[i].set(txt_color, rgb);
} }
else else
{ {
const std::string& txt_color = config->opt_string("filament_colour", i); const std::string& txt_color = config->opt_string("filament_colour", i);
if (PresetBundle::parse_color(txt_color, rgb)) if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
colors[i].set(txt_color, rgb); colors[i].set(txt_color, rgb);
} }
} }

View file

@ -1,6 +1,9 @@
#include "BitmapCache.hpp" #include "BitmapCache.hpp"
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include "../Utils/MacDarkMode.hpp"
#include "GUI.hpp"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#if ! defined(WIN32) && ! defined(__APPLE__) #if ! defined(WIN32) && ! defined(__APPLE__)
@ -20,6 +23,16 @@
namespace Slic3r { namespace GUI { namespace Slic3r { namespace GUI {
BitmapCache::BitmapCache()
{
#ifdef __APPLE__
// Note: win->GetContentScaleFactor() is not used anymore here because it tends to
// return bogus results quite often (such as 1.0 on Retina or even 0.0).
// We're using the max scaling factor across all screens because it's very likely to be good enough.
m_scale = mac_max_scaling_factor();
#endif
}
void BitmapCache::clear() void BitmapCache::clear()
{ {
for (std::pair<const std::string, wxBitmap*> &bitmap : m_map) for (std::pair<const std::string, wxBitmap*> &bitmap : m_map)
@ -49,7 +62,7 @@ static wxBitmap wxImage_to_wxBitmap_with_alpha(wxImage &&image, float scale = 1.
#endif #endif
} }
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height, float scale/* = 1.0f*/) wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height)
{ {
wxBitmap *bitmap = nullptr; wxBitmap *bitmap = nullptr;
auto it = m_map.find(bitmap_key); auto it = m_map.find(bitmap_key);
@ -61,7 +74,7 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_
// So, We need to let the Mac OS wxBitmap implementation // So, We need to let the Mac OS wxBitmap implementation
// know that the image may already be scaled appropriately for Retina, // know that the image may already be scaled appropriately for Retina,
// and thereby that it's not supposed to upscale it. // and thereby that it's not supposed to upscale it.
bitmap->CreateScaled(width, height, -1, scale); bitmap->CreateScaled(width, height, -1, m_scale);
#endif #endif
m_map[bitmap_key] = bitmap; m_map[bitmap_key] = bitmap;
} else { } else {
@ -103,13 +116,18 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp
return this->insert(bitmap_key, bmps, bmps + 3); return this->insert(bitmap_key, bmps, bmps + 3);
} }
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *begin, const wxBitmap *end, float scale/* = 1.0f*/) wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *begin, const wxBitmap *end)
{ {
size_t width = 0; size_t width = 0;
size_t height = 0; size_t height = 0;
for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
#ifdef __APPLE__
width += bmp->GetScaledWidth();
height = std::max<size_t>(height, bmp->GetScaledHeight());
#else
width += bmp->GetWidth(); width += bmp->GetWidth();
height = std::max<size_t>(height, bmp->GetHeight()); height = std::max<size_t>(height, bmp->GetHeight());
#endif
} }
#ifdef BROKEN_ALPHA #ifdef BROKEN_ALPHA
@ -166,13 +184,7 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *beg
#else #else
#ifdef __APPLE__ wxBitmap *bitmap = this->insert(bitmap_key, width, height);
// Note, for this moment width and height are scaled, so divide them by scale to avoid one more multiplication inside CreateScaled()
width *= 1.0 / scale;
height *= 1.0 / scale;
#endif
wxBitmap *bitmap = this->insert(bitmap_key, width, height, scale);
wxMemoryDC memDC; wxMemoryDC memDC;
memDC.SelectObject(*bitmap); memDC.SelectObject(*bitmap);
memDC.SetBackground(*wxTRANSPARENT_BRUSH); memDC.SetBackground(*wxTRANSPARENT_BRUSH);
@ -181,8 +193,12 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *beg
for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
if (bmp->GetWidth() > 0) if (bmp->GetWidth() > 0)
memDC.DrawBitmap(*bmp, x, 0, true); memDC.DrawBitmap(*bmp, x, 0, true);
#ifdef __APPLE__
// we should "move" with step equal to non-scaled width // we should "move" with step equal to non-scaled width
x += bmp->GetWidth()/scale; x += bmp->GetScaledWidth();
#else
x += bmp->GetWidth();
#endif
} }
memDC.SelectObject(wxNullBitmap); memDC.SelectObject(wxNullBitmap);
return bitmap; return bitmap;
@ -190,7 +206,7 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *beg
#endif #endif
} }
wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, float scale /* = 1.0f */, const bool grayscale/* = false*/) wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale/* = false*/)
{ {
wxImage image(width, height); wxImage image(width, height);
image.InitAlpha(); image.InitAlpha();
@ -207,7 +223,7 @@ wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned w
if (grayscale) if (grayscale)
image = image.ConvertToGreyscale(m_gs, m_gs, m_gs); image = image.ConvertToGreyscale(m_gs, m_gs, m_gs);
return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image), scale)); return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image), m_scale));
} }
wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width, unsigned height, wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width, unsigned height,
@ -242,12 +258,12 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width,
} }
wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height, wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height,
float scale /* = 1.0f */, const bool grayscale/* = false*/, const bool dark_mode/* = false*/) const bool grayscale/* = false*/, const bool dark_mode/* = false*/)
{ {
std::string bitmap_key = bitmap_name + ( target_height !=0 ? std::string bitmap_key = bitmap_name + ( target_height !=0 ?
"-h" + std::to_string(target_height) : "-h" + std::to_string(target_height) :
"-w" + std::to_string(target_width)) "-w" + std::to_string(target_width))
+ (scale != 1.0f ? "-s" + std::to_string(scale) : "") + (m_scale != 1.0f ? "-s" + std::to_string(m_scale) : "")
+ (grayscale ? "-gs" : ""); + (grayscale ? "-gs" : "");
/* For the Dark mode of any platform, we should draw icons in respect to OS background /* For the Dark mode of any platform, we should draw icons in respect to OS background
@ -287,7 +303,7 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_
if (image == nullptr) if (image == nullptr)
return nullptr; return nullptr;
target_height != 0 ? target_height *= scale : target_width *= scale; target_height != 0 ? target_height *= m_scale : target_width *= m_scale;
float svg_scale = target_height != 0 ? float svg_scale = target_height != 0 ?
(float)target_height / image->height : target_width != 0 ? (float)target_height / image->height : target_width != 0 ?
@ -312,11 +328,16 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_
::nsvgDeleteRasterizer(rast); ::nsvgDeleteRasterizer(rast);
::nsvgDelete(image); ::nsvgDelete(image);
return this->insert_raw_rgba(bitmap_key, width, height, data.data(), scale, grayscale); return this->insert_raw_rgba(bitmap_key, width, height, data.data(), grayscale);
} }
wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency) //we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap
wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling/* = false*/)
{ {
double scale = suppress_scaling ? 1.0f : m_scale;
width *= scale;
height *= scale;
wxImage image(width, height); wxImage image(width, height);
image.InitAlpha(); image.InitAlpha();
unsigned char* imgdata = image.GetData(); unsigned char* imgdata = image.GetData();
@ -327,7 +348,32 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi
*imgdata ++ = b; *imgdata ++ = b;
*imgalpha ++ = transparency; *imgalpha ++ = transparency;
} }
return wxImage_to_wxBitmap_with_alpha(std::move(image)); return wxImage_to_wxBitmap_with_alpha(std::move(image), scale);
}
static inline int hex_digit_to_int(const char c)
{
return
(c >= '0' && c <= '9') ? int(c - '0') :
(c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
(c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
}
bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out)
{
rgb_out[0] = rgb_out[1] = rgb_out[2] = 0;
if (scolor.size() != 7 || scolor.front() != '#')
return false;
const char* c = scolor.data() + 1;
for (size_t i = 0; i < 3; ++i) {
int digit1 = hex_digit_to_int(*c++);
int digit2 = hex_digit_to_int(*c++);
if (digit1 == -1 || digit2 == -1)
return false;
rgb_out[i] = (unsigned char)(digit1 * 16 + digit2);
}
return true;
} }
} // namespace GUI } // namespace GUI

View file

@ -6,43 +6,42 @@
#include <wx/wx.h> #include <wx/wx.h>
#endif #endif
#include "libslic3r/libslic3r.h"
#include "libslic3r/Config.hpp"
#include "GUI.hpp"
namespace Slic3r { namespace GUI { namespace Slic3r { namespace GUI {
class BitmapCache class BitmapCache
{ {
public: public:
BitmapCache() {} BitmapCache();
~BitmapCache() { clear(); } ~BitmapCache() { clear(); }
void clear(); void clear();
double scale() { return m_scale; }
wxBitmap* find(const std::string &name) { auto it = m_map.find(name); return (it == m_map.end()) ? nullptr : it->second; } wxBitmap* find(const std::string &name) { auto it = m_map.find(name); return (it == m_map.end()) ? nullptr : it->second; }
const wxBitmap* find(const std::string &name) const { return const_cast<BitmapCache*>(this)->find(name); } const wxBitmap* find(const std::string &name) const { return const_cast<BitmapCache*>(this)->find(name); }
wxBitmap* insert(const std::string &name, size_t width, size_t height, float scale = 1.0f); wxBitmap* insert(const std::string &name, size_t width, size_t height);
wxBitmap* insert(const std::string &name, const wxBitmap &bmp); wxBitmap* insert(const std::string &name, const wxBitmap &bmp);
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2); wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2);
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3); wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3);
wxBitmap* insert(const std::string &name, const std::vector<wxBitmap> &bmps, float scale = 1.0f) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size(), scale); } wxBitmap* insert(const std::string &name, const std::vector<wxBitmap> &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); }
wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end, float scale = 1.0f); wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end);
wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, float scale = 1.0f, const bool grayscale = false); wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale = false);
// Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero. // Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero.
wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false); wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false);
// Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width. // Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width.
wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, float scale = 1.0f, const bool grayscale = false, const bool dark_mode = false); wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false);
static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency); /*static */wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false);
static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); } /*static */wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3], bool suppress_scaling = false) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); }
static wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); } /*static */wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); }
static bool parse_color(const std::string& scolor, unsigned char* rgb_out);
private: private:
std::map<std::string, wxBitmap*> m_map; std::map<std::string, wxBitmap*> m_map;
double m_gs = 0.2; // value, used for image.ConvertToGreyscale(m_gs, m_gs, m_gs) double m_gs = 0.2; // value, used for image.ConvertToGreyscale(m_gs, m_gs, m_gs)
double m_scale = 1.0; // value, used for correct scaling of SVG icons on Retina display
}; };
} // GUI } // GUI

View file

@ -52,28 +52,26 @@ Control::Control( wxWindow *parent,
if (!is_osx) if (!is_osx)
SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX
const float scale_factor = get_svg_scale_factor(this);
m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "right_half_circle.png") : ScalableBitmap(this, "thumb_up")); m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "right_half_circle.png") : ScalableBitmap(this, "thumb_up"));
m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "thumb_down")); m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "thumb_down"));
m_thumb_size = m_bmp_thumb_lower.bmp().GetSize()*(1.0/scale_factor); m_thumb_size = m_bmp_thumb_lower.GetBmpSize();
m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add"); m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add");
m_bmp_add_tick_off = ScalableBitmap(this, "colorchange_add_f"); m_bmp_add_tick_off = ScalableBitmap(this, "colorchange_add_f");
m_bmp_del_tick_on = ScalableBitmap(this, "colorchange_del"); m_bmp_del_tick_on = ScalableBitmap(this, "colorchange_del");
m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_del_f"); m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_del_f");
m_tick_icon_dim = int((float)m_bmp_add_tick_on.bmp().GetSize().x / scale_factor); m_tick_icon_dim = m_bmp_add_tick_on.GetBmpWidth();
m_bmp_one_layer_lock_on = ScalableBitmap(this, "lock_closed"); m_bmp_one_layer_lock_on = ScalableBitmap(this, "lock_closed");
m_bmp_one_layer_lock_off = ScalableBitmap(this, "lock_closed_f"); m_bmp_one_layer_lock_off = ScalableBitmap(this, "lock_closed_f");
m_bmp_one_layer_unlock_on = ScalableBitmap(this, "lock_open"); m_bmp_one_layer_unlock_on = ScalableBitmap(this, "lock_open");
m_bmp_one_layer_unlock_off = ScalableBitmap(this, "lock_open_f"); m_bmp_one_layer_unlock_off = ScalableBitmap(this, "lock_open_f");
m_lock_icon_dim = int((float)m_bmp_one_layer_lock_on.bmp().GetSize().x / scale_factor); m_lock_icon_dim = m_bmp_one_layer_lock_on.GetBmpWidth();
m_bmp_revert = ScalableBitmap(this, "undo"); m_bmp_revert = ScalableBitmap(this, "undo");
m_revert_icon_dim = int((float)m_bmp_revert.bmp().GetSize().x / scale_factor); m_revert_icon_dim = m_bmp_revert.GetBmpWidth();
m_bmp_cog = ScalableBitmap(this, "cog"); m_bmp_cog = ScalableBitmap(this, "cog");
m_cog_icon_dim = int((float)m_bmp_cog.bmp().GetSize().x / scale_factor); m_cog_icon_dim = m_bmp_cog.GetBmpWidth();
m_selection = ssUndef; m_selection = ssUndef;
m_ticks.set_pause_print_msg(_utf8(L("Place bearings in slots and resume"))); m_ticks.set_pause_print_msg(_utf8(L("Place bearings in slots and resume")));
@ -554,9 +552,9 @@ void Control::draw_ticks(wxDC& dc)
// Draw icon for "Pause print" or "Custom Gcode" // Draw icon for "Pause print" or "Custom Gcode"
if (tick.gcode != ColorChangeCode && tick.gcode != ToolChangeCode) if (tick.gcode != ColorChangeCode && tick.gcode != ToolChangeCode)
icon = create_scaled_bitmap(this, tick.gcode == PausePrintCode ? "pause_print" : "edit_gcode"); icon = create_scaled_bitmap(tick.gcode == PausePrintCode ? "pause_print" : "edit_gcode");
else if (m_ticks.is_conflict_tick(tick, m_mode, m_only_extruder, m_values[tick.tick])) else if (m_ticks.is_conflict_tick(tick, m_mode, m_only_extruder, m_values[tick.tick]))
icon = create_scaled_bitmap(this, "error_tick"); icon = create_scaled_bitmap("error_tick");
if (!icon.IsNull()) if (!icon.IsNull())
{ {
@ -937,9 +935,12 @@ wxString Control::get_tooltip(IconFocus icon_focus)
if (conflict == ctModeConflict) if (conflict == ctModeConflict)
tooltip += _(L("G-code of this tick has a conflict with slider(print) mode.\n" tooltip += _(L("G-code of this tick has a conflict with slider(print) mode.\n"
"Any its editing will cause a changes of DoubleSlider data.")); "Any its editing will cause a changes of DoubleSlider data."));
else if (conflict == ctMeaningless) else if (conflict == ctMeaninglessColorChange)
tooltip += _(L("There is a color change for extruder that wouldn't be used till the end of printing.\n" tooltip += _(L("There is a color change for extruder that wouldn't be used till the end of printing.\n"
"This code wouldn't be processed during GCode generation.")); "This code wouldn't be processed during GCode generation."));
else if (conflict == ctMeaninglessToolChange)
tooltip += _(L("There is a extruder change to the same extruder.\n"
"This code wouldn't be processed during GCode generation."));
else if (conflict == ctRedundant) else if (conflict == ctRedundant)
tooltip += _(L("There is a color change for extruder that has not been used before.\n" tooltip += _(L("There is a color change for extruder that has not been used before.\n"
"Check your choice to avoid redundant color changes.")); "Check your choice to avoid redundant color changes."));
@ -1028,7 +1029,7 @@ void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current
_(L("Change extruder (N/A)")); _(L("Change extruder (N/A)"));
wxMenuItem* change_extruder_menu_item = menu->AppendSubMenu(change_extruder_menu, change_extruder_menu_name, _(L("Use another extruder"))); wxMenuItem* change_extruder_menu_item = menu->AppendSubMenu(change_extruder_menu, change_extruder_menu_name, _(L("Use another extruder")));
change_extruder_menu_item->SetBitmap(create_scaled_bitmap(this, active_extruders[1] > 0 ? "edit_uni" : "change_extruder")); change_extruder_menu_item->SetBitmap(create_scaled_bitmap(active_extruders[1] > 0 ? "edit_uni" : "change_extruder"));
GUI::wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this, change_extruder_menu_item](wxUpdateUIEvent& evt) { GUI::wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this, change_extruder_menu_item](wxUpdateUIEvent& evt) {
enable_menu_item(evt, [this]() {return m_mode == t_mode::MultiAsSingle; }, change_extruder_menu_item, this); }, enable_menu_item(evt, [this]() {return m_mode == t_mode::MultiAsSingle; }, change_extruder_menu_item, this); },
@ -1062,7 +1063,7 @@ void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_curren
from_u8((boost::format(_utf8(L("Switch code to Color change (%1%) for:"))) % ColorChangeCode).str()) : from_u8((boost::format(_utf8(L("Switch code to Color change (%1%) for:"))) % ColorChangeCode).str()) :
from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % ColorChangeCode).str()); from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % ColorChangeCode).str());
wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, ""); wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, "");
add_color_change_menu_item->SetBitmap(create_scaled_bitmap(this, "colorchange_add_m")); add_color_change_menu_item->SetBitmap(create_scaled_bitmap("colorchange_add_m"));
} }
} }
@ -1837,12 +1838,12 @@ ConflictType TickCodeInfo::is_conflict_tick(const TickCode& tick, t_mode out_mod
// check ColorChange tick // check ColorChange tick
if (tick.gcode == ColorChangeCode) if (tick.gcode == ColorChangeCode)
{ {
// We should mark a tick as a "Meaningless", // We should mark a tick as a "MeaninglessColorChange",
// if it has a ColorChange for unused extruder from current print to end of the print // if it has a ColorChange for unused extruder from current print to end of the print
std::set<int> used_extruders_for_tick = get_used_extruders_for_tick(tick.tick, only_extruder, print_z); std::set<int> used_extruders_for_tick = get_used_extruders_for_tick(tick.tick, only_extruder, print_z);
if (used_extruders_for_tick.find(tick.extruder) == used_extruders_for_tick.end()) if (used_extruders_for_tick.find(tick.extruder) == used_extruders_for_tick.end())
return ctMeaningless; return ctMeaninglessColorChange;
// We should mark a tick as a "Redundant", // We should mark a tick as a "Redundant",
// if it has a ColorChange for extruder that has not been used before // if it has a ColorChange for extruder that has not been used before
@ -1862,6 +1863,21 @@ ConflictType TickCodeInfo::is_conflict_tick(const TickCode& tick, t_mode out_mod
} }
} }
// check ToolChange tick
if (mode == t_mode::MultiAsSingle && tick.gcode == ToolChangeCode)
{
// We should mark a tick as a "MeaninglessToolChange",
// if it has a ToolChange to the same extruder
auto it = ticks.find(tick);
if (it == ticks.begin())
return tick.extruder == std::max<int>(only_extruder, 1) ? ctMeaninglessToolChange : ctNone;
--it;
if (it->gcode == ToolChangeCode && tick.extruder == it->extruder)
return ctMeaninglessToolChange;
}
return ctNone; return ctNone;
} }

View file

@ -43,7 +43,8 @@ enum ConflictType
{ {
ctNone, ctNone,
ctModeConflict, ctModeConflict,
ctMeaningless, ctMeaninglessColorChange,
ctMeaninglessToolChange,
ctRedundant ctRedundant
}; };

View file

@ -1399,6 +1399,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>); wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent); wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RELOAD_FROM_DISK, SimpleEvent);
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25; const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25;
@ -1855,7 +1856,11 @@ void GLCanvas3D::render()
const Size& cnv_size = get_canvas_size(); const Size& cnv_size = get_canvas_size();
#if ENABLE_6DOF_CAMERA #if ENABLE_6DOF_CAMERA
m_camera.apply_viewport(0, 0, (unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); // Probably due to different order of events on Linux/GTK2, when one switched from 3D scene
// to preview, this was called before canvas had its final size. It reported zero width
// and the viewport was set incorrectly, leading to tripping glAsserts further down
// the road (in apply_projection). That's why the minimum size is forced to 10.
m_camera.apply_viewport(0, 0, std::max(10u, (unsigned int)cnv_size.get_width()), std::max(10u, (unsigned int)cnv_size.get_height()));
#endif // ENABLE_6DOF_CAMERA #endif // ENABLE_6DOF_CAMERA
if (m_camera.requires_zoom_to_bed) if (m_camera.requires_zoom_to_bed)
@ -2769,6 +2774,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE)); post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE));
break; break;
case WXK_ESCAPE: { deselect_all(); break; } case WXK_ESCAPE: { deselect_all(); break; }
case WXK_F5: { post_event(SimpleEvent(EVT_GLCANVAS_RELOAD_FROM_DISK)); break; }
case '0': { select_view("iso"); break; } case '0': { select_view("iso"); break; }
case '1': { select_view("top"); break; } case '1': { select_view("top"); break; }
case '2': { select_view("bottom"); break; } case '2': { select_view("bottom"); break; }

View file

@ -108,6 +108,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>); wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent); wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RELOAD_FROM_DISK, SimpleEvent);
class GLCanvas3D class GLCanvas3D
{ {

View file

@ -90,20 +90,20 @@ ObjectList::ObjectList(wxWindow* parent) :
// see note in PresetBundle::load_compatible_bitmaps() // see note in PresetBundle::load_compatible_bitmaps()
// ptFFF // ptFFF
CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap(this, "layers"); CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers");
CATEGORY_ICON[L("Infill")] = create_scaled_bitmap(this, "infill"); CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill");
CATEGORY_ICON[L("Support material")] = create_scaled_bitmap(this, "support"); CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("support");
CATEGORY_ICON[L("Speed")] = create_scaled_bitmap(this, "time"); CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time");
CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap(this, "funnel"); CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel");
CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap(this, "funnel"); CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap("funnel");
CATEGORY_ICON[L("Wipe options")] = create_scaled_bitmap(this, "funnel"); CATEGORY_ICON[L("Wipe options")] = create_scaled_bitmap("funnel");
// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap(this, "skirt+brim"); // CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("skirt+brim");
// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap(this, "time"); // CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap("time");
CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap(this, "wrench"); CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench");
// ptSLA // ptSLA
CATEGORY_ICON[L("Supports")] = create_scaled_bitmap(this, "support"/*"sla_supports"*/); CATEGORY_ICON[L("Supports")] = create_scaled_bitmap("support"/*"sla_supports"*/);
CATEGORY_ICON[L("Pad")] = create_scaled_bitmap(this, "pad"); CATEGORY_ICON[L("Pad")] = create_scaled_bitmap("pad");
CATEGORY_ICON[L("Hollowing")] = create_scaled_bitmap(this, "hollowing"); CATEGORY_ICON[L("Hollowing")] = create_scaled_bitmap("hollowing");
} }
// create control // create control
@ -607,23 +607,20 @@ void ObjectList::msw_rescale_icons()
// Update CATEGORY_ICON according to new scale // Update CATEGORY_ICON according to new scale
{ {
// Note: `this` isn't passed to create_scaled_bitmap() here because of bugs in the widget,
// see note in PresetBundle::load_compatible_bitmaps()
// ptFFF // ptFFF
CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap(nullptr, "layers"); CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers");
CATEGORY_ICON[L("Infill")] = create_scaled_bitmap(nullptr, "infill"); CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill");
CATEGORY_ICON[L("Support material")] = create_scaled_bitmap(nullptr, "support"); CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("support");
CATEGORY_ICON[L("Speed")] = create_scaled_bitmap(nullptr, "time"); CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time");
CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap(nullptr, "funnel"); CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel");
CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap(nullptr, "funnel"); CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap("funnel");
CATEGORY_ICON[L("Wipe options")] = create_scaled_bitmap(nullptr, "funnel"); CATEGORY_ICON[L("Wipe options")] = create_scaled_bitmap("funnel");
// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap(nullptr, "skirt+brim"); // CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("skirt+brim");
// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap(nullptr, "time"); // CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap("time");
CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap(nullptr, "wrench"); CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench");
// ptSLA // ptSLA
CATEGORY_ICON[L("Supports")] = create_scaled_bitmap(nullptr, "support"/*"sla_supports"*/); CATEGORY_ICON[L("Supports")] = create_scaled_bitmap("support"/*"sla_supports"*/);
CATEGORY_ICON[L("Pad")] = create_scaled_bitmap(nullptr, "pad"); CATEGORY_ICON[L("Pad")] = create_scaled_bitmap("pad");
} }
} }
@ -1003,14 +1000,13 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event)
const bool mult_sel = multiple_selection(); const bool mult_sel = multiple_selection();
if ((mult_sel && !selected_instances_of_same_object()) || if ((mult_sel && !selected_instances_of_same_object()) ||
(!mult_sel && (GetSelection() != item)) || (!mult_sel && (GetSelection() != item)) ) {
m_objects_model->GetParent(item) == wxDataViewItem(nullptr) ) {
event.Veto(); event.Veto();
return; return;
} }
const ItemType& type = m_objects_model->GetItemType(item); const ItemType& type = m_objects_model->GetItemType(item);
if (!(type & (itVolume | itInstance))) { if (!(type & (itVolume | itObject | itInstance))) {
event.Veto(); event.Veto();
return; return;
} }
@ -1024,11 +1020,13 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event)
for (auto sel : sels ) for (auto sel : sels )
sub_obj_idxs.insert(m_objects_model->GetInstanceIdByItem(sel)); sub_obj_idxs.insert(m_objects_model->GetInstanceIdByItem(sel));
} }
else else if (type & itObject)
m_dragged_data.init(m_objects_model->GetIdByItem(item), type);
else
m_dragged_data.init(m_objects_model->GetObjectIdByItem(item), m_dragged_data.init(m_objects_model->GetObjectIdByItem(item),
type&itVolume ? m_objects_model->GetVolumeIdByItem(item) : type&itVolume ? m_objects_model->GetVolumeIdByItem(item) :
m_objects_model->GetInstanceIdByItem(item), m_objects_model->GetInstanceIdByItem(item),
type); type);
/* Under MSW or OSX, DnD moves an item to the place of another selected item /* Under MSW or OSX, DnD moves an item to the place of another selected item
* But under GTK, DnD moves an item between another two items. * But under GTK, DnD moves an item between another two items.
@ -1049,10 +1047,20 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event)
bool ObjectList::can_drop(const wxDataViewItem& item) const bool ObjectList::can_drop(const wxDataViewItem& item) const
{ {
return (m_dragged_data.type() == itInstance && !item.IsOk()) || // move instance(s) or object on "empty place" of ObjectList
(m_dragged_data.type() == itVolume && item.IsOk() && if ( (m_dragged_data.type() & (itInstance | itObject)) && !item.IsOk() )
m_objects_model->GetItemType(item) == itVolume && return true;
m_dragged_data.obj_idx() == m_objects_model->GetObjectIdByItem(item));
// type of moved item should be the same as a "destination" item
if (!item.IsOk() || !(m_dragged_data.type() & (itVolume|itObject)) ||
m_objects_model->GetItemType(item) != m_dragged_data.type() )
return false;
// move volumes inside one object only
if (m_dragged_data.type() & itVolume)
return m_dragged_data.obj_idx() == m_objects_model->GetObjectIdByItem(item);
return true;
} }
void ObjectList::OnDropPossible(wxDataViewEvent &event) void ObjectList::OnDropPossible(wxDataViewEvent &event)
@ -1082,9 +1090,6 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
return; return;
} }
const int from_volume_id = m_dragged_data.sub_obj_idx();
int to_volume_id = m_objects_model->GetVolumeIdByItem(item);
// It looks like a fixed in current version of the wxWidgets // It looks like a fixed in current version of the wxWidgets
// #ifdef __WXGTK__ // #ifdef __WXGTK__
// /* Under GTK, DnD moves an item between another two items. // /* Under GTK, DnD moves an item between another two items.
@ -1096,14 +1101,33 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
take_snapshot(_((m_dragged_data.type() == itVolume) ? L("Volumes in Object reordered") : L("Object reordered"))); take_snapshot(_((m_dragged_data.type() == itVolume) ? L("Volumes in Object reordered") : L("Object reordered")));
auto& volumes = (*m_objects)[m_dragged_data.obj_idx()]->volumes; if (m_dragged_data.type() & itVolume)
auto delta = to_volume_id < from_volume_id ? -1 : 1; {
int cnt = 0; int from_volume_id = m_dragged_data.sub_obj_idx();
for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++) int to_volume_id = m_objects_model->GetVolumeIdByItem(item);
std::swap(volumes[id], volumes[id + delta]); int delta = to_volume_id < from_volume_id ? -1 : 1;
select_item(m_objects_model->ReorganizeChildren(from_volume_id, to_volume_id, auto& volumes = (*m_objects)[m_dragged_data.obj_idx()]->volumes;
m_objects_model->GetParent(item)));
int cnt = 0;
for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++)
std::swap(volumes[id], volumes[id + delta]);
select_item(m_objects_model->ReorganizeChildren(from_volume_id, to_volume_id, m_objects_model->GetParent(item)));
}
else if (m_dragged_data.type() & itObject)
{
int from_obj_id = m_dragged_data.obj_idx();
int to_obj_id = item.IsOk() ? m_objects_model->GetIdByItem(item) : ((int)m_objects->size()-1);
int delta = to_obj_id < from_obj_id ? -1 : 1;
int cnt = 0;
for (int id = from_obj_id; cnt < abs(from_obj_id - to_obj_id); id += delta, cnt++)
std::swap((*m_objects)[id], (*m_objects)[id + delta]);
select_item(m_objects_model->ReorganizeObjects(from_obj_id, to_obj_id));
}
changed_object(m_dragged_data.obj_idx()); changed_object(m_dragged_data.obj_idx());

View file

@ -10,6 +10,7 @@
#include "Event.hpp" #include "Event.hpp"
#include "wxExtensions.hpp" #include "wxExtensions.hpp"
#include "ObjectDataViewModel.hpp"
class wxBoxSizer; class wxBoxSizer;
class wxBitmapComboBox; class wxBitmapComboBox;

View file

@ -68,7 +68,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
/* Load default preset bitmaps before a tabpanel initialization, /* Load default preset bitmaps before a tabpanel initialization,
* but after filling of an em_unit value * but after filling of an em_unit value
*/ */
wxGetApp().preset_bundle->load_default_preset_bitmaps(this); wxGetApp().preset_bundle->load_default_preset_bitmaps();
// initialize tabpanel and menubar // initialize tabpanel and menubar
init_tabpanel(); init_tabpanel();
@ -345,7 +345,7 @@ void MainFrame::on_dpi_changed(const wxRect &suggested_rect)
/* Load default preset bitmaps before a tabpanel initialization, /* Load default preset bitmaps before a tabpanel initialization,
* but after filling of an em_unit value * but after filling of an em_unit value
*/ */
wxGetApp().preset_bundle->load_default_preset_bitmaps(this); wxGetApp().preset_bundle->load_default_preset_bitmaps();
// update Plater // update Plater
wxGetApp().plater()->msw_rescale(); wxGetApp().plater()->msw_rescale();
@ -578,6 +578,11 @@ void MainFrame::init_menubar()
append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "V", append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "V",
_(L("Paste clipboard")), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); }, _(L("Paste clipboard")), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); },
"paste_menu", nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this); "paste_menu", nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this);
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("Re&load from disk")) + sep + "F5",
_(L("Reload the plater from disk")), [this](wxCommandEvent&) { m_plater->reload_all_from_disk(); },
"", nullptr, [this]() {return !m_plater->model().objects.empty(); }, this);
} }
// Window menu // Window menu
@ -728,7 +733,7 @@ void MainFrame::update_menubar()
m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _(L("S&end G-code")) : _(L("S&end to print"))) + dots + "\tCtrl+Shift+G"); m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _(L("S&end G-code")) : _(L("S&end to print"))) + dots + "\tCtrl+Shift+G");
m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _(L("&Filament Settings Tab")) : _(L("Mate&rial Settings Tab"))) + "\tCtrl+3"); m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _(L("&Filament Settings Tab")) : _(L("Mate&rial Settings Tab"))) + "\tCtrl+3");
m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(this, is_fff ? "spool": "resin")); m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(is_fff ? "spool": "resin"));
} }
// To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG". // To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".

View file

@ -53,7 +53,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
rightsizer->Add(btn_sizer, 0, wxALIGN_RIGHT); rightsizer->Add(btn_sizer, 0, wxALIGN_RIGHT);
if (! bitmap.IsOk()) { if (! bitmap.IsOk()) {
bitmap = create_scaled_bitmap(this, "PrusaSlicer_192px.png", 192); bitmap = create_scaled_bitmap("PrusaSlicer_192px.png", this, 192);
} }
logo = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap); logo = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap);
@ -99,7 +99,7 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg)
btn_ok->SetFocus(); btn_ok->SetFocus();
btn_sizer->Add(btn_ok, 0, wxRIGHT, HORIZ_SPACING); btn_sizer->Add(btn_ok, 0, wxRIGHT, HORIZ_SPACING);
logo->SetBitmap(create_scaled_bitmap(this, "PrusaSlicer_192px_grayscale.png", 192)); logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192));
SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT*wxGetApp().em_unit())); SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT*wxGetApp().em_unit()));
Fit(); Fit();

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,516 @@
#ifndef slic3r_GUI_ObjectDataViewModel_hpp_
#define slic3r_GUI_ObjectDataViewModel_hpp_
#include <wx/dataview.h>
#include <vector>
namespace Slic3r {
enum class ModelVolumeType : int;
namespace GUI {
typedef double coordf_t;
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
// ----------------------------------------------------------------------------
// DataViewBitmapText: helper class used by BitmapTextRenderer
// ----------------------------------------------------------------------------
class DataViewBitmapText : public wxObject
{
public:
DataViewBitmapText( const wxString &text = wxEmptyString,
const wxBitmap& bmp = wxNullBitmap) :
m_text(text),
m_bmp(bmp)
{ }
DataViewBitmapText(const DataViewBitmapText &other)
: wxObject(),
m_text(other.m_text),
m_bmp(other.m_bmp)
{ }
void SetText(const wxString &text) { m_text = text; }
wxString GetText() const { return m_text; }
void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; }
const wxBitmap &GetBitmap() const { return m_bmp; }
bool IsSameAs(const DataViewBitmapText& other) const {
return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp);
}
bool operator==(const DataViewBitmapText& other) const {
return IsSameAs(other);
}
bool operator!=(const DataViewBitmapText& other) const {
return !IsSameAs(other);
}
private:
wxString m_text;
wxBitmap m_bmp;
wxDECLARE_DYNAMIC_CLASS(DataViewBitmapText);
};
DECLARE_VARIANT_OBJECT(DataViewBitmapText)
// ----------------------------------------------------------------------------
// BitmapTextRenderer
// ----------------------------------------------------------------------------
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
class BitmapTextRenderer : public wxDataViewRenderer
#else
class BitmapTextRenderer : public wxDataViewCustomRenderer
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
{
public:
BitmapTextRenderer(wxWindow* parent,
wxDataViewCellMode mode =
#ifdef __WXOSX__
wxDATAVIEW_CELL_INERT
#else
wxDATAVIEW_CELL_EDITABLE
#endif
, int align = wxDVR_DEFAULT_ALIGNMENT
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
);
#else
) :
wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align),
m_parent(parent)
{}
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
bool SetValue(const wxVariant& value);
bool GetValue(wxVariant& value) const;
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY
virtual wxString GetAccessibleDescription() const override;
#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
virtual bool Render(wxRect cell, wxDC* dc, int state) override;
virtual wxSize GetSize() const override;
bool HasEditorCtrl() const override
{
#ifdef __WXOSX__
return false;
#else
return true;
#endif
}
wxWindow* CreateEditorCtrl(wxWindow* parent,
wxRect labelRect,
const wxVariant& value) override;
bool GetValueFromEditorCtrl(wxWindow* ctrl,
wxVariant& value) override;
bool WasCanceled() const { return m_was_unusable_symbol; }
private:
DataViewBitmapText m_value;
bool m_was_unusable_symbol{ false };
wxWindow* m_parent{ nullptr };
};
// ----------------------------------------------------------------------------
// BitmapChoiceRenderer
// ----------------------------------------------------------------------------
class BitmapChoiceRenderer : public wxDataViewCustomRenderer
{
public:
BitmapChoiceRenderer(wxDataViewCellMode mode =
#ifdef __WXOSX__
wxDATAVIEW_CELL_INERT
#else
wxDATAVIEW_CELL_EDITABLE
#endif
, int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL
) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {}
bool SetValue(const wxVariant& value);
bool GetValue(wxVariant& value) const;
virtual bool Render(wxRect cell, wxDC* dc, int state) override;
virtual wxSize GetSize() const override;
bool HasEditorCtrl() const override { return true; }
wxWindow* CreateEditorCtrl(wxWindow* parent,
wxRect labelRect,
const wxVariant& value) override;
bool GetValueFromEditorCtrl(wxWindow* ctrl,
wxVariant& value) override;
private:
DataViewBitmapText m_value;
};
// ----------------------------------------------------------------------------
// ObjectDataViewModelNode: a node inside ObjectDataViewModel
// ----------------------------------------------------------------------------
enum ItemType {
itUndef = 0,
itObject = 1,
itVolume = 2,
itInstanceRoot = 4,
itInstance = 8,
itSettings = 16,
itLayerRoot = 32,
itLayer = 64,
};
enum ColumnNumber
{
colName = 0, // item name
colPrint , // printable property
colExtruder , // extruder selection
colEditing , // item editing
};
enum PrintIndicator
{
piUndef = 0, // no print indicator
piPrintable , // printable
piUnprintable , // unprintable
};
class ObjectDataViewModelNode;
WX_DEFINE_ARRAY_PTR(ObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray);
class ObjectDataViewModelNode
{
ObjectDataViewModelNode* m_parent;
MyObjectTreeModelNodePtrArray m_children;
wxBitmap m_empty_bmp;
size_t m_volumes_cnt = 0;
std::vector< std::string > m_opt_categories;
t_layer_height_range m_layer_range = { 0.0f, 0.0f };
wxString m_name;
wxBitmap& m_bmp = m_empty_bmp;
ItemType m_type;
int m_idx = -1;
bool m_container = false;
wxString m_extruder = "default";
wxBitmap m_extruder_bmp;
wxBitmap m_action_icon;
PrintIndicator m_printable {piUndef};
wxBitmap m_printable_icon;
std::string m_action_icon_name = "";
ModelVolumeType m_volume_type;
public:
ObjectDataViewModelNode(const wxString& name,
const wxString& extruder):
m_parent(NULL),
m_name(name),
m_type(itObject),
m_extruder(extruder)
{
set_action_and_extruder_icons();
init_container();
}
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const wxString& sub_obj_name,
const wxBitmap& bmp,
const wxString& extruder,
const int idx = -1 ) :
m_parent (parent),
m_name (sub_obj_name),
m_type (itVolume),
m_idx (idx),
m_extruder (extruder)
{
m_bmp = bmp;
set_action_and_extruder_icons();
init_container();
}
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const t_layer_height_range& layer_range,
const int idx = -1,
const wxString& extruder = wxEmptyString );
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type);
~ObjectDataViewModelNode()
{
// free all our children nodes
size_t count = m_children.GetCount();
for (size_t i = 0; i < count; i++)
{
ObjectDataViewModelNode *child = m_children[i];
delete child;
}
#ifndef NDEBUG
// Indicate that the object was deleted.
m_idx = -2;
#endif /* NDEBUG */
}
void init_container();
bool IsContainer() const
{
return m_container;
}
ObjectDataViewModelNode* GetParent()
{
assert(m_parent == nullptr || m_parent->valid());
return m_parent;
}
MyObjectTreeModelNodePtrArray& GetChildren()
{
return m_children;
}
ObjectDataViewModelNode* GetNthChild(unsigned int n)
{
return m_children.Item(n);
}
void Insert(ObjectDataViewModelNode* child, unsigned int n)
{
if (!m_container)
m_container = true;
m_children.Insert(child, n);
}
void Append(ObjectDataViewModelNode* child)
{
if (!m_container)
m_container = true;
m_children.Add(child);
}
void RemoveAllChildren()
{
if (GetChildCount() == 0)
return;
for (int id = int(GetChildCount()) - 1; id >= 0; --id)
{
if (m_children.Item(id)->GetChildCount() > 0)
m_children[id]->RemoveAllChildren();
auto node = m_children[id];
m_children.RemoveAt(id);
delete node;
}
}
size_t GetChildCount() const
{
return m_children.GetCount();
}
bool SetValue(const wxVariant &variant, unsigned int col);
void SetBitmap(const wxBitmap &icon) { m_bmp = icon; }
const wxBitmap& GetBitmap() const { return m_bmp; }
const wxString& GetName() const { return m_name; }
ItemType GetType() const { return m_type; }
void SetIdx(const int& idx);
int GetIdx() const { return m_idx; }
t_layer_height_range GetLayerRange() const { return m_layer_range; }
PrintIndicator IsPrintable() const { return m_printable; }
// use this function only for childrens
void AssignAllVal(ObjectDataViewModelNode& from_node)
{
// ! Don't overwrite other values because of equality of this values for all children --
m_name = from_node.m_name;
m_bmp = from_node.m_bmp;
m_idx = from_node.m_idx;
m_extruder = from_node.m_extruder;
m_type = from_node.m_type;
}
bool SwapChildrens(int frst_id, int scnd_id) {
if (GetChildCount() < 2 ||
frst_id < 0 || (size_t)frst_id >= GetChildCount() ||
scnd_id < 0 || (size_t)scnd_id >= GetChildCount())
return false;
ObjectDataViewModelNode new_scnd = *GetNthChild(frst_id);
ObjectDataViewModelNode new_frst = *GetNthChild(scnd_id);
new_scnd.m_idx = m_children.Item(scnd_id)->m_idx;
new_frst.m_idx = m_children.Item(frst_id)->m_idx;
m_children.Item(frst_id)->AssignAllVal(new_frst);
m_children.Item(scnd_id)->AssignAllVal(new_scnd);
return true;
}
// Set action icons for node
void set_action_and_extruder_icons();
// Set printable icon for node
void set_printable_icon(PrintIndicator printable);
void update_settings_digest_bitmaps();
bool update_settings_digest(const std::vector<std::string>& categories);
int volume_type() const { return int(m_volume_type); }
void msw_rescale();
#ifndef NDEBUG
bool valid();
#endif /* NDEBUG */
bool invalid() const { return m_idx < -1; }
private:
friend class ObjectDataViewModel;
};
// ----------------------------------------------------------------------------
// ObjectDataViewModel
// ----------------------------------------------------------------------------
// custom message the model sends to associated control to notify a last volume deleted from the object:
wxDECLARE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent);
class ObjectDataViewModel :public wxDataViewModel
{
std::vector<ObjectDataViewModelNode*> m_objects;
std::vector<wxBitmap*> m_volume_bmps;
wxBitmap* m_warning_bmp { nullptr };
wxDataViewCtrl* m_ctrl { nullptr };
public:
ObjectDataViewModel();
~ObjectDataViewModel();
wxDataViewItem Add( const wxString &name,
const int extruder,
const bool has_errors = false);
wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item,
const wxString &name,
const Slic3r::ModelVolumeType volume_type,
const bool has_errors = false,
const int extruder = 0,
const bool create_frst_child = true);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, const std::vector<bool>& print_indicator);
wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item);
wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item,
const t_layer_height_range& layer_range,
const int extruder = 0,
const int index = -1);
wxDataViewItem Delete(const wxDataViewItem &item);
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);
void DeleteAll();
void DeleteChildren(wxDataViewItem& parent);
void DeleteVolumeChildren(wxDataViewItem& parent);
void DeleteSettings(const wxDataViewItem& parent);
wxDataViewItem GetItemById(int obj_idx);
wxDataViewItem GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type);
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx);
wxDataViewItem GetItemByLayerId(int obj_idx, int layer_idx);
wxDataViewItem GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
int GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
int GetIdByItem(const wxDataViewItem& item) const;
int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const;
int GetObjectIdByItem(const wxDataViewItem& item) const;
int GetVolumeIdByItem(const wxDataViewItem& item) const;
int GetInstanceIdByItem(const wxDataViewItem& item) const;
int GetLayerIdByItem(const wxDataViewItem& item) const;
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
int GetRowByItem(const wxDataViewItem& item) const;
bool IsEmpty() { return m_objects.empty(); }
bool InvalidItem(const wxDataViewItem& item);
// helper method for wxLog
wxString GetName(const wxDataViewItem &item) const;
wxBitmap& GetBitmap(const wxDataViewItem &item) const;
wxString GetExtruder(const wxDataViewItem &item) const;
int GetExtruderNumber(const wxDataViewItem &item) const;
// helper methods to change the model
virtual unsigned int GetColumnCount() const override { return 3;}
virtual wxString GetColumnType(unsigned int col) const override{ return wxT("string"); }
virtual void GetValue( wxVariant &variant,
const wxDataViewItem &item,
unsigned int col) const override;
virtual bool SetValue( const wxVariant &variant,
const wxDataViewItem &item,
unsigned int col) override;
bool SetValue( const wxVariant &variant,
const int item_idx,
unsigned int col);
void SetExtruder(const wxString& extruder, wxDataViewItem item);
// For parent move child from cur_volume_id place to new_volume_id
// Remaining items will moved up/down accordingly
wxDataViewItem ReorganizeChildren( const int cur_volume_id,
const int new_volume_id,
const wxDataViewItem &parent);
wxDataViewItem ReorganizeObjects( int current_id, int new_id);
virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override;
virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override;
// get object item
wxDataViewItem GetTopParent(const wxDataViewItem &item) const;
virtual bool IsContainer(const wxDataViewItem &item) const override;
virtual unsigned int GetChildren(const wxDataViewItem &parent,
wxDataViewItemArray &array) const override;
void GetAllChildren(const wxDataViewItem &parent,wxDataViewItemArray &array) const;
// Is the container just a header or an item with all columns
// In our case it is an item with all columns
virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
ItemType GetItemType(const wxDataViewItem &item) const ;
wxDataViewItem GetItemByType( const wxDataViewItem &parent_item,
ItemType type) const;
wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const;
wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const;
wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const;
bool IsSettingsItem(const wxDataViewItem &item) const;
void UpdateSettingsDigest( const wxDataViewItem &item,
const std::vector<std::string>& categories);
bool IsPrintable(const wxDataViewItem &item) const;
void UpdateObjectPrintable(wxDataViewItem parent_item);
void UpdateInstancesPrintable(wxDataViewItem parent_item);
void SetVolumeBitmaps(const std::vector<wxBitmap*>& volume_bmps) { m_volume_bmps = volume_bmps; }
void SetWarningBitmap(wxBitmap* bitmap) { m_warning_bmp = bitmap; }
void SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType type);
wxDataViewItem SetPrintableState( PrintIndicator printable, int obj_idx,
int subobj_idx = -1,
ItemType subobj_type = itInstance);
wxDataViewItem SetObjectPrintableState(PrintIndicator printable, wxDataViewItem obj_item);
void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; }
// Rescale bitmaps for existing Items
void Rescale();
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
const bool is_marked = false);
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const;
bool UpdateColumValues(unsigned col);
void UpdateExtruderBitmap(wxDataViewItem item);
private:
wxDataViewItem AddRoot(const wxDataViewItem& parent_item, const ItemType root_type);
wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item);
};
}
}
#endif // slic3r_GUI_ObjectDataViewModel_hpp_

View file

@ -169,7 +169,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
info_manifold_text->SetFont(wxGetApp().small_font()); info_manifold_text->SetFont(wxGetApp().small_font());
info_manifold = new wxStaticText(parent, wxID_ANY, ""); info_manifold = new wxStaticText(parent, wxID_ANY, "");
info_manifold->SetFont(wxGetApp().small_font()); info_manifold->SetFont(wxGetApp().small_font());
manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap(parent, "exclamation")); manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap("exclamation"));
auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL); auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL);
sizer_manifold->Add(info_manifold_text, 0); sizer_manifold->Add(info_manifold_text, 0);
sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2); sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2);
@ -188,7 +188,7 @@ void ObjectInfo::show_sizer(bool show)
void ObjectInfo::msw_rescale() void ObjectInfo::msw_rescale()
{ {
manifold_warning_icon->SetBitmap(create_scaled_bitmap(nullptr, "exclamation")); manifold_warning_icon->SetBitmap(create_scaled_bitmap("exclamation"));
} }
enum SlicedInfoIdx enum SlicedInfoIdx
@ -258,7 +258,7 @@ void SlicedInfo::SetTextAndShow(SlicedInfoIdx idx, const wxString& text, const w
} }
PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) : PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) :
wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), 0, nullptr, wxCB_READONLY), PresetBitmapComboBox(parent, wxSize(15 * wxGetApp().em_unit(), -1)),
preset_type(preset_type), preset_type(preset_type),
last_selected(wxNOT_FOUND), last_selected(wxNOT_FOUND),
m_em_unit(wxGetApp().em_unit()) m_em_unit(wxGetApp().em_unit())
@ -1875,6 +1875,7 @@ struct Plater::priv
} }
void export_gcode(fs::path output_path, PrintHostJob upload_job); void export_gcode(fs::path output_path, PrintHostJob upload_job);
void reload_from_disk(); void reload_from_disk();
void reload_all_from_disk();
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
void set_current_panel(wxPanel* panel); void set_current_panel(wxPanel* panel);
@ -2075,6 +2076,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); });
view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); });
view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); });
view3D_canvas->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { if (!this->model.objects.empty()) this->reload_all_from_disk(); });
// 3DScene/Toolbar: // 3DScene/Toolbar:
view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this);
@ -3447,6 +3449,24 @@ void Plater::priv::reload_from_disk()
} }
} }
void Plater::priv::reload_all_from_disk()
{
Plater::TakeSnapshot snapshot(q, _(L("Reload all from disk")));
Plater::SuppressSnapshots suppress(q);
Selection& selection = get_selection();
Selection::IndicesList curr_idxs = selection.get_volume_idxs();
// reload from disk uses selection
select_all();
reload_from_disk();
// restore previous selection
selection.clear();
for (unsigned int idx : curr_idxs)
{
selection.add(idx, false);
}
}
void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/)
{ {
if (obj_idx < 0) if (obj_idx < 0)
@ -5034,6 +5054,11 @@ void Plater::reload_from_disk()
p->reload_from_disk(); p->reload_from_disk();
} }
void Plater::reload_all_from_disk()
{
p->reload_all_from_disk();
}
bool Plater::has_toolpaths_to_export() const bool Plater::has_toolpaths_to_export() const
{ {
return p->preview->get_canvas3d()->has_toolpaths_to_export(); return p->preview->get_canvas3d()->has_toolpaths_to_export();

View file

@ -12,6 +12,7 @@
#include "3DScene.hpp" #include "3DScene.hpp"
#include "GLTexture.hpp" #include "GLTexture.hpp"
#include "wxExtensions.hpp"
class wxButton; class wxButton;
class ScalableButton; class ScalableButton;
@ -49,7 +50,7 @@ using t_optgroups = std::vector <std::shared_ptr<ConfigOptionsGroup>>;
class Plater; class Plater;
enum class ActionButtonType : int; enum class ActionButtonType : int;
class PresetComboBox : public wxBitmapComboBox class PresetComboBox : public PresetBitmapComboBox
{ {
public: public:
PresetComboBox(wxWindow *parent, Preset::Type preset_type); PresetComboBox(wxWindow *parent, Preset::Type preset_type);
@ -193,6 +194,7 @@ public:
void export_amf(); void export_amf();
void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
void reload_from_disk(); void reload_from_disk();
void reload_all_from_disk();
bool has_toolpaths_to_export() const; bool has_toolpaths_to_export() const;
void export_toolpaths_to_obj() const; void export_toolpaths_to_obj() const;
void hollow(); void hollow();

View file

@ -873,18 +873,14 @@ bool PresetCollection::delete_preset(const std::string& name)
return true; return true;
} }
void PresetCollection::load_bitmap_default(wxWindow *window, const std::string &file_name) void PresetCollection::load_bitmap_default(const std::string &file_name)
{ {
// XXX: See note in PresetBundle::load_compatible_bitmaps() *m_bitmap_main_frame = create_scaled_bitmap(file_name);
(void)window;
*m_bitmap_main_frame = create_scaled_bitmap(nullptr, file_name);
} }
void PresetCollection::load_bitmap_add(wxWindow *window, const std::string &file_name) void PresetCollection::load_bitmap_add(const std::string &file_name)
{ {
// XXX: See note in PresetBundle::load_compatible_bitmaps() *m_bitmap_add = create_scaled_bitmap(file_name);
(void)window;
*m_bitmap_add = create_scaled_bitmap(nullptr, file_name);
} }
const Preset* PresetCollection::get_selected_preset_parent() const const Preset* PresetCollection::get_selected_preset_parent() const

View file

@ -313,10 +313,10 @@ public:
bool delete_preset(const std::string& name); bool delete_preset(const std::string& name);
// Load default bitmap to be placed at the wxBitmapComboBox of a MainFrame. // Load default bitmap to be placed at the wxBitmapComboBox of a MainFrame.
void load_bitmap_default(wxWindow *window, const std::string &file_name); void load_bitmap_default(const std::string &file_name);
// Load "add new printer" bitmap to be placed at the wxBitmapComboBox of a MainFrame. // Load "add new printer" bitmap to be placed at the wxBitmapComboBox of a MainFrame.
void load_bitmap_add(wxWindow *window, const std::string &file_name); void load_bitmap_add(const std::string &file_name);
// Compatible & incompatible marks, to be placed at the wxBitmapComboBox items. // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items.
void set_bitmap_compatible (const wxBitmap *bmp) { m_bitmap_compatible = bmp; } void set_bitmap_compatible (const wxBitmap *bmp) { m_bitmap_compatible = bmp; }

View file

@ -480,19 +480,12 @@ void PresetBundle::export_selections(AppConfig &config)
config.set("presets", "printer", printers.get_selected_preset_name()); config.set("presets", "printer", printers.get_selected_preset_name());
} }
void PresetBundle::load_compatible_bitmaps(wxWindow *window) void PresetBundle::load_compatible_bitmaps()
{ {
// We don't actually pass the window pointer here and instead generate *m_bitmapCompatible = create_scaled_bitmap("flag_green");
// a low DPI bitmap, because the wxBitmapComboBox and wxDataViewCtrl don't support *m_bitmapIncompatible = create_scaled_bitmap("flag_red");
// high DPI bitmaps very well, they compute their dimensions wrong. *m_bitmapLock = create_scaled_bitmap("lock_closed");
// TODO: Update this when fixed in wxWidgets *m_bitmapLockOpen = create_scaled_bitmap("lock_open");
// See also PresetCollection::load_bitmap_default() and PresetCollection::load_bitmap_add()
(void)window;
*m_bitmapCompatible = create_scaled_bitmap(nullptr, "flag_green");
*m_bitmapIncompatible = create_scaled_bitmap(nullptr, "flag_red");
*m_bitmapLock = create_scaled_bitmap(nullptr, "lock_closed");
*m_bitmapLockOpen = create_scaled_bitmap(nullptr, "lock_open");
prints .set_bitmap_compatible(m_bitmapCompatible); prints .set_bitmap_compatible(m_bitmapCompatible);
filaments .set_bitmap_compatible(m_bitmapCompatible); filaments .set_bitmap_compatible(m_bitmapCompatible);
@ -1536,31 +1529,7 @@ void PresetBundle::set_filament_preset(size_t idx, const std::string &name)
filament_presets[idx] = Preset::remove_suffix_modified(name); filament_presets[idx] = Preset::remove_suffix_modified(name);
} }
static inline int hex_digit_to_int(const char c) void PresetBundle::load_default_preset_bitmaps()
{
return
(c >= '0' && c <= '9') ? int(c - '0') :
(c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
(c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
}
bool PresetBundle::parse_color(const std::string &scolor, unsigned char *rgb_out)
{
rgb_out[0] = rgb_out[1] = rgb_out[2] = 0;
if (scolor.size() != 7 || scolor.front() != '#')
return false;
const char *c = scolor.data() + 1;
for (size_t i = 0; i < 3; ++ i) {
int digit1 = hex_digit_to_int(*c ++);
int digit2 = hex_digit_to_int(*c ++);
if (digit1 == -1 || digit2 == -1)
return false;
rgb_out[i] = (unsigned char)(digit1 * 16 + digit2);
}
return true;
}
void PresetBundle::load_default_preset_bitmaps(wxWindow *window)
{ {
// Clear bitmap cache, before load new scaled default preset bitmaps // Clear bitmap cache, before load new scaled default preset bitmaps
m_bitmapCache->clear(); m_bitmapCache->clear();
@ -1570,13 +1539,13 @@ void PresetBundle::load_default_preset_bitmaps(wxWindow *window)
this->sla_materials.clear_bitmap_cache(); this->sla_materials.clear_bitmap_cache();
this->printers.clear_bitmap_cache(); this->printers.clear_bitmap_cache();
this->prints.load_bitmap_default(window, "cog"); this->prints.load_bitmap_default("cog");
this->sla_prints.load_bitmap_default(window, "cog"); this->sla_prints.load_bitmap_default("cog");
this->filaments.load_bitmap_default(window, "spool.png"); this->filaments.load_bitmap_default("spool.png");
this->sla_materials.load_bitmap_default(window, "resin"); this->sla_materials.load_bitmap_default("resin");
this->printers.load_bitmap_default(window, "printer"); this->printers.load_bitmap_default("printer");
this->printers.load_bitmap_add(window, "add.png"); this->printers.load_bitmap_add("add.png");
this->load_compatible_bitmaps(window); this->load_compatible_bitmaps();
} }
void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui) void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui)
@ -1587,7 +1556,7 @@ void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::Pre
unsigned char rgb[3]; unsigned char rgb[3];
std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder);
if (! parse_color(extruder_color, rgb)) if (!m_bitmapCache->parse_color(extruder_color, rgb))
// Extruder color is not defined. // Extruder color is not defined.
extruder_color.clear(); extruder_color.clear();
@ -1623,7 +1592,12 @@ void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::Pre
// To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size, so // To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size, so
// set a bitmap height to m_bitmapLock->GetHeight() // set a bitmap height to m_bitmapLock->GetHeight()
const int icon_height = m_bitmapLock->GetHeight();//2 * icon_unit; //16 * scale_f + 0.5f; // Note, under OSX we should use a ScaledHeight because of Retina scale
#ifdef __APPLE__
const int icon_height = m_bitmapLock->GetScaledHeight();
#else
const int icon_height = m_bitmapLock->GetHeight();
#endif
wxString tooltip = ""; wxString tooltip = "";
@ -1652,10 +1626,10 @@ void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::Pre
// Paint a red flag for incompatible presets. // Paint a red flag for incompatible presets.
bmps.emplace_back(preset.is_compatible ? m_bitmapCache->mkclear(normal_icon_width, icon_height) : *m_bitmapIncompatible); bmps.emplace_back(preset.is_compatible ? m_bitmapCache->mkclear(normal_icon_width, icon_height) : *m_bitmapIncompatible);
// Paint the color bars. // Paint the color bars.
parse_color(filament_rgb, rgb); m_bitmapCache->parse_color(filament_rgb, rgb);
bmps.emplace_back(m_bitmapCache->mksolid(single_bar ? wide_icon_width : normal_icon_width, icon_height, rgb)); bmps.emplace_back(m_bitmapCache->mksolid(single_bar ? wide_icon_width : normal_icon_width, icon_height, rgb));
if (! single_bar) { if (! single_bar) {
parse_color(extruder_rgb, rgb); m_bitmapCache->parse_color(extruder_rgb, rgb);
bmps.emplace_back(m_bitmapCache->mksolid(thin_icon_width, icon_height, rgb)); bmps.emplace_back(m_bitmapCache->mksolid(thin_icon_width, icon_height, rgb));
} }
// Paint a lock at the system presets. // Paint a lock at the system presets.

View file

@ -129,9 +129,7 @@ public:
// preset if the current print or filament preset is not compatible. // preset if the current print or filament preset is not compatible.
void update_compatible(bool select_other_if_incompatible); void update_compatible(bool select_other_if_incompatible);
static bool parse_color(const std::string &scolor, unsigned char *rgb_out); void load_default_preset_bitmaps();
void load_default_preset_bitmaps(wxWindow *window);
// Set the is_visible flag for printer vendors, printer models and printer variants // Set the is_visible flag for printer vendors, printer models and printer variants
// based on the user configuration. // based on the user configuration.
@ -160,7 +158,7 @@ private:
// If it is not an external config, then the config will be stored into the user profile directory. // If it is not an external config, then the config will be stored into the user profile directory.
void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config); void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config);
void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree);
void load_compatible_bitmaps(wxWindow *window); void load_compatible_bitmaps();
DynamicPrintConfig full_fff_config() const; DynamicPrintConfig full_fff_config() const;
DynamicPrintConfig full_sla_config() const; DynamicPrintConfig full_sla_config() const;

View file

@ -114,7 +114,7 @@ void Tab::create_preset_tab()
#endif //__WXOSX__ #endif //__WXOSX__
// preset chooser // preset chooser
m_presets_choice = new wxBitmapComboBox(panel, wxID_ANY, "", wxDefaultPosition, wxSize(35 * m_em_unit, -1), 0, 0, wxCB_READONLY); m_presets_choice = new PresetBitmapComboBox(panel, wxSize(35 * m_em_unit, -1));
auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
@ -1690,7 +1690,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) {
auto btn = new wxButton(parent, wxID_ANY, " " + _(L("Browse"))+" " +dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); auto btn = new wxButton(parent, wxID_ANY, " " + _(L("Browse"))+" " +dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
btn->SetBitmap(create_scaled_bitmap(this, "browse")); btn->SetBitmap(create_scaled_bitmap("browse"));
auto sizer = new wxBoxSizer(wxHORIZONTAL); auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn); sizer->Add(btn);

View file

@ -120,7 +120,7 @@ protected:
Preset::Type m_type; Preset::Type m_type;
std::string m_name; std::string m_name;
const wxString m_title; const wxString m_title;
wxBitmapComboBox* m_presets_choice; PresetBitmapComboBox* m_presets_choice;
ScalableButton* m_btn_save_preset; ScalableButton* m_btn_save_preset;
ScalableButton* m_btn_delete_preset; ScalableButton* m_btn_delete_preset;
ScalableButton* m_btn_hide_incompatible_presets; ScalableButton* m_btn_hide_incompatible_presets;

View file

@ -149,7 +149,7 @@ MsgDataIncompatible::MsgDataIncompatible(const std::unordered_map<std::string, w
MsgDialog(nullptr, wxString::Format(_(L("%s incompatibility")), SLIC3R_APP_NAME), MsgDialog(nullptr, wxString::Format(_(L("%s incompatibility")), SLIC3R_APP_NAME),
wxString::Format(_(L("%s configuration is incompatible")), SLIC3R_APP_NAME), wxID_NONE) wxString::Format(_(L("%s configuration is incompatible")), SLIC3R_APP_NAME), wxID_NONE)
{ {
logo->SetBitmap(create_scaled_bitmap(this, "PrusaSlicer_192px_grayscale.png", 192)); logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192));
auto *text = new wxStaticText(this, wxID_ANY, wxString::Format(_(L( auto *text = new wxStaticText(this, wxID_ANY, wxString::Format(_(L(
"This version of %s is not compatible with currently installed configuration bundles.\n" "This version of %s is not compatible with currently installed configuration bundles.\n"

View file

@ -1,7 +1,7 @@
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
#include "WipeTowerDialog.hpp" #include "WipeTowerDialog.hpp"
#include "PresetBundle.hpp" #include "BitmapCache.hpp"
#include "GUI.hpp" #include "GUI.hpp"
#include "I18N.hpp" #include "I18N.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
@ -191,7 +191,7 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con
for (const std::string& color : extruder_colours) { for (const std::string& color : extruder_colours) {
unsigned char rgb[3]; unsigned char rgb[3];
Slic3r::PresetBundle::parse_color(color, rgb); Slic3r::GUI::BitmapCache::parse_color(color, rgb);
m_colours.push_back(wxColor(rgb[0], rgb[1], rgb[2])); m_colours.push_back(wxColor(rgb[0], rgb[1], rgb[2]));
} }

File diff suppressed because it is too large Load diff

View file

@ -4,23 +4,14 @@
#include <wx/checklst.h> #include <wx/checklst.h>
#include <wx/combo.h> #include <wx/combo.h>
#include <wx/dataview.h> #include <wx/dataview.h>
#include <wx/dc.h>
#include <wx/wupdlock.h>
#include <wx/button.h> #include <wx/button.h>
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/menu.h> #include <wx/menu.h>
#include <wx/wx.h> #include <wx/bmpcbox.h>
#include <vector> #include <vector>
#include <set>
#include <functional> #include <functional>
namespace Slic3r {
enum class ModelVolumeType : int;
};
typedef double coordf_t;
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
#ifdef __WXMSW__ #ifdef __WXMSW__
void msw_rescale_menu(wxMenu* menu); void msw_rescale_menu(wxMenu* menu);
@ -48,14 +39,12 @@ wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string,
void enable_menu_item(wxUpdateUIEvent& evt, std::function<bool()> const cb_condition, wxMenuItem* item, wxWindow* win); void enable_menu_item(wxUpdateUIEvent& evt, std::function<bool()> const cb_condition, wxMenuItem* item, wxWindow* win);
class wxDialog; class wxDialog;
class wxBitmapComboBox;
void edit_tooltip(wxString& tooltip); void edit_tooltip(wxString& tooltip);
void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector<int>& btn_ids); void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector<int>& btn_ids);
int em_unit(wxWindow* win); int em_unit(wxWindow* win);
float get_svg_scale_factor(wxWindow* win);
wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name, wxBitmap create_scaled_bitmap(const std::string& bmp_name, wxWindow *win = nullptr,
const int px_cnt = 16, const bool grayscale = false); const int px_cnt = 16, const bool grayscale = false);
std::vector<wxBitmap*> get_extruder_color_icons(bool thin_icon = false); std::vector<wxBitmap*> get_extruder_color_icons(bool thin_icon = false);
@ -102,6 +91,37 @@ public:
void OnListBoxSelection(wxCommandEvent& evt); void OnListBoxSelection(wxCommandEvent& evt);
}; };
namespace Slic3r {
namespace GUI {
// *** PresetBitmapComboBox ***
// BitmapComboBox used to presets list on Sidebar and Tabs
class PresetBitmapComboBox: public wxBitmapComboBox
{
public:
PresetBitmapComboBox(wxWindow* parent, const wxSize& size = wxDefaultSize);
~PresetBitmapComboBox() {}
#ifdef __APPLE__
protected:
/* For PresetBitmapComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina
* (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean
* "please scale this to such and such" but rather
* "the wxImage is already sized for backing scale such and such". )
* Unfortunately, the constructor changes the size of wxBitmap too.
* Thus We need to use unscaled size value for bitmaps that we use
* to avoid scaled size of control items.
* For this purpose control drawing methods and
* control size calculation methods (virtual) are overridden.
**/
virtual bool OnAddBitmap(const wxBitmap& bitmap) override;
virtual void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override;
#endif
};
}
}
// *** wxDataViewTreeCtrlComboBox *** // *** wxDataViewTreeCtrlComboBox ***
@ -127,598 +147,6 @@ public:
}; };
// ----------------------------------------------------------------------------
// DataViewBitmapText: helper class used by PrusaBitmapTextRenderer
// ----------------------------------------------------------------------------
class DataViewBitmapText : public wxObject
{
public:
DataViewBitmapText( const wxString &text = wxEmptyString,
const wxBitmap& bmp = wxNullBitmap) :
m_text(text),
m_bmp(bmp)
{ }
DataViewBitmapText(const DataViewBitmapText &other)
: wxObject(),
m_text(other.m_text),
m_bmp(other.m_bmp)
{ }
void SetText(const wxString &text) { m_text = text; }
wxString GetText() const { return m_text; }
void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; }
const wxBitmap &GetBitmap() const { return m_bmp; }
bool IsSameAs(const DataViewBitmapText& other) const {
return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp);
}
bool operator==(const DataViewBitmapText& other) const {
return IsSameAs(other);
}
bool operator!=(const DataViewBitmapText& other) const {
return !IsSameAs(other);
}
private:
wxString m_text;
wxBitmap m_bmp;
wxDECLARE_DYNAMIC_CLASS(DataViewBitmapText);
};
DECLARE_VARIANT_OBJECT(DataViewBitmapText)
// ----------------------------------------------------------------------------
// ObjectDataViewModelNode: a node inside ObjectDataViewModel
// ----------------------------------------------------------------------------
enum ItemType {
itUndef = 0,
itObject = 1,
itVolume = 2,
itInstanceRoot = 4,
itInstance = 8,
itSettings = 16,
itLayerRoot = 32,
itLayer = 64,
};
enum ColumnNumber
{
colName = 0, // item name
colPrint , // printable property
colExtruder , // extruder selection
colEditing , // item editing
};
enum PrintIndicator
{
piUndef = 0, // no print indicator
piPrintable , // printable
piUnprintable , // unprintable
};
class ObjectDataViewModelNode;
WX_DEFINE_ARRAY_PTR(ObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray);
class ObjectDataViewModelNode
{
ObjectDataViewModelNode* m_parent;
MyObjectTreeModelNodePtrArray m_children;
wxBitmap m_empty_bmp;
size_t m_volumes_cnt = 0;
std::vector< std::string > m_opt_categories;
t_layer_height_range m_layer_range = { 0.0f, 0.0f };
wxString m_name;
wxBitmap& m_bmp = m_empty_bmp;
ItemType m_type;
int m_idx = -1;
bool m_container = false;
wxString m_extruder = "default";
wxBitmap m_extruder_bmp;
wxBitmap m_action_icon;
PrintIndicator m_printable {piUndef};
wxBitmap m_printable_icon;
std::string m_action_icon_name = "";
Slic3r::ModelVolumeType m_volume_type;
// pointer to control (is needed to create scaled bitmaps)
wxDataViewCtrl* m_ctrl{ nullptr };
public:
ObjectDataViewModelNode(const wxString& name,
const wxString& extruder,
wxDataViewCtrl* ctrl):
m_parent(NULL),
m_name(name),
m_type(itObject),
m_extruder(extruder),
m_ctrl(ctrl)
{
set_action_and_extruder_icons();
init_container();
}
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const wxString& sub_obj_name,
const wxBitmap& bmp,
const wxString& extruder,
const int idx = -1 ) :
m_parent (parent),
m_name (sub_obj_name),
m_type (itVolume),
m_idx (idx),
m_extruder (extruder),
m_ctrl (parent->m_ctrl)
{
m_bmp = bmp;
set_action_and_extruder_icons();
init_container();
}
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const t_layer_height_range& layer_range,
const int idx = -1,
const wxString& extruder = wxEmptyString );
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type);
~ObjectDataViewModelNode()
{
// free all our children nodes
size_t count = m_children.GetCount();
for (size_t i = 0; i < count; i++)
{
ObjectDataViewModelNode *child = m_children[i];
delete child;
}
#ifndef NDEBUG
// Indicate that the object was deleted.
m_idx = -2;
#endif /* NDEBUG */
}
void init_container();
bool IsContainer() const
{
return m_container;
}
ObjectDataViewModelNode* GetParent()
{
assert(m_parent == nullptr || m_parent->valid());
return m_parent;
}
MyObjectTreeModelNodePtrArray& GetChildren()
{
return m_children;
}
ObjectDataViewModelNode* GetNthChild(unsigned int n)
{
return m_children.Item(n);
}
void Insert(ObjectDataViewModelNode* child, unsigned int n)
{
if (!m_container)
m_container = true;
m_children.Insert(child, n);
}
void Append(ObjectDataViewModelNode* child)
{
if (!m_container)
m_container = true;
m_children.Add(child);
}
void RemoveAllChildren()
{
if (GetChildCount() == 0)
return;
for (int id = int(GetChildCount()) - 1; id >= 0; --id)
{
if (m_children.Item(id)->GetChildCount() > 0)
m_children[id]->RemoveAllChildren();
auto node = m_children[id];
m_children.RemoveAt(id);
delete node;
}
}
size_t GetChildCount() const
{
return m_children.GetCount();
}
bool SetValue(const wxVariant &variant, unsigned int col);
void SetBitmap(const wxBitmap &icon) { m_bmp = icon; }
const wxBitmap& GetBitmap() const { return m_bmp; }
const wxString& GetName() const { return m_name; }
ItemType GetType() const { return m_type; }
void SetIdx(const int& idx);
int GetIdx() const { return m_idx; }
t_layer_height_range GetLayerRange() const { return m_layer_range; }
PrintIndicator IsPrintable() const { return m_printable; }
// use this function only for childrens
void AssignAllVal(ObjectDataViewModelNode& from_node)
{
// ! Don't overwrite other values because of equality of this values for all children --
m_name = from_node.m_name;
m_bmp = from_node.m_bmp;
m_idx = from_node.m_idx;
m_extruder = from_node.m_extruder;
m_type = from_node.m_type;
}
bool SwapChildrens(int frst_id, int scnd_id) {
if (GetChildCount() < 2 ||
frst_id < 0 || (size_t)frst_id >= GetChildCount() ||
scnd_id < 0 || (size_t)scnd_id >= GetChildCount())
return false;
ObjectDataViewModelNode new_scnd = *GetNthChild(frst_id);
ObjectDataViewModelNode new_frst = *GetNthChild(scnd_id);
new_scnd.m_idx = m_children.Item(scnd_id)->m_idx;
new_frst.m_idx = m_children.Item(frst_id)->m_idx;
m_children.Item(frst_id)->AssignAllVal(new_frst);
m_children.Item(scnd_id)->AssignAllVal(new_scnd);
return true;
}
// Set action icons for node
void set_action_and_extruder_icons();
// Set printable icon for node
void set_printable_icon(PrintIndicator printable);
void update_settings_digest_bitmaps();
bool update_settings_digest(const std::vector<std::string>& categories);
int volume_type() const { return int(m_volume_type); }
void msw_rescale();
#ifndef NDEBUG
bool valid();
#endif /* NDEBUG */
bool invalid() const { return m_idx < -1; }
private:
friend class ObjectDataViewModel;
};
// ----------------------------------------------------------------------------
// ObjectDataViewModel
// ----------------------------------------------------------------------------
// custom message the model sends to associated control to notify a last volume deleted from the object:
wxDECLARE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent);
class ObjectDataViewModel :public wxDataViewModel
{
std::vector<ObjectDataViewModelNode*> m_objects;
std::vector<wxBitmap*> m_volume_bmps;
wxBitmap* m_warning_bmp { nullptr };
wxDataViewCtrl* m_ctrl { nullptr };
public:
ObjectDataViewModel();
~ObjectDataViewModel();
wxDataViewItem Add( const wxString &name,
const int extruder,
const bool has_errors = false);
wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item,
const wxString &name,
const Slic3r::ModelVolumeType volume_type,
const bool has_errors = false,
const int extruder = 0,
const bool create_frst_child = true);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, const std::vector<bool>& print_indicator);
wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item);
wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item,
const t_layer_height_range& layer_range,
const int extruder = 0,
const int index = -1);
wxDataViewItem Delete(const wxDataViewItem &item);
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);
void DeleteAll();
void DeleteChildren(wxDataViewItem& parent);
void DeleteVolumeChildren(wxDataViewItem& parent);
void DeleteSettings(const wxDataViewItem& parent);
wxDataViewItem GetItemById(int obj_idx);
wxDataViewItem GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type);
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx);
wxDataViewItem GetItemByLayerId(int obj_idx, int layer_idx);
wxDataViewItem GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
int GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
int GetIdByItem(const wxDataViewItem& item) const;
int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const;
int GetObjectIdByItem(const wxDataViewItem& item) const;
int GetVolumeIdByItem(const wxDataViewItem& item) const;
int GetInstanceIdByItem(const wxDataViewItem& item) const;
int GetLayerIdByItem(const wxDataViewItem& item) const;
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
int GetRowByItem(const wxDataViewItem& item) const;
bool IsEmpty() { return m_objects.empty(); }
bool InvalidItem(const wxDataViewItem& item);
// helper method for wxLog
wxString GetName(const wxDataViewItem &item) const;
wxBitmap& GetBitmap(const wxDataViewItem &item) const;
wxString GetExtruder(const wxDataViewItem &item) const;
int GetExtruderNumber(const wxDataViewItem &item) const;
// helper methods to change the model
virtual unsigned int GetColumnCount() const override { return 3;}
virtual wxString GetColumnType(unsigned int col) const override{ return wxT("string"); }
virtual void GetValue( wxVariant &variant,
const wxDataViewItem &item,
unsigned int col) const override;
virtual bool SetValue( const wxVariant &variant,
const wxDataViewItem &item,
unsigned int col) override;
bool SetValue( const wxVariant &variant,
const int item_idx,
unsigned int col);
void SetExtruder(const wxString& extruder, wxDataViewItem item);
// For parent move child from cur_volume_id place to new_volume_id
// Remaining items will moved up/down accordingly
wxDataViewItem ReorganizeChildren( const int cur_volume_id,
const int new_volume_id,
const wxDataViewItem &parent);
virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override;
virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override;
// get object item
wxDataViewItem GetTopParent(const wxDataViewItem &item) const;
virtual bool IsContainer(const wxDataViewItem &item) const override;
virtual unsigned int GetChildren(const wxDataViewItem &parent,
wxDataViewItemArray &array) const override;
void GetAllChildren(const wxDataViewItem &parent,wxDataViewItemArray &array) const;
// Is the container just a header or an item with all columns
// In our case it is an item with all columns
virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
ItemType GetItemType(const wxDataViewItem &item) const ;
wxDataViewItem GetItemByType( const wxDataViewItem &parent_item,
ItemType type) const;
wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const;
wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const;
wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const;
bool IsSettingsItem(const wxDataViewItem &item) const;
void UpdateSettingsDigest( const wxDataViewItem &item,
const std::vector<std::string>& categories);
bool IsPrintable(const wxDataViewItem &item) const;
void UpdateObjectPrintable(wxDataViewItem parent_item);
void UpdateInstancesPrintable(wxDataViewItem parent_item);
void SetVolumeBitmaps(const std::vector<wxBitmap*>& volume_bmps) { m_volume_bmps = volume_bmps; }
void SetWarningBitmap(wxBitmap* bitmap) { m_warning_bmp = bitmap; }
void SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType type);
wxDataViewItem SetPrintableState( PrintIndicator printable, int obj_idx,
int subobj_idx = -1,
ItemType subobj_type = itInstance);
wxDataViewItem SetObjectPrintableState(PrintIndicator printable, wxDataViewItem obj_item);
void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; }
// Rescale bitmaps for existing Items
void Rescale();
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
const bool is_marked = false);
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const;
bool UpdateColumValues(unsigned col);
void UpdateExtruderBitmap(wxDataViewItem item);
private:
wxDataViewItem AddRoot(const wxDataViewItem& parent_item, const ItemType root_type);
wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item);
};
// ----------------------------------------------------------------------------
// BitmapTextRenderer
// ----------------------------------------------------------------------------
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
class BitmapTextRenderer : public wxDataViewRenderer
#else
class BitmapTextRenderer : public wxDataViewCustomRenderer
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
{
public:
BitmapTextRenderer( wxWindow* parent,
wxDataViewCellMode mode =
#ifdef __WXOSX__
wxDATAVIEW_CELL_INERT
#else
wxDATAVIEW_CELL_EDITABLE
#endif
,int align = wxDVR_DEFAULT_ALIGNMENT
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
);
#else
) :
wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align),
m_parent(parent)
{}
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
bool SetValue(const wxVariant &value);
bool GetValue(wxVariant &value) const;
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY
virtual wxString GetAccessibleDescription() const override;
#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
virtual bool Render(wxRect cell, wxDC *dc, int state);
virtual wxSize GetSize() const;
bool HasEditorCtrl() const override
{
#ifdef __WXOSX__
return false;
#else
return true;
#endif
}
wxWindow* CreateEditorCtrl(wxWindow* parent,
wxRect labelRect,
const wxVariant& value) override;
bool GetValueFromEditorCtrl( wxWindow* ctrl,
wxVariant& value) override;
bool WasCanceled() const { return m_was_unusable_symbol; }
private:
DataViewBitmapText m_value;
bool m_was_unusable_symbol {false};
wxWindow* m_parent {nullptr};
};
// ----------------------------------------------------------------------------
// BitmapChoiceRenderer
// ----------------------------------------------------------------------------
class BitmapChoiceRenderer : public wxDataViewCustomRenderer
{
public:
BitmapChoiceRenderer(wxDataViewCellMode mode =
#ifdef __WXOSX__
wxDATAVIEW_CELL_INERT
#else
wxDATAVIEW_CELL_EDITABLE
#endif
,int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL
) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {}
bool SetValue(const wxVariant& value);
bool GetValue(wxVariant& value) const;
virtual bool Render(wxRect cell, wxDC* dc, int state);
virtual wxSize GetSize() const;
bool HasEditorCtrl() const override { return true; }
wxWindow* CreateEditorCtrl(wxWindow* parent,
wxRect labelRect,
const wxVariant& value) override;
bool GetValueFromEditorCtrl( wxWindow* ctrl,
wxVariant& value) override;
private:
DataViewBitmapText m_value;
};
// ----------------------------------------------------------------------------
// MyCustomRenderer
// ----------------------------------------------------------------------------
class MyCustomRenderer : public wxDataViewCustomRenderer
{
public:
// This renderer can be either activatable or editable, for demonstration
// purposes. In real programs, you should select whether the user should be
// able to activate or edit the cell and it doesn't make sense to switch
// between the two -- but this is just an example, so it doesn't stop us.
explicit MyCustomRenderer(wxDataViewCellMode mode)
: wxDataViewCustomRenderer("string", mode, wxALIGN_CENTER)
{ }
virtual bool Render(wxRect rect, wxDC *dc, int state) override/*wxOVERRIDE*/
{
dc->SetBrush(*wxLIGHT_GREY_BRUSH);
dc->SetPen(*wxTRANSPARENT_PEN);
rect.Deflate(2);
dc->DrawRoundedRectangle(rect, 5);
RenderText(m_value,
0, // no offset
wxRect(dc->GetTextExtent(m_value)).CentreIn(rect),
dc,
state);
return true;
}
virtual bool ActivateCell(const wxRect& WXUNUSED(cell),
wxDataViewModel *WXUNUSED(model),
const wxDataViewItem &WXUNUSED(item),
unsigned int WXUNUSED(col),
const wxMouseEvent *mouseEvent) override/*wxOVERRIDE*/
{
wxString position;
if (mouseEvent)
position = wxString::Format("via mouse at %d, %d", mouseEvent->m_x, mouseEvent->m_y);
else
position = "from keyboard";
// wxLogMessage("MyCustomRenderer ActivateCell() %s", position);
return false;
}
virtual wxSize GetSize() const override/*wxOVERRIDE*/
{
return wxSize(60, 20);
}
virtual bool SetValue(const wxVariant &value) override/*wxOVERRIDE*/
{
m_value = value.GetString();
return true;
}
virtual bool GetValue(wxVariant &WXUNUSED(value)) const override/*wxOVERRIDE*/{ return true; }
virtual bool HasEditorCtrl() const override/*wxOVERRIDE*/{ return true; }
virtual wxWindow*
CreateEditorCtrl(wxWindow* parent,
wxRect labelRect,
const wxVariant& value) override/*wxOVERRIDE*/
{
wxTextCtrl* text = new wxTextCtrl(parent, wxID_ANY, value,
labelRect.GetPosition(),
labelRect.GetSize(),
wxTE_PROCESS_ENTER);
text->SetInsertionPointEnd();
return text;
}
virtual bool
GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override/*wxOVERRIDE*/
{
wxTextCtrl* text = wxDynamicCast(ctrl, wxTextCtrl);
if (!text)
return false;
value = text->GetValue();
return true;
}
private:
wxString m_value;
};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ScalableBitmap // ScalableBitmap
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -733,6 +161,10 @@ public:
~ScalableBitmap() {} ~ScalableBitmap() {}
wxSize GetBmpSize() const;
int GetBmpWidth() const;
int GetBmpHeight() const;
void msw_rescale(); void msw_rescale();
const wxBitmap& bmp() const { return m_bmp; } const wxBitmap& bmp() const { return m_bmp; }

View file

@ -26,58 +26,6 @@ const char *const SUPPORT_TEST_MODELS[] = {
} // namespace } // namespace
// Test pair hash for 'nums' random number pairs.
template <class I, class II> void test_pairhash()
{
const constexpr size_t nums = 1000;
I A[nums] = {0}, B[nums] = {0};
std::unordered_set<I> CH;
std::unordered_map<II, std::pair<I, I>> ints;
std::random_device rd;
std::mt19937 gen(rd());
const I Ibits = int(sizeof(I) * CHAR_BIT);
const II IIbits = int(sizeof(II) * CHAR_BIT);
int bits = IIbits / 2 < Ibits ? Ibits / 2 : Ibits;
if (std::is_signed<I>::value) bits -= 1;
const I Imin = 0;
const I Imax = I(std::pow(2., bits) - 1);
std::uniform_int_distribution<I> dis(Imin, Imax);
for (size_t i = 0; i < nums;) {
I a = dis(gen);
if (CH.find(a) == CH.end()) { CH.insert(a); A[i] = a; ++i; }
}
for (size_t i = 0; i < nums;) {
I b = dis(gen);
if (CH.find(b) == CH.end()) { CH.insert(b); B[i] = b; ++i; }
}
for (size_t i = 0; i < nums; ++i) {
I a = A[i], b = B[i];
REQUIRE(a != b);
II hash_ab = sla::pairhash<I, II>(a, b);
II hash_ba = sla::pairhash<I, II>(b, a);
REQUIRE(hash_ab == hash_ba);
auto it = ints.find(hash_ab);
if (it != ints.end()) {
REQUIRE((
(it->second.first == a && it->second.second == b) ||
(it->second.first == b && it->second.second == a)
));
} else
ints[hash_ab] = std::make_pair(a, b);
}
}
TEST_CASE("Pillar pairhash should be unique", "[SLASupportGeneration]") { TEST_CASE("Pillar pairhash should be unique", "[SLASupportGeneration]") {
test_pairhash<int, int>(); test_pairhash<int, int>();
test_pairhash<int, long>(); test_pairhash<int, long>();
@ -225,69 +173,6 @@ TEST_CASE("InitializedRasterShouldBeNONEmpty", "[SLARasterOutput]") {
REQUIRE(raster.pixel_dimensions().h_mm == Approx(pixdim.h_mm)); REQUIRE(raster.pixel_dimensions().h_mm == Approx(pixdim.h_mm));
} }
using TPixel = uint8_t;
static constexpr const TPixel FullWhite = 255;
static constexpr const TPixel FullBlack = 0;
template <class A, int N> constexpr int arraysize(const A (&)[N]) { return N; }
static void check_raster_transformations(sla::Raster::Orientation o,
sla::Raster::TMirroring mirroring)
{
double disp_w = 120., disp_h = 68.;
sla::Raster::Resolution res{2560, 1440};
sla::Raster::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px};
auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)});
sla::Raster::Trafo trafo{o, mirroring};
trafo.origin_x = bb.center().x();
trafo.origin_y = bb.center().y();
sla::Raster raster{res, pixdim, trafo};
// create box of size 32x32 pixels (not 1x1 to avoid antialiasing errors)
coord_t pw = 32 * coord_t(std::ceil(scaled<double>(pixdim.w_mm)));
coord_t ph = 32 * coord_t(std::ceil(scaled<double>(pixdim.h_mm)));
ExPolygon box;
box.contour.points = {{-pw, -ph}, {pw, -ph}, {pw, ph}, {-pw, ph}};
double tr_x = scaled<double>(20.), tr_y = tr_x;
box.translate(tr_x, tr_y);
ExPolygon expected_box = box;
// Now calculate the position of the translated box according to output
// trafo.
if (o == sla::Raster::Orientation::roPortrait) expected_box.rotate(PI / 2.);
if (mirroring[X])
for (auto &p : expected_box.contour.points) p.x() = -p.x();
if (mirroring[Y])
for (auto &p : expected_box.contour.points) p.y() = -p.y();
raster.draw(box);
Point expected_coords = expected_box.contour.bounding_box().center();
double rx = unscaled(expected_coords.x() + bb.center().x()) / pixdim.w_mm;
double ry = unscaled(expected_coords.y() + bb.center().y()) / pixdim.h_mm;
auto w = size_t(std::floor(rx));
auto h = res.height_px - size_t(std::floor(ry));
REQUIRE((w < res.width_px && h < res.height_px));
auto px = raster.read_pixel(w, h);
if (px != FullWhite) {
sla::PNGImage img;
std::fstream outf("out.png", std::ios::out);
outf << img.serialize(raster);
}
REQUIRE(px == FullWhite);
}
TEST_CASE("MirroringShouldBeCorrect", "[SLARasterOutput]") { TEST_CASE("MirroringShouldBeCorrect", "[SLARasterOutput]") {
sla::Raster::TMirroring mirrorings[] = {sla::Raster::NoMirror, sla::Raster::TMirroring mirrorings[] = {sla::Raster::NoMirror,
sla::Raster::MirrorX, sla::Raster::MirrorX,
@ -301,54 +186,6 @@ TEST_CASE("MirroringShouldBeCorrect", "[SLARasterOutput]") {
check_raster_transformations(orientation, mirror); check_raster_transformations(orientation, mirror);
} }
static ExPolygon square_with_hole(double v)
{
ExPolygon poly;
coord_t V = scaled(v / 2.);
poly.contour.points = {{-V, -V}, {V, -V}, {V, V}, {-V, V}};
poly.holes.emplace_back();
V = V / 2;
poly.holes.front().points = {{-V, V}, {V, V}, {V, -V}, {-V, -V}};
return poly;
}
static double pixel_area(TPixel px, const sla::Raster::PixelDim &pxdim)
{
return (pxdim.h_mm * pxdim.w_mm) * px * 1. / (FullWhite - FullBlack);
}
static double raster_white_area(const sla::Raster &raster)
{
if (raster.empty()) return std::nan("");
auto res = raster.resolution();
double a = 0;
for (size_t x = 0; x < res.width_px; ++x)
for (size_t y = 0; y < res.height_px; ++y) {
auto px = raster.read_pixel(x, y);
a += pixel_area(px, raster.pixel_dimensions());
}
return a;
}
static double predict_error(const ExPolygon &p, const sla::Raster::PixelDim &pd)
{
auto lines = p.lines();
double pix_err = pixel_area(FullWhite, pd) / 2.;
// Worst case is when a line is parallel to the shorter axis of one pixel,
// when the line will be composed of the max number of pixels
double pix_l = std::min(pd.h_mm, pd.w_mm);
double error = 0.;
for (auto &l : lines)
error += (unscaled(l.length()) / pix_l) * pix_err;
return error;
}
TEST_CASE("RasterizedPolygonAreaShouldMatch", "[SLARasterOutput]") { TEST_CASE("RasterizedPolygonAreaShouldMatch", "[SLARasterOutput]") {
double disp_w = 120., disp_h = 68.; double disp_w = 120., disp_h = 68.;
@ -388,8 +225,4 @@ TEST_CASE("Triangle mesh conversions should be correct", "[SLAConversions]")
std::fstream infile{"extruder_idler_quads.obj", std::ios::in}; std::fstream infile{"extruder_idler_quads.obj", std::ios::in};
cntr.from_obj(infile); cntr.from_obj(infile);
} }
} }

View file

@ -292,3 +292,103 @@ void check_validity(const TriangleMesh &input_mesh, int flags)
REQUIRE(mesh.is_manifold()); REQUIRE(mesh.is_manifold());
} }
} }
void check_raster_transformations(sla::Raster::Orientation o, sla::Raster::TMirroring mirroring)
{
double disp_w = 120., disp_h = 68.;
sla::Raster::Resolution res{2560, 1440};
sla::Raster::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px};
auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)});
sla::Raster::Trafo trafo{o, mirroring};
trafo.origin_x = bb.center().x();
trafo.origin_y = bb.center().y();
sla::Raster raster{res, pixdim, trafo};
// create box of size 32x32 pixels (not 1x1 to avoid antialiasing errors)
coord_t pw = 32 * coord_t(std::ceil(scaled<double>(pixdim.w_mm)));
coord_t ph = 32 * coord_t(std::ceil(scaled<double>(pixdim.h_mm)));
ExPolygon box;
box.contour.points = {{-pw, -ph}, {pw, -ph}, {pw, ph}, {-pw, ph}};
double tr_x = scaled<double>(20.), tr_y = tr_x;
box.translate(tr_x, tr_y);
ExPolygon expected_box = box;
// Now calculate the position of the translated box according to output
// trafo.
if (o == sla::Raster::Orientation::roPortrait) expected_box.rotate(PI / 2.);
if (mirroring[X])
for (auto &p : expected_box.contour.points) p.x() = -p.x();
if (mirroring[Y])
for (auto &p : expected_box.contour.points) p.y() = -p.y();
raster.draw(box);
Point expected_coords = expected_box.contour.bounding_box().center();
double rx = unscaled(expected_coords.x() + bb.center().x()) / pixdim.w_mm;
double ry = unscaled(expected_coords.y() + bb.center().y()) / pixdim.h_mm;
auto w = size_t(std::floor(rx));
auto h = res.height_px - size_t(std::floor(ry));
REQUIRE((w < res.width_px && h < res.height_px));
auto px = raster.read_pixel(w, h);
if (px != FullWhite) {
sla::PNGImage img;
std::fstream outf("out.png", std::ios::out);
outf << img.serialize(raster);
}
REQUIRE(px == FullWhite);
}
ExPolygon square_with_hole(double v)
{
ExPolygon poly;
coord_t V = scaled(v / 2.);
poly.contour.points = {{-V, -V}, {V, -V}, {V, V}, {-V, V}};
poly.holes.emplace_back();
V = V / 2;
poly.holes.front().points = {{-V, V}, {V, V}, {V, -V}, {-V, -V}};
return poly;
}
double raster_white_area(const sla::Raster &raster)
{
if (raster.empty()) return std::nan("");
auto res = raster.resolution();
double a = 0;
for (size_t x = 0; x < res.width_px; ++x)
for (size_t y = 0; y < res.height_px; ++y) {
auto px = raster.read_pixel(x, y);
a += pixel_area(px, raster.pixel_dimensions());
}
return a;
}
double predict_error(const ExPolygon &p, const sla::Raster::PixelDim &pd)
{
auto lines = p.lines();
double pix_err = pixel_area(FullWhite, pd) / 2.;
// Worst case is when a line is parallel to the shorter axis of one pixel,
// when the line will be composed of the max number of pixels
double pix_l = std::min(pd.h_mm, pd.w_mm);
double error = 0.;
for (auto &l : lines)
error += (unscaled(l.length()) / pix_l) * pix_err;
return error;
}

View file

@ -6,6 +6,7 @@
// Debug // Debug
#include <fstream> #include <fstream>
#include <unordered_set>
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
#include "libslic3r/Format/OBJ.hpp" #include "libslic3r/Format/OBJ.hpp"
@ -109,4 +110,78 @@ inline void test_support_model_collision(
test_support_model_collision(obj_filename, input_supportcfg, hcfg, {}); test_support_model_collision(obj_filename, input_supportcfg, hcfg, {});
} }
// Test pair hash for 'nums' random number pairs.
template <class I, class II> void test_pairhash()
{
const constexpr size_t nums = 1000;
I A[nums] = {0}, B[nums] = {0};
std::unordered_set<I> CH;
std::unordered_map<II, std::pair<I, I>> ints;
std::random_device rd;
std::mt19937 gen(rd());
const I Ibits = int(sizeof(I) * CHAR_BIT);
const II IIbits = int(sizeof(II) * CHAR_BIT);
int bits = IIbits / 2 < Ibits ? Ibits / 2 : Ibits;
if (std::is_signed<I>::value) bits -= 1;
const I Imin = 0;
const I Imax = I(std::pow(2., bits) - 1);
std::uniform_int_distribution<I> dis(Imin, Imax);
for (size_t i = 0; i < nums;) {
I a = dis(gen);
if (CH.find(a) == CH.end()) { CH.insert(a); A[i] = a; ++i; }
}
for (size_t i = 0; i < nums;) {
I b = dis(gen);
if (CH.find(b) == CH.end()) { CH.insert(b); B[i] = b; ++i; }
}
for (size_t i = 0; i < nums; ++i) {
I a = A[i], b = B[i];
REQUIRE(a != b);
II hash_ab = sla::pairhash<I, II>(a, b);
II hash_ba = sla::pairhash<I, II>(b, a);
REQUIRE(hash_ab == hash_ba);
auto it = ints.find(hash_ab);
if (it != ints.end()) {
REQUIRE((
(it->second.first == a && it->second.second == b) ||
(it->second.first == b && it->second.second == a)
));
} else
ints[hash_ab] = std::make_pair(a, b);
}
}
// SLA Raster test utils:
using TPixel = uint8_t;
static constexpr const TPixel FullWhite = 255;
static constexpr const TPixel FullBlack = 0;
template <class A, int N> constexpr int arraysize(const A (&)[N]) { return N; }
void check_raster_transformations(sla::Raster::Orientation o,
sla::Raster::TMirroring mirroring);
ExPolygon square_with_hole(double v);
inline double pixel_area(TPixel px, const sla::Raster::PixelDim &pxdim)
{
return (pxdim.h_mm * pxdim.w_mm) * px * 1. / (FullWhite - FullBlack);
}
double raster_white_area(const sla::Raster &raster);
double predict_error(const ExPolygon &p, const sla::Raster::PixelDim &pd);
#endif // SLA_TEST_UTILS_HPP #endif // SLA_TEST_UTILS_HPP