diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index c4c7224fb..525cc7931 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -150,12 +150,64 @@ sub new { $self->rotate(rad2deg($angle), Z, 'absolute'); }; + # callback to react to gizmo rotate + my $on_gizmo_rotate_3D = sub { + my ($angle_x, $angle_y, $angle_z) = @_; + + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + + my $model_object = $self->{model}->objects->[$obj_idx]; + my $model_instance = $model_object->instances->[0]; + + $self->stop_background_process; + + my $rotation = Slic3r::Pointf3->new($angle_x, $angle_y, $angle_z); + foreach my $inst (@{ $model_object->instances }) { + $inst->set_rotations($rotation); + } + Slic3r::GUI::_3DScene::update_gizmos_data($self->{canvas3D}) if ($self->{canvas3D}); + + # update print and start background processing + $self->{print}->add_model_object($model_object, $obj_idx); + + $self->selection_changed; # refresh info (size etc.) + $self->update; + $self->schedule_background_process; + }; + # callback to react to gizmo flatten my $on_gizmo_flatten = sub { my ($angle, $axis_x, $axis_y, $axis_z) = @_; $self->rotate(rad2deg($angle), undef, 'absolute', $axis_x, $axis_y, $axis_z) if $angle != 0; }; + # callback to react to gizmo flatten + my $on_gizmo_flatten_3D = sub { + my ($angle_x, $angle_y, $angle_z) = @_; + + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + + my $model_object = $self->{model}->objects->[$obj_idx]; + my $model_instance = $model_object->instances->[0]; + + $self->stop_background_process; + + my $rotation = Slic3r::Pointf3->new($angle_x, $angle_y, $angle_z); + foreach my $inst (@{ $model_object->instances }) { + $inst->set_rotations($rotation); + } + Slic3r::GUI::_3DScene::update_gizmos_data($self->{canvas3D}) if ($self->{canvas3D}); + + # update print and start background processing + $self->{print}->add_model_object($model_object, $obj_idx); + + $self->selection_changed; # refresh info (size etc.) + $self->update; + $self->schedule_background_process; + }; + # callback to update object's geometry info while using gizmos my $on_update_geometry_info = sub { my ($size_x, $size_y, $size_z, $scale_factor) = @_; @@ -261,7 +313,9 @@ sub new { Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons); Slic3r::GUI::_3DScene::register_on_gizmo_scale_uniformly_callback($self->{canvas3D}, $on_gizmo_scale_uniformly); Slic3r::GUI::_3DScene::register_on_gizmo_rotate_callback($self->{canvas3D}, $on_gizmo_rotate); + Slic3r::GUI::_3DScene::register_on_gizmo_rotate_3D_callback($self->{canvas3D}, $on_gizmo_rotate_3D); Slic3r::GUI::_3DScene::register_on_gizmo_flatten_callback($self->{canvas3D}, $on_gizmo_flatten); + Slic3r::GUI::_3DScene::register_on_gizmo_flatten_3D_callback($self->{canvas3D}, $on_gizmo_flatten_3D); Slic3r::GUI::_3DScene::register_on_update_geometry_info_callback($self->{canvas3D}, $on_update_geometry_info); Slic3r::GUI::_3DScene::register_action_add_callback($self->{canvas3D}, $on_action_add); Slic3r::GUI::_3DScene::register_action_delete_callback($self->{canvas3D}, $on_action_delete); diff --git a/slic3r.pl b/slic3r.pl index 38c10a900..95427443b 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -109,6 +109,7 @@ if ((!@ARGV || $opt{gui}) && !$opt{no_gui} && !$opt{save} && eval "require Slic3 $Slic3r::GUI::no_plater = $opt{no_plater}; $Slic3r::GUI::autosave = $opt{autosave}; } + Slic3r::GUI::set_gui_appctl(); $gui = Slic3r::GUI->new; #setlocale(LC_NUMERIC, 'C'); $gui->{mainframe}->load_config_file($_) for @{$opt{load}}; @@ -121,6 +122,9 @@ if ((!@ARGV || $opt{gui}) && !$opt{no_gui} && !$opt{save} && eval "require Slic3 die $@ if $@ && $opt{gui}; if (@ARGV) { # slicing from command line + Slic3r::GUI::set_cli_appctl(); + my $appctl = Slic3r::AppController->new(); + $config->validate; if ($opt{repair}) { @@ -210,7 +214,10 @@ if (@ARGV) { # slicing from command line $sprint->apply_config($config); if ($opt{export_png}) { - $sprint->export_png; + # $sprint->export_png; + $appctl->set_model($model); + $appctl->set_print($sprint->_print); + $appctl->print_ctl()->slice_to_png(); } else { my $t0 = [gettimeofday]; # The following call may die if the output_filename_format template substitution fails, diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index c9b4989f5..c4a0f300a 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1284,6 +1284,9 @@ namespace Slic3r { transform(1, 0) * inv_sx, transform(1, 1) * inv_sy, transform(1, 2) * inv_sz, transform(2, 0) * inv_sx, transform(2, 1) * inv_sy, transform(2, 2) * inv_sz; +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d rotation = m3x3.eulerAngles(0, 1, 2); +#else Eigen::AngleAxisd rotation; rotation.fromRotationMatrix(m3x3); @@ -1292,6 +1295,7 @@ namespace Slic3r { return; double angle_z = (rotation.axis() == Vec3d::UnitZ()) ? rotation.angle() : -rotation.angle(); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #if ENABLE_MODELINSTANCE_3D_OFFSET instance.set_offset(offset); @@ -1300,7 +1304,11 @@ namespace Slic3r { instance.offset(1) = offset_y; #endif // ENABLE_MODELINSTANCE_3D_OFFSET instance.scaling_factor = sx; +#if ENABLE_MODELINSTANCE_3D_ROTATION + instance.set_rotation(rotation); +#else instance.rotation = angle_z; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION } bool _3MF_Importer::_handle_start_config(const char** attributes, unsigned int num_attributes) diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 89a0eacbe..3e0eb54de 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -30,7 +30,12 @@ // 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them. // 1 : Introduction of amf versioning. No other change in data saved into amf files. #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION +// 2 : Added z component of offset +// Added x and y components of rotation +#else // 2 : Added z component of offset. +#endif // ENABLE_MODELINSTANCE_3D_ROTATION const unsigned int VERSION_AMF = 2; #else const unsigned int VERSION_AMF = 1; @@ -127,6 +132,10 @@ struct AMFParserContext #if ENABLE_MODELINSTANCE_3D_OFFSET NODE_TYPE_DELTAZ, // amf/constellation/instance/deltaz #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + NODE_TYPE_RX, // amf/constellation/instance/rx + NODE_TYPE_RY, // amf/constellation/instance/ry +#endif // ENABLE_MODELINSTANCE_3D_ROTATION NODE_TYPE_RZ, // amf/constellation/instance/rz NODE_TYPE_SCALE, // amf/constellation/instance/scale NODE_TYPE_METADATA, // anywhere under amf/*/metadata @@ -134,7 +143,11 @@ struct AMFParserContext struct Instance { #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rx_set(false), ry_set(false), rz_set(false), scale_set(false) {} +#else Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rz_set(false), scale_set(false) {} +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else Instance() : deltax_set(false), deltay_set(false), rz_set(false), scale_set(false) {} #endif // ENABLE_MODELINSTANCE_3D_OFFSET @@ -149,6 +162,14 @@ struct AMFParserContext float deltaz; bool deltaz_set; #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + // Rotation around the X axis. + float rx; + bool rx_set; + // Rotation around the Y axis. + float ry; + bool ry_set; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION // Rotation around the Z axis. float rz; bool rz_set; @@ -275,6 +296,12 @@ void AMFParserContext::startElement(const char *name, const char **atts) else if (strcmp(name, "deltaz") == 0) node_type_new = NODE_TYPE_DELTAZ; #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + else if (strcmp(name, "rx") == 0) + node_type_new = NODE_TYPE_RX; + else if (strcmp(name, "ry") == 0) + node_type_new = NODE_TYPE_RY; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION else if (strcmp(name, "rz") == 0) node_type_new = NODE_TYPE_RZ; else if (strcmp(name, "scale") == 0) @@ -339,7 +366,11 @@ void AMFParserContext::characters(const XML_Char *s, int len) if (m_path.back() == NODE_TYPE_DELTAX || m_path.back() == NODE_TYPE_DELTAY || m_path.back() == NODE_TYPE_DELTAZ || - m_path.back() == NODE_TYPE_RZ || +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_path.back() == NODE_TYPE_RX || + m_path.back() == NODE_TYPE_RY || +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + m_path.back() == NODE_TYPE_RZ || m_path.back() == NODE_TYPE_SCALE) #else if (m_path.back() == NODE_TYPE_DELTAX || m_path.back() == NODE_TYPE_DELTAY || m_path.back() == NODE_TYPE_RZ || m_path.back() == NODE_TYPE_SCALE) @@ -391,6 +422,20 @@ void AMFParserContext::endElement(const char * /* name */) m_value[0].clear(); break; #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + case NODE_TYPE_RX: + assert(m_instance); + m_instance->rx = float(atof(m_value[0].c_str())); + m_instance->rx_set = true; + m_value[0].clear(); + break; + case NODE_TYPE_RY: + assert(m_instance); + m_instance->ry = float(atof(m_value[0].c_str())); + m_instance->ry_set = true; + m_value[0].clear(); + break; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION case NODE_TYPE_RZ: assert(m_instance); m_instance->rz = float(atof(m_value[0].c_str())); @@ -542,12 +587,16 @@ void AMFParserContext::endDocument() if (instance.deltax_set && instance.deltay_set) { ModelInstance *mi = m_model.objects[object.second.idx]->add_instance(); #if ENABLE_MODELINSTANCE_3D_OFFSET - mi->set_offset(Vec3d((double)instance.deltax, (double)instance.deltay, (double)instance.deltaz)); + mi->set_offset(Vec3d(instance.deltax_set ? (double)instance.deltax : 0.0, instance.deltay_set ? (double)instance.deltay : 0.0, instance.deltaz_set ? (double)instance.deltaz : 0.0)); #else mi->offset(0) = instance.deltax; mi->offset(1) = instance.deltay; #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + mi->set_rotation(Vec3d(instance.rx_set ? (double)instance.rx : 0.0, instance.ry_set ? (double)instance.ry : 0.0, instance.rz_set ? (double)instance.rz : 0.0)); +#else mi->rotation = instance.rz_set ? instance.rz : 0.f; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION mi->scaling_factor = instance.scale_set ? instance.scale : 1.f; } } @@ -851,6 +900,10 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c #if ENABLE_MODELINSTANCE_3D_OFFSET " %lf\n" #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + " %lf\n" + " %lf\n" +#endif // ENABLE_MODELINSTANCE_3D_ROTATION " %lf\n" " %lf\n" " \n", @@ -863,8 +916,15 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c instance->offset(0), instance->offset(1), #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + instance->get_rotation(X), + instance->get_rotation(Y), + instance->get_rotation(Z), +#else instance->rotation, +#endif // ENABLE_MODELINSTANCE_3D_ROTATION instance->scaling_factor); + //FIXME missing instance->scaling_factor instances.append(buf); } diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp index 45eb56c63..27095acef 100644 --- a/src/libslic3r/Format/PRUS.cpp +++ b/src/libslic3r/Format/PRUS.cpp @@ -164,7 +164,11 @@ bool load_prus(const char *path, Model *model) const char *zero_tag = ""; const char *zero_xml = strstr(scene_xml_data.data(), zero_tag); float trafo[3][4] = { 0 }; +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d instance_rotation = Vec3d::Zero(); +#else double instance_rotation = 0.; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION double instance_scaling_factor = 1.f; #if ENABLE_MODELINSTANCE_3D_OFFSET Vec3d instance_offset = Vec3d::Zero(); @@ -197,10 +201,14 @@ bool load_prus(const char *path, Model *model) instance_scaling_factor = scale[0]; scale[0] = scale[1] = scale[2] = 1.; } +#if ENABLE_MODELINSTANCE_3D_ROTATION + instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]); +#else if (rotation[0] == 0. && rotation[1] == 0.) { instance_rotation = - rotation[2]; rotation[2] = 0.; } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION Eigen::Matrix3f mat_rot, mat_scale, mat_trafo; mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) * Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) * @@ -366,8 +374,12 @@ bool load_prus(const char *path, Model *model) model_object = model->add_object(name_utf8.data(), path, std::move(mesh)); volume = model_object->volumes.front(); ModelInstance *instance = model_object->add_instance(); - instance->rotation = instance_rotation; - instance->scaling_factor = instance_scaling_factor; +#if ENABLE_MODELINSTANCE_3D_ROTATION + instance->set_rotation(instance_rotation); +#else + instance->rotation = instance_rotation; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + instance->scaling_factor = instance_scaling_factor; #if ENABLE_MODELINSTANCE_3D_OFFSET instance->set_offset(instance_offset); #else diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index ed705e007..2d23c69b7 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1066,6 +1066,29 @@ size_t ModelVolume::split(unsigned int max_extruders) return idx; } +#if ENABLE_MODELINSTANCE_3D_ROTATION +void ModelInstance::set_rotation(const Vec3d& rotation) +{ + set_rotation(X, rotation(0)); + set_rotation(Y, rotation(1)); + set_rotation(Z, rotation(2)); +} + +void ModelInstance::set_rotation(Axis axis, double rotation) +{ + static const double TWO_PI = 2.0 * (double)PI; + while (rotation < 0.0) + { + rotation += TWO_PI; + } + while (TWO_PI < rotation) + { + rotation -= TWO_PI; + } + m_rotation(axis) = rotation; +} +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const { mesh->transform(world_matrix(dont_translate).cast()); @@ -1110,7 +1133,12 @@ Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const void ModelInstance::transform_polygon(Polygon* polygon) const { +#if ENABLE_MODELINSTANCE_3D_ROTATION + // CHECK_ME -> Is the following correct or it should take in account all three rotations ? + polygon->rotate(this->m_rotation(2)); // rotate around polygon origin +#else polygon->rotate(this->rotation); // rotate around polygon origin +#endif // ENABLE_MODELINSTANCE_3D_ROTATION polygon->scale(this->scaling_factor); // scale around polygon origin } @@ -1126,7 +1154,15 @@ Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, b #endif // ENABLE_MODELINSTANCE_3D_OFFSET if (!dont_rotate) +#if ENABLE_MODELINSTANCE_3D_ROTATION + { + m.rotate(Eigen::AngleAxisd(m_rotation(2), Vec3d::UnitZ())); + m.rotate(Eigen::AngleAxisd(m_rotation(1), Vec3d::UnitY())); + m.rotate(Eigen::AngleAxisd(m_rotation(0), Vec3d::UnitX())); + } +#else m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ())); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION if (!dont_scale) m.scale(scaling_factor); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 3acf9b8d2..062efdb85 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -248,11 +248,16 @@ public: #if ENABLE_MODELINSTANCE_3D_OFFSET private: Vec3d m_offset; // in unscaled coordinates +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d m_rotation; // Rotation around the three axes, in radians around mesh center point +#endif // ENABLE_MODELINSTANCE_3D_ROTATION public: #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if !ENABLE_MODELINSTANCE_3D_ROTATION double rotation; // Rotation around the Z axis, in radians around mesh center point +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION double scaling_factor; #if !ENABLE_MODELINSTANCE_3D_OFFSET Vec2d offset; // in unscaled coordinates @@ -271,6 +276,14 @@ public: void set_offset(Axis axis, double offset) { m_offset(axis) = offset; } #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + const Vec3d& get_rotation() const { return m_rotation; } + double get_rotation(Axis axis) const { return m_rotation(axis); } + + void set_rotation(const Vec3d& rotation); + void set_rotation(Axis axis, double rotation); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; // Calculate a bounding box of a transformed mesh. To be called on an external mesh. @@ -291,9 +304,15 @@ private: ModelObject* object; #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + ModelInstance(ModelObject *object) : m_rotation(Vec3d::Zero()), scaling_factor(1), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {} + ModelInstance(ModelObject *object, const ModelInstance &other) : + m_rotation(other.m_rotation), scaling_factor(other.scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {} +#else ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {} ModelInstance(ModelObject *object, const ModelInstance &other) : rotation(other.rotation), scaling_factor(other.scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {} +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), offset(Vec2d::Zero()), object(object), print_volume_state(PVS_Inside) {} ModelInstance(ModelObject *object, const ModelInstance &other) : diff --git a/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.hpp index 372689c39..9f983ee9f 100644 --- a/src/libslic3r/ModelArrange.hpp +++ b/src/libslic3r/ModelArrange.hpp @@ -292,59 +292,59 @@ protected: using Distance = TCoord; using Pile = sl::Shapes; - Packer pck_; - PConfig pconf_; // Placement configuration - double bin_area_; - SpatIndex rtree_; - SpatIndex smallsrtree_; - double norm_; - Pile merged_pile_; - Box pilebb_; - ItemGroup remaining_; - ItemGroup items_; + Packer m_pck; + PConfig m_pconf; // Placement configuration + double m_bin_area; + SpatIndex m_rtree; + SpatIndex m_smallsrtree; + double m_norm; + Pile m_merged_pile; + Box m_pilebb; + ItemGroup m_remaining; + ItemGroup m_items; public: _ArrBase(const TBin& bin, Distance dist, std::function progressind, std::function stopcond): - pck_(bin, dist), bin_area_(sl::area(bin)), - norm_(std::sqrt(sl::area(bin))) + m_pck(bin, dist), m_bin_area(sl::area(bin)), + m_norm(std::sqrt(sl::area(bin))) { - fillConfig(pconf_); + fillConfig(m_pconf); - pconf_.before_packing = + m_pconf.before_packing = [this](const Pile& merged_pile, // merged pile const ItemGroup& items, // packed items const ItemGroup& remaining) // future items to be packed { - items_ = items; - merged_pile_ = merged_pile; - remaining_ = remaining; + m_items = items; + m_merged_pile = merged_pile; + m_remaining = remaining; - pilebb_ = sl::boundingBox(merged_pile); + m_pilebb = sl::boundingBox(merged_pile); - rtree_.clear(); - smallsrtree_.clear(); + m_rtree.clear(); + m_smallsrtree.clear(); // We will treat big items (compared to the print bed) differently auto isBig = [this](double a) { - return a/bin_area_ > BIG_ITEM_TRESHOLD ; + return a/m_bin_area > BIG_ITEM_TRESHOLD ; }; for(unsigned idx = 0; idx < items.size(); ++idx) { Item& itm = items[idx]; - if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx}); - smallsrtree_.insert({itm.boundingBox(), idx}); + if(isBig(itm.area())) m_rtree.insert({itm.boundingBox(), idx}); + m_smallsrtree.insert({itm.boundingBox(), idx}); } }; - pck_.progressIndicator(progressind); - pck_.stopCondition(stopcond); + m_pck.progressIndicator(progressind); + m_pck.stopCondition(stopcond); } template inline IndexedPackGroup operator()(Args&&...args) { - rtree_.clear(); - return pck_.executeIndexed(std::forward(args)...); + m_rtree.clear(); + return m_pck.executeIndexed(std::forward(args)...); } }; @@ -358,18 +358,18 @@ public: _ArrBase(bin, dist, progressind, stopcond) { - pconf_.object_function = [this, bin] (const Item &item) { + m_pconf.object_function = [this, bin] (const Item &item) { auto result = objfunc(bin.center(), - merged_pile_, - pilebb_, - items_, + m_merged_pile, + m_pilebb, + m_items, item, - bin_area_, - norm_, - rtree_, - smallsrtree_, - remaining_); + m_bin_area, + m_norm, + m_rtree, + m_smallsrtree, + m_remaining); double score = std::get<0>(result); auto& fullbb = std::get<1>(result); @@ -381,7 +381,7 @@ public: return score; }; - pck_.configure(pconf_); + m_pck.configure(m_pconf); } }; @@ -396,27 +396,27 @@ public: std::function stopcond): _ArrBase(bin, dist, progressind, stopcond) { - pconf_.object_function = [this, &bin] (const Item &item) { + m_pconf.object_function = [this, &bin] (const Item &item) { auto result = objfunc(bin.center(), - merged_pile_, - pilebb_, - items_, + m_merged_pile, + m_pilebb, + m_items, item, - bin_area_, - norm_, - rtree_, - smallsrtree_, - remaining_); + m_bin_area, + m_norm, + m_rtree, + m_smallsrtree, + m_remaining); double score = std::get<0>(result); auto isBig = [this](const Item& itm) { - return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ; + return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; }; if(isBig(item)) { - auto mp = merged_pile_; + auto mp = m_merged_pile; mp.push_back(item.transformedShape()); auto chull = sl::convexHull(mp); double miss = Placer::overfit(chull, bin); @@ -427,7 +427,7 @@ public: return score; }; - pck_.configure(pconf_); + m_pck.configure(m_pconf); } }; @@ -439,25 +439,25 @@ public: std::function stopcond): _ArrBase(bin, dist, progressind, stopcond) { - pconf_.object_function = [this, &bin] (const Item &item) { + m_pconf.object_function = [this, &bin] (const Item &item) { auto binbb = sl::boundingBox(bin); auto result = objfunc(binbb.center(), - merged_pile_, - pilebb_, - items_, + m_merged_pile, + m_pilebb, + m_items, item, - bin_area_, - norm_, - rtree_, - smallsrtree_, - remaining_); + m_bin_area, + m_norm, + m_rtree, + m_smallsrtree, + m_remaining); double score = std::get<0>(result); return score; }; - pck_.configure(pconf_); + m_pck.configure(m_pconf); } }; @@ -469,22 +469,22 @@ public: std::function stopcond): _ArrBase(Box(0, 0), dist, progressind, stopcond) { - this->pconf_.object_function = [this] (const Item &item) { + this->m_pconf.object_function = [this] (const Item &item) { auto result = objfunc({0, 0}, - merged_pile_, - pilebb_, - items_, + m_merged_pile, + m_pilebb, + m_items, item, 0, - norm_, - rtree_, - smallsrtree_, - remaining_); + m_norm, + m_rtree, + m_smallsrtree, + m_remaining); return std::get<0>(result); }; - this->pck_.configure(pconf_); + this->m_pck.configure(m_pconf); } }; @@ -527,14 +527,19 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { // Invalid geometries would throw exceptions when arranging if(item.vertexCount() > 3) { - item.rotation(objinst->rotation); - item.translation( { -#if ENABLE_MODELINSTANCE_3D_OFFSET - ClipperLib::cInt(objinst->get_offset(X) / SCALING_FACTOR), - ClipperLib::cInt(objinst->get_offset(Y) / SCALING_FACTOR) +#if ENABLE_MODELINSTANCE_3D_ROTATION + // CHECK_ME -> is the following correct or it should take in account all three rotations ? + item.rotation(objinst->get_rotation(Z)); #else - ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR), - ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR) + item.rotation(objinst->rotation); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + item.translation({ +#if ENABLE_MODELINSTANCE_3D_OFFSET + ClipperLib::cInt(objinst->get_offset(X)/SCALING_FACTOR), + ClipperLib::cInt(objinst->get_offset(Y)/SCALING_FACTOR) +#else + ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR), + ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR) #endif // ENABLE_MODELINSTANCE_3D_OFFSET }); ret.emplace_back(objinst, item); @@ -668,18 +673,25 @@ void applyResult( // Get the model instance from the shapemap using the index ModelInstance *inst_ptr = shapemap[idx].first; - // Get the tranformation data from the item object and scale it + // Get the transformation data from the item object and scale it // appropriately auto off = item.translation(); Radians rot = item.rotation(); #if ENABLE_MODELINSTANCE_3D_OFFSET - Vec3d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR, 0.0); + Vec3d foff(off.X*SCALING_FACTOR + batch_offset, + off.Y*SCALING_FACTOR, + 0.0); #else Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR); #endif // ENABLE_MODELINSTANCE_3D_OFFSET - // write the tranformation data into the model instance + // write the transformation data into the model instance +#if ENABLE_MODELINSTANCE_3D_ROTATION + // CHECK_ME -> Is the following correct ? + inst_ptr->set_rotation(Vec3d(0.0, 0.0, rot)); +#else inst_ptr->rotation = rot; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #if ENABLE_MODELINSTANCE_3D_OFFSET inst_ptr->set_offset(foff); #else @@ -695,7 +707,7 @@ void applyResult( * The arrangement considers multiple bins (aka. print beds) for placing all * the items provided in the model argument. If the items don't fit on one * print bed, the remaining will be placed onto newly created print beds. - * The first_bin_only parameter, if set to true, disables this behaviour and + * The first_bin_only parameter, if set to true, disables this behavior and * makes sure that only one print bed is filled and the remaining items will be * untouched. When set to false, the items which could not fit onto the * print bed will be placed next to the print bed so the user should see a @@ -741,6 +753,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, IndexedPackGroup result; + // If there is no hint about the shape, we will try to guess if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed); BoundingBox bbb(bed); diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp index c3c59fc30..917a39e08 100644 --- a/src/libslic3r/PrintExport.hpp +++ b/src/libslic3r/PrintExport.hpp @@ -32,10 +32,10 @@ template class FilePrinter { public: - void printConfig(const Print&); + void print_config(const Print&); // Draw an ExPolygon which is a polygon inside a slice on the specified layer. - void drawPolygon(const ExPolygon& p, unsigned lyr); + void draw_polygon(const ExPolygon& p, unsigned lyr); // Tell the printer how many layers should it consider. void layers(unsigned layernum); @@ -47,45 +47,47 @@ public: * specified layer number than an appropriate number of layers will be * allocated in the printer. */ - void beginLayer(unsigned layer); + void begin_layer(unsigned layer); // Allocate a new layer on top of the last and switch to it. - void beginLayer(); + void begin_layer(); /* * Finish the selected layer. It means that no drawing is allowed on that * layer anymore. This fact can be used to prepare the file system output * data like png comprimation and so on. */ - void finishLayer(unsigned layer); + void finish_layer(unsigned layer); // Finish the top layer. - void finishLayer(); + void finish_layer(); // Save all the layers into the file (or dir) specified in the path argument void save(const std::string& path); // Save only the selected layer to the file specified in path argument. - void saveLayer(unsigned lyr, const std::string& path); + void save_layer(unsigned lyr, const std::string& path); }; template struct VeryFalse { static const bool value = false; }; // This has to be explicitly implemented in the gui layer or a default zlib // based implementation is needed. -template class Zipper { +template class LayerWriter { public: - Zipper(const std::string& /*zipfile_path*/) { + LayerWriter(const std::string& /*zipfile_path*/) { static_assert(VeryFalse::value, - "No zipper implementation provided!"); + "No layer writer implementation provided!"); } void next_entry(const std::string& /*fname*/) {} std::string get_name() { return ""; } - template Zipper& operator<<(const T& /*arg*/) { + bool is_ok() { return false; } + + template LayerWriter& operator<<(const T& /*arg*/) { return *this; } @@ -110,22 +112,22 @@ template class FilePrinter { // We will save the compressed PNG data into stringstreams which can be done // in parallel. Later we can write every layer to the disk sequentially. - std::vector layers_rst_; - Raster::Resolution res_; - Raster::PixelDim pxdim_; - const Print *print_ = nullptr; - double exp_time_s_ = .0, exp_time_first_s_ = .0; + std::vector m_layers_rst; + Raster::Resolution m_res; + Raster::PixelDim m_pxdim; + const Print *m_print = nullptr; + double m_exp_time_s = .0, m_exp_time_first_s = .0; std::string createIniContent(const std::string& projectname) { - double layer_height = print_? - print_->default_object_config().layer_height.getFloat() : + double layer_height = m_print? + m_print->default_object_config().layer_height.getFloat() : 0.05; using std::string; using std::to_string; - auto expt_str = to_string(exp_time_s_); - auto expt_first_str = to_string(exp_time_first_s_); + auto expt_str = to_string(m_exp_time_s); + auto expt_first_str = to_string(m_exp_time_first_s); auto stepnum_str = to_string(static_cast(800*layer_height)); auto layerh_str = to_string(layer_height); @@ -153,117 +155,84 @@ public: inline FilePrinter(double width_mm, double height_mm, unsigned width_px, unsigned height_px, double exp_time, double exp_time_first): - res_(width_px, height_px), - pxdim_(width_mm/width_px, height_mm/height_px), - exp_time_s_(exp_time), - exp_time_first_s_(exp_time_first) + m_res(width_px, height_px), + m_pxdim(width_mm/width_px, height_mm/height_px), + m_exp_time_s(exp_time), + m_exp_time_first_s(exp_time_first) { } FilePrinter(const FilePrinter& ) = delete; FilePrinter(FilePrinter&& m): - layers_rst_(std::move(m.layers_rst_)), - res_(m.res_), - pxdim_(m.pxdim_) {} + m_layers_rst(std::move(m.m_layers_rst)), + m_res(m.m_res), + m_pxdim(m.m_pxdim) {} - inline void layers(unsigned cnt) { if(cnt > 0) layers_rst_.resize(cnt); } - inline unsigned layers() const { return unsigned(layers_rst_.size()); } + inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } + inline unsigned layers() const { return unsigned(m_layers_rst.size()); } - void printConfig(const Print& printconf) { print_ = &printconf; } + void print_config(const Print& printconf) { m_print = &printconf; } - inline void drawPolygon(const ExPolygon& p, unsigned lyr) { - assert(lyr < layers_rst_.size()); - layers_rst_[lyr].first.draw(p); + inline void draw_polygon(const ExPolygon& p, unsigned lyr) { + assert(lyr < m_layers_rst.size()); + m_layers_rst[lyr].first.draw(p); } - inline void beginLayer(unsigned lyr) { - if(layers_rst_.size() <= lyr) layers_rst_.resize(lyr+1); - layers_rst_[lyr].first.reset(res_, pxdim_, ORIGIN); + inline void begin_layer(unsigned lyr) { + if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1); + m_layers_rst[lyr].first.reset(m_res, m_pxdim, ORIGIN); } - inline void beginLayer() { - layers_rst_.emplace_back(); - layers_rst_.front().first.reset(res_, pxdim_, ORIGIN); + inline void begin_layer() { + m_layers_rst.emplace_back(); + m_layers_rst.front().first.reset(m_res, m_pxdim, ORIGIN); } - inline void finishLayer(unsigned lyr_id) { - assert(lyr_id < layers_rst_.size()); - layers_rst_[lyr_id].first.save(layers_rst_[lyr_id].second, + inline void finish_layer(unsigned lyr_id) { + assert(lyr_id < m_layers_rst.size()); + m_layers_rst[lyr_id].first.save(m_layers_rst[lyr_id].second, Raster::Compression::PNG); - layers_rst_[lyr_id].first.reset(); + m_layers_rst[lyr_id].first.reset(); } - inline void finishLayer() { - if(!layers_rst_.empty()) { - layers_rst_.back().first.save(layers_rst_.back().second, + inline void finish_layer() { + if(!m_layers_rst.empty()) { + m_layers_rst.back().first.save(m_layers_rst.back().second, Raster::Compression::PNG); - layers_rst_.back().first.reset(); + m_layers_rst.back().first.reset(); } } inline void save(const std::string& path) { try { - Zipper zipper(path); + LayerWriter writer(path); - std::string project = zipper.get_name(); + std::string project = writer.get_name(); - zipper.next_entry(project); - zipper << createIniContent(project); + writer.next_entry("config.ini"); + writer << createIniContent(project); - for(unsigned i = 0; i < layers_rst_.size(); i++) { - if(layers_rst_[i].second.rdbuf()->in_avail() > 0) { + for(unsigned i = 0; i < m_layers_rst.size(); i++) { + if(m_layers_rst[i].second.rdbuf()->in_avail() > 0) { char lyrnum[6]; std::sprintf(lyrnum, "%.5d", i); auto zfilename = project + lyrnum + ".png"; - zipper.next_entry(zfilename); - zipper << layers_rst_[i].second.rdbuf(); - layers_rst_[i].second.str(""); + writer.next_entry(zfilename); + writer << m_layers_rst[i].second.rdbuf(); + m_layers_rst[i].second.str(""); } } - zipper.close(); - } catch(std::exception&) { - BOOST_LOG_TRIVIAL(error) << "Can't create zip file for layers! " - << path; + writer.close(); + } catch(std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); return; } - -// wxFileName filepath(path); - -// wxFFileOutputStream zipfile(path); - -// std::string project = filepath.GetName().ToStdString(); - -// if(!zipfile.IsOk()) { -// BOOST_LOG_TRIVIAL(error) << "Can't create zip file for layers! " -// << path; -// return; -// } - -// wxZipOutputStream zipstream(zipfile); -// wxStdOutputStream pngstream(zipstream); - -// zipstream.PutNextEntry("config.ini"); -// pngstream << createIniContent(project); - -// for(unsigned i = 0; i < layers_rst_.size(); i++) { -// if(layers_rst_[i].second.rdbuf()->in_avail() > 0) { -// char lyrnum[6]; -// std::sprintf(lyrnum, "%.5d", i); -// auto zfilename = project + lyrnum + ".png"; -// zipstream.PutNextEntry(zfilename); -// pngstream << layers_rst_[i].second.rdbuf(); -// layers_rst_[i].second.str(""); -// } -// } - -// zipstream.Close(); -// zipfile.Close(); } - void saveLayer(unsigned lyr, const std::string& path) { + void save_layer(unsigned lyr, const std::string& path) { unsigned i = lyr; - assert(i < layers_rst_.size()); + assert(i < m_layers_rst.size()); char lyrnum[6]; std::sprintf(lyrnum, "%.5d", lyr); @@ -271,19 +240,19 @@ public: std::fstream out(loc, std::fstream::out | std::fstream::binary); if(out.good()) { - layers_rst_[i].first.save(out, Raster::Compression::PNG); + m_layers_rst[i].first.save(out, Raster::Compression::PNG); } else { BOOST_LOG_TRIVIAL(error) << "Can't create file for layer"; } out.close(); - layers_rst_[i].first.reset(); + m_layers_rst[i].first.reset(); } }; // Let's shadow this eigen interface -inline coord_t px(const Point& p) { return p(0); } -inline coord_t py(const Point& p) { return p(1); } +inline coord_t px(const Point& p) { return p(0); } +inline coord_t py(const Point& p) { return p(1); } inline coordf_t px(const Vec2d& p) { return p(0); } inline coordf_t py(const Vec2d& p) { return p(1); } @@ -337,7 +306,7 @@ void print_to(Print& print, FilePrinter printer(width_mm, height_mm, std::forward(args)...); - printer.printConfig(print); + printer.print_config(print); printer.layers(layers.size()); // Allocate space for all the layers @@ -358,7 +327,7 @@ void print_to(Print& print, { LayerPtrs lrange = layers[keys[layer_id]]; - printer.beginLayer(layer_id); // Switch to the appropriate layer + printer.begin_layer(layer_id); // Switch to the appropriate layer for(Layer *lp : lrange) { Layer& l = *lp; @@ -379,7 +348,7 @@ void print_to(Print& print, slice.translate(-px(print_bb.min) + cx, -py(print_bb.min) + cy); - printer.drawPolygon(slice, layer_id); + printer.draw_polygon(slice, layer_id); } /*if(print.has_support_material() && layer_id > 0) { @@ -392,7 +361,7 @@ void print_to(Print& print, } - printer.finishLayer(layer_id); // Finish the layer for later saving it. + printer.finish_layer(layer_id); // Finish the layer for later saving it. auto st = static_cast(layer_id*80.0/layers.size()); m.lock(); diff --git a/src/libslic3r/Rasterizer/Rasterizer.cpp b/src/libslic3r/Rasterizer/Rasterizer.cpp index b0bf04343..3ff3e0949 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.cpp +++ b/src/libslic3r/Rasterizer/Rasterizer.cpp @@ -37,37 +37,37 @@ public: using Origin = Raster::Origin; private: - Raster::Resolution resolution_; - Raster::PixelDim pxdim_; - TBuffer buf_; - TRawBuffer rbuf_; - TPixelRenderer pixfmt_; - TRawRenderer raw_renderer_; - TRendererAA renderer_; - Origin o_; - std::function flipy_ = [](agg::path_storage&) {}; + Raster::Resolution m_resolution; + Raster::PixelDim m_pxdim; + TBuffer m_buf; + TRawBuffer m_rbuf; + TPixelRenderer m_pixfmt; + TRawRenderer m_raw_renderer; + TRendererAA m_renderer; + Origin m_o; + std::function m_flipy = [](agg::path_storage&) {}; public: inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd, Origin o): - resolution_(res), pxdim_(pd), - buf_(res.pixels()), - rbuf_(reinterpret_cast(buf_.data()), + m_resolution(res), m_pxdim(pd), + m_buf(res.pixels()), + m_rbuf(reinterpret_cast(m_buf.data()), res.width_px, res.height_px, res.width_px*TPixelRenderer::num_components), - pixfmt_(rbuf_), - raw_renderer_(pixfmt_), - renderer_(raw_renderer_), - o_(o) + m_pixfmt(m_rbuf), + m_raw_renderer(m_pixfmt), + m_renderer(m_raw_renderer), + m_o(o) { - renderer_.color(ColorWhite); + m_renderer.color(ColorWhite); // If we would like to play around with gamma // ras.gamma(agg::gamma_power(1.0)); clear(); - if(o_ == Origin::TOP_LEFT) flipy_ = [this](agg::path_storage& path) { - path.flip_y(0, resolution_.height_px); + if(m_o == Origin::TOP_LEFT) m_flipy = [this](agg::path_storage& path) { + path.flip_y(0, m_resolution.height_px); }; } @@ -76,35 +76,35 @@ public: agg::scanline_p8 scanlines; auto&& path = to_path(poly.contour); - flipy_(path); + m_flipy(path); ras.add_path(path); for(auto h : poly.holes) { auto&& holepath = to_path(h); - flipy_(holepath); + m_flipy(holepath); ras.add_path(holepath); } - agg::render_scanlines(ras, scanlines, renderer_); + agg::render_scanlines(ras, scanlines, m_renderer); } inline void clear() { - raw_renderer_.clear(ColorBlack); + m_raw_renderer.clear(ColorBlack); } - inline TBuffer& buffer() { return buf_; } + inline TBuffer& buffer() { return m_buf; } - inline const Raster::Resolution resolution() { return resolution_; } + inline const Raster::Resolution resolution() { return m_resolution; } - inline Origin origin() const /*noexcept*/ { return o_; } + inline Origin origin() const /*noexcept*/ { return m_o; } private: double getPx(const Point& p) { - return p(0) * SCALING_FACTOR/pxdim_.w_mm; + return p(0) * SCALING_FACTOR/m_pxdim.w_mm; } double getPy(const Point& p) { - return p(1) * SCALING_FACTOR/pxdim_.h_mm; + return p(1) * SCALING_FACTOR/m_pxdim.h_mm; } agg::path_storage to_path(const Polygon& poly) { @@ -124,57 +124,57 @@ const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255); const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0); Raster::Raster(const Resolution &r, const PixelDim &pd, Origin o): - impl_(new Impl(r, pd, o)) {} + m_impl(new Impl(r, pd, o)) {} Raster::Raster() {} Raster::~Raster() {} Raster::Raster(Raster &&m): - impl_(std::move(m.impl_)) {} + m_impl(std::move(m.m_impl)) {} void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd) { // Free up the unnecessary memory and make sure it stays clear after // an exception - auto o = impl_? impl_->origin() : Origin::TOP_LEFT; + auto o = m_impl? m_impl->origin() : Origin::TOP_LEFT; reset(r, pd, o); } void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, Raster::Origin o) { - impl_.reset(); - impl_.reset(new Impl(r, pd, o)); + m_impl.reset(); + m_impl.reset(new Impl(r, pd, o)); } void Raster::reset() { - impl_.reset(); + m_impl.reset(); } Raster::Resolution Raster::resolution() const { - if(impl_) return impl_->resolution(); + if(m_impl) return m_impl->resolution(); return Resolution(0, 0); } void Raster::clear() { - assert(impl_); - impl_->clear(); + assert(m_impl); + m_impl->clear(); } void Raster::draw(const ExPolygon &poly) { - assert(impl_); - impl_->draw(poly); + assert(m_impl); + m_impl->draw(poly); } void Raster::save(std::ostream& stream, Compression comp) { - assert(impl_); + assert(m_impl); switch(comp) { case Compression::PNG: { @@ -188,7 +188,7 @@ void Raster::save(std::ostream& stream, Compression comp) wr.write_info(); - auto& b = impl_->buffer(); + auto& b = m_impl->buffer(); auto ptr = reinterpret_cast( b.data() ); unsigned stride = sizeof(Impl::TBuffer::value_type) * resolution().width_px; @@ -201,12 +201,12 @@ void Raster::save(std::ostream& stream, Compression comp) } case Compression::RAW: { stream << "P5 " - << impl_->resolution().width_px << " " - << impl_->resolution().height_px << " " + << m_impl->resolution().width_px << " " + << m_impl->resolution().height_px << " " << "255 "; - stream.write(reinterpret_cast(impl_->buffer().data()), - impl_->buffer().size()*sizeof(Impl::TBuffer::value_type)); + stream.write(reinterpret_cast(m_impl->buffer().data()), + m_impl->buffer().size()*sizeof(Impl::TBuffer::value_type)); } } } diff --git a/src/libslic3r/Rasterizer/Rasterizer.hpp b/src/libslic3r/Rasterizer/Rasterizer.hpp index cbb39bc6b..b6406b770 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.hpp +++ b/src/libslic3r/Rasterizer/Rasterizer.hpp @@ -18,7 +18,7 @@ class ExPolygon; */ class Raster { class Impl; - std::unique_ptr impl_; + std::unique_ptr m_impl; public: /// Supported compression types @@ -65,7 +65,7 @@ public: /** * Release the allocated resources. Drawing in this state ends in - * unspecified behaviour. + * unspecified behavior. */ void reset(); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 5c4f8617d..25e6b85e7 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -6,6 +6,10 @@ // Add z coordinate to model instances' offset #define ENABLE_MODELINSTANCE_3D_OFFSET (1 && ENABLE_1_42_0) +// Add double click on gizmo grabbers to reset transformation components to their default value +#define ENABLE_GIZMOS_RESET (1 && ENABLE_1_42_0) +// Add x and y rotation components to model instances' offset +#define ENABLE_MODELINSTANCE_3D_ROTATION (1 && ENABLE_MODELINSTANCE_3D_OFFSET) #endif // _technologies_h_ diff --git a/src/slic3r/AppController.cpp b/src/slic3r/AppController.cpp index 6728c6c00..2bb79cec1 100644 --- a/src/slic3r/AppController.cpp +++ b/src/slic3r/AppController.cpp @@ -18,13 +18,10 @@ #include #include -#include -#include -#include namespace Slic3r { -class AppControllerBoilerplate::PriData { +class AppControllerGui::PriData { public: std::mutex m; std::thread::id ui_thread; @@ -32,16 +29,16 @@ public: inline explicit PriData(std::thread::id uit): ui_thread(uit) {} }; -AppControllerBoilerplate::AppControllerBoilerplate() - :pri_data_(new PriData(std::this_thread::get_id())) {} +AppControllerGui::AppControllerGui() + :m_pri_data(new PriData(std::this_thread::get_id())) {} -AppControllerBoilerplate::~AppControllerBoilerplate() { - pri_data_.reset(); +AppControllerGui::~AppControllerGui() { + m_pri_data.reset(); } -bool AppControllerBoilerplate::is_main_thread() const +bool AppControllerGui::is_main_thread() const { - return pri_data_->ui_thread == std::this_thread::get_id(); + return m_pri_data->ui_thread == std::this_thread::get_id(); } namespace GUI { @@ -57,172 +54,32 @@ static const PrintStep STEP_SKIRT = psSkirt; static const PrintStep STEP_BRIM = psBrim; static const PrintStep STEP_WIPE_TOWER = psWipeTower; -AppControllerBoilerplate::ProgresIndicatorPtr -AppControllerBoilerplate::global_progress_indicator() { +ProgresIndicatorPtr AppControllerGui::global_progress_indicator() { ProgresIndicatorPtr ret; - pri_data_->m.lock(); - ret = global_progressind_; - pri_data_->m.unlock(); + m_pri_data->m.lock(); + ret = m_global_progressind; + m_pri_data->m.unlock(); return ret; } -void AppControllerBoilerplate::global_progress_indicator( - AppControllerBoilerplate::ProgresIndicatorPtr gpri) +void AppControllerGui::global_progress_indicator(ProgresIndicatorPtr gpri) { - pri_data_->m.lock(); - global_progressind_ = gpri; - pri_data_->m.unlock(); + m_pri_data->m.lock(); + m_global_progressind = gpri; + m_pri_data->m.unlock(); } -//void PrintController::make_skirt() -//{ -// assert(print_ != nullptr); - -// // prerequisites -// for(auto obj : print_->objects) make_perimeters(obj); -// for(auto obj : print_->objects) infill(obj); -// for(auto obj : print_->objects) gen_support_material(obj); - -// if(!print_->state.is_done(STEP_SKIRT)) { -// print_->state.set_started(STEP_SKIRT); -// print_->skirt.clear(); -// if(print_->has_skirt()) print_->_make_skirt(); - -// print_->state.set_done(STEP_SKIRT); -// } -//} - -//void PrintController::make_brim() -//{ -// assert(print_ != nullptr); - -// // prerequisites -// for(auto obj : print_->objects) make_perimeters(obj); -// for(auto obj : print_->objects) infill(obj); -// for(auto obj : print_->objects) gen_support_material(obj); -// make_skirt(); - -// if(!print_->state.is_done(STEP_BRIM)) { -// print_->state.set_started(STEP_BRIM); - -// // since this method must be idempotent, we clear brim paths *before* -// // checking whether we need to generate them -// print_->brim.clear(); - -// if(print_->config.brim_width > 0) print_->_make_brim(); - -// print_->state.set_done(STEP_BRIM); -// } -//} - -//void PrintController::make_wipe_tower() -//{ -// assert(print_ != nullptr); - -// // prerequisites -// for(auto obj : print_->objects) make_perimeters(obj); -// for(auto obj : print_->objects) infill(obj); -// for(auto obj : print_->objects) gen_support_material(obj); -// make_skirt(); -// make_brim(); - -// if(!print_->state.is_done(STEP_WIPE_TOWER)) { -// print_->state.set_started(STEP_WIPE_TOWER); - -// // since this method must be idempotent, we clear brim paths *before* -// // checking whether we need to generate them -// print_->brim.clear(); - -// if(print_->has_wipe_tower()) print_->_make_wipe_tower(); - -// print_->state.set_done(STEP_WIPE_TOWER); -// } -//} - -//void PrintController::slice(PrintObject *pobj) -//{ -// assert(pobj != nullptr && print_ != nullptr); - -// if(pobj->state.is_done(STEP_SLICE)) return; - -// pobj->state.set_started(STEP_SLICE); - -// pobj->_slice(); - -// auto msg = pobj->_fix_slicing_errors(); -// if(!msg.empty()) report_issue(IssueType::WARN, msg); - -// // simplify slices if required -// if (print_->config.resolution) -// pobj->_simplify_slices(scale_(print_->config.resolution)); - - -// if(pobj->layers.empty()) -// report_issue(IssueType::ERR, -// L("No layers were detected. You might want to repair your " -// "STL file(s) or check their size or thickness and retry") -// ); - -// pobj->state.set_done(STEP_SLICE); -//} - -//void PrintController::make_perimeters(PrintObject *pobj) -//{ -// assert(pobj != nullptr); - -// slice(pobj); - -// if (!pobj->state.is_done(STEP_PERIMETERS)) { -// pobj->_make_perimeters(); -// } -//} - -//void PrintController::infill(PrintObject *pobj) -//{ -// assert(pobj != nullptr); - -// make_perimeters(pobj); - -// if (!pobj->state.is_done(STEP_PREPARE_INFILL)) { -// pobj->state.set_started(STEP_PREPARE_INFILL); - -// pobj->_prepare_infill(); - -// pobj->state.set_done(STEP_PREPARE_INFILL); -// } - -// pobj->_infill(); -//} - -//void PrintController::gen_support_material(PrintObject *pobj) -//{ -// assert(pobj != nullptr); - -// // prerequisites -// slice(pobj); - -// if(!pobj->state.is_done(STEP_SUPPORTMATERIAL)) { -// pobj->state.set_started(STEP_SUPPORTMATERIAL); - -// pobj->clear_support_layers(); - -// if((pobj->config.support_material || pobj->config.raft_layers > 0) -// && pobj->layers.size() > 1) { -// pobj->_generate_support_material(); -// } - -// pobj->state.set_done(STEP_SUPPORTMATERIAL); -// } -//} - PrintController::PngExportData PrintController::query_png_export_data(const DynamicPrintConfig& conf) { PngExportData ret; - auto zippath = query_destination_path("Output zip file", "*.zip", "out"); + auto c = GUI::get_appctl(); + auto zippath = c->query_destination_path("Output zip file", "*.zip", + "export-png", + "out"); ret.zippath = zippath; @@ -246,98 +103,53 @@ PrintController::query_png_export_data(const DynamicPrintConfig& conf) return ret; } -void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri) +void PrintController::slice(ProgresIndicatorPtr pri) { - auto st = pri->state(); + m_print->set_status_callback([pri](int st, const std::string& msg){ + pri->update(unsigned(st), msg); + }); - Slic3r::trace(3, "Starting the slicing process."); - - pri->update(st+20, L("Generating perimeters")); -// for(auto obj : print_->objects) make_perimeters(obj); - - pri->update(st+60, L("Infilling layers")); -// for(auto obj : print_->objects) infill(obj); - - pri->update(st+70, L("Generating support material")); -// for(auto obj : print_->objects) gen_support_material(obj); - -// pri->message_fmt(L("Weight: %.1fg, Cost: %.1f"), -// print_->total_weight, print_->total_cost); - pri->state(st+85); - - - pri->update(st+88, L("Generating skirt")); - make_skirt(); - - - pri->update(st+90, L("Generating brim")); - make_brim(); - - pri->update(st+95, L("Generating wipe tower")); - make_wipe_tower(); - - pri->update(st+100, L("Done")); - - // time to make some statistics.. - - Slic3r::trace(3, L("Slicing process finished.")); + m_print->process(); } void PrintController::slice() { - auto pri = global_progress_indicator(); - if(!pri) pri = create_progress_indicator(100, L("Slicing")); + auto ctl = GUI::get_appctl(); + auto pri = ctl->global_progress_indicator(); + if(!pri) pri = ctl->create_progress_indicator(100, L("Slicing")); slice(pri); } -struct wxZipper {}; - -template<> class Zipper { - wxFileName m_fpath; - wxFFileOutputStream m_zipfile; - wxZipOutputStream m_zipstream; - wxStdOutputStream m_pngstream; +template<> class LayerWriter { + Zipper m_zip; public: - Zipper(const std::string& zipfile_path): - m_fpath(zipfile_path), - m_zipfile(zipfile_path), - m_zipstream(m_zipfile), - m_pngstream(m_zipstream) - { - if(!m_zipfile.IsOk()) - throw std::runtime_error(L("Cannot create zip file.")); + inline LayerWriter(const std::string& zipfile_path): m_zip(zipfile_path) {} + + inline void next_entry(const std::string& fname) { m_zip.next_entry(fname); } + + inline std::string get_name() const { return m_zip.get_name(); } + + template inline LayerWriter& operator<<(const T& arg) { + m_zip.stream() << arg; return *this; } - void next_entry(const std::string& fname) { - m_zipstream.PutNextEntry(fname); - } - - std::string get_name() { - return m_fpath.GetName().ToStdString(); - } - - template Zipper& operator<<(const T& arg) { - m_pngstream << arg; return *this; - } - - void close() { - m_zipstream.Close(); - m_zipfile.Close(); - } + inline void close() { m_zip.close(); } }; void PrintController::slice_to_png() { using Pointf3 = Vec3d; + auto ctl = GUI::get_appctl(); auto presetbundle = GUI::get_preset_bundle(); assert(presetbundle); + // FIXME: this crashes in command line mode auto pt = presetbundle->printers.get_selected_preset().printer_technology(); if(pt != ptSLA) { - report_issue(IssueType::ERR, L("Printer technology is not SLA!"), + ctl->report_issue(IssueType::ERR, L("Printer technology is not SLA!"), L("Error")); return; } @@ -348,13 +160,13 @@ void PrintController::slice_to_png() auto exd = query_png_export_data(conf); if(exd.zippath.empty()) return; - Print *print = print_; + Print *print = m_print; try { print->apply_config(conf); print->validate(); } catch(std::exception& e) { - report_issue(IssueType::ERR, e.what(), "Error"); + ctl->report_issue(IssueType::ERR, e.what(), "Error"); return; } @@ -400,13 +212,13 @@ void PrintController::slice_to_png() << L("Width needed: ") << px(punsc) << " mm\n" << L("Height needed: ") << py(punsc) << " mm\n"; - if(!report_issue(IssueType::WARN_Q, ss.str(), L("Warning"))) { + if(!ctl->report_issue(IssueType::WARN_Q, ss.str(), L("Warning"))) { scale_back(); return; } } - auto pri = create_progress_indicator( + auto pri = ctl->create_progress_indicator( 200, L("Slicing to zipped png files...")); pri->on_cancel([&print](){ print->cancel(); }); @@ -415,7 +227,7 @@ void PrintController::slice_to_png() pri->update(0, L("Slicing...")); slice(pri); } catch (std::exception& e) { - report_issue(IssueType::ERR, e.what(), L("Exception occurred")); + ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred")); scale_back(); if(print->canceled()) print->restart(); return; @@ -428,22 +240,23 @@ void PrintController::slice_to_png() }); try { - print_to( *print, exd.zippath, + print_to( *print, exd.zippath, exd.width_mm, exd.height_mm, exd.width_px, exd.height_px, exd.exp_time_s, exd.exp_time_first_s); } catch (std::exception& e) { - report_issue(IssueType::ERR, e.what(), L("Exception occurred")); + ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred")); } scale_back(); if(print->canceled()) print->restart(); + print->set_status_default(); } const PrintConfig &PrintController::config() const { - return print_->config(); + return m_print->config(); } void ProgressIndicator::message_fmt( @@ -477,15 +290,17 @@ void AppController::arrange_model() { using Coord = libnest2d::TCoord; - if(arranging_.load()) return; + auto ctl = GUI::get_appctl(); + + if(m_arranging.load()) return; // to prevent UI reentrancies - arranging_.store(true); + m_arranging.store(true); unsigned count = 0; - for(auto obj : model_->objects) count += obj->instances.size(); + for(auto obj : m_model->objects) count += obj->instances.size(); - auto pind = global_progress_indicator(); + auto pind = ctl->global_progress_indicator(); float pmax = 1.0; @@ -496,7 +311,7 @@ void AppController::arrange_model() pind->max(count); pind->on_cancel([this](){ - arranging_.store(false); + m_arranging.store(false); }); } @@ -517,20 +332,20 @@ void AppController::arrange_model() // TODO: from Sasha from GUI hint.type = arr::BedShapeType::WHO_KNOWS; - arr::arrange(*model_, + arr::arrange(*m_model, min_obj_distance, bed, hint, false, // create many piles not just one pile - [this, pind, count](unsigned rem) { + [this, pind, &ctl, count](unsigned rem) { if(pind) pind->update(count - rem, L("Arranging objects...")); - process_events(); - }, [this] () { return !arranging_.load(); }); + ctl->process_events(); + }, [this] () { return !m_arranging.load(); }); } catch(std::exception& e) { std::cerr << e.what() << std::endl; - report_issue(IssueType::ERR, + ctl->report_issue(IssueType::ERR, L("Could not arrange model objects! " "Some geometries may be invalid."), L("Exception occurred")); @@ -539,13 +354,13 @@ void AppController::arrange_model() // Restore previous max value if(pind) { pind->max(pmax); - pind->update(0, arranging_.load() ? L("Arranging done.") : + pind->update(0, m_arranging.load() ? L("Arranging done.") : L("Arranging canceled.")); pind->on_cancel(/*remove cancel function*/); } - arranging_.store(false); + m_arranging.store(false); } } diff --git a/src/slic3r/AppController.hpp b/src/slic3r/AppController.hpp index 88d1f0eca..a34e5d035 100644 --- a/src/slic3r/AppController.hpp +++ b/src/slic3r/AppController.hpp @@ -20,6 +20,21 @@ class PrintConfig; class ProgressStatusBar; class DynamicPrintConfig; +/// A Progress indicator object smart pointer +using ProgresIndicatorPtr = std::shared_ptr; + +using FilePath = std::string; +using FilePathList = std::vector; + +/// Common runtime issue types +enum class IssueType { + INFO, + WARN, + WARN_Q, // Warning with a question to continue + ERR, + FATAL +}; + /** * @brief A boilerplate class for creating application logic. It should provide * features as issue reporting and progress indication, etc... @@ -32,34 +47,12 @@ class DynamicPrintConfig; * UI toolkit dependencies. We can implement it with any UI framework or make it * a cli client. */ -class AppControllerBoilerplate { +class AppControllerBase { public: - /// A Progress indicator object smart pointer - using ProgresIndicatorPtr = std::shared_ptr; + using Ptr = std::shared_ptr; -private: - class PriData; // Some structure to store progress indication data - - // Pimpl data for thread safe progress indication features - std::unique_ptr pri_data_; - -public: - - AppControllerBoilerplate(); - ~AppControllerBoilerplate(); - - using Path = std::string; - using PathList = std::vector; - - /// Common runtime issue types - enum class IssueType { - INFO, - WARN, - WARN_Q, // Warning with a question to continue - ERR, - FATAL - }; + inline virtual ~AppControllerBase() {} /** * @brief Query some paths from the user. @@ -67,25 +60,30 @@ public: * It should display a file chooser dialog in case of a UI application. * @param title Title of a possible query dialog. * @param extensions Recognized file extensions. - * @return Returns a list of paths choosed by the user. + * @return Returns a list of paths chosen by the user. */ - PathList query_destination_paths( + virtual FilePathList query_destination_paths( const std::string& title, - const std::string& extensions) const; + const std::string& extensions, + const std::string& functionid = "", + const std::string& hint = "") const = 0; /** * @brief Same as query_destination_paths but works for directories only. */ - PathList query_destination_dirs( - const std::string& title) const; + virtual FilePathList query_destination_dirs( + const std::string& title, + const std::string& functionid = "", + const std::string& hint = "") const = 0; /** * @brief Same as query_destination_paths but returns only one path. */ - Path query_destination_path( + virtual FilePath query_destination_path( const std::string& title, const std::string& extensions, - const std::string& hint = "") const; + const std::string& functionid = "", + const std::string& hint = "") const = 0; /** * @brief Report an issue to the user be it fatal or recoverable. @@ -97,12 +95,9 @@ public: * @param brief A very brief description. Can be used for message dialog * title. */ - bool report_issue(IssueType issuetype, - const std::string& description, - const std::string& brief); - - bool report_issue(IssueType issuetype, - const std::string& description); + virtual bool report_issue(IssueType issuetype, + const std::string& description, + const std::string& brief) = 0; /** * @brief Return the global progress indicator for the current controller. @@ -110,9 +105,9 @@ public: * * Only one thread should use the global indicator at a time. */ - ProgresIndicatorPtr global_progress_indicator(); + virtual ProgresIndicatorPtr global_progress_indicator() = 0; - void global_progress_indicator(ProgresIndicatorPtr gpri); + virtual void global_progress_indicator(ProgresIndicatorPtr gpri) = 0; /** * @brief A predicate telling the caller whether it is the thread that @@ -122,7 +117,7 @@ public: * @return Return true for the same caller thread that created this * object and false for every other. */ - bool is_main_thread() const; + virtual bool is_main_thread() const = 0; /** * @brief The frontend supports asynch execution. @@ -138,11 +133,9 @@ public: * @return true if a job or method can be executed asynchronously, false * otherwise. */ - bool supports_asynch() const; + virtual bool supports_asynch() const = 0; - void process_events(); - -protected: + virtual void process_events() = 0; /** * @brief Create a new progress indicator and return a smart pointer to it. @@ -151,36 +144,169 @@ protected: * @param firstmsg The message for the first subtask to be displayed. * @return Smart pointer to the created object. */ - ProgresIndicatorPtr create_progress_indicator( + virtual ProgresIndicatorPtr create_progress_indicator( unsigned statenum, const std::string& title, - const std::string& firstmsg) const; + const std::string& firstmsg = "") const = 0; +}; - ProgresIndicatorPtr create_progress_indicator( +/** + * @brief Implementation of AppControllerBase for the GUI app + */ +class AppControllerGui: public AppControllerBase { +private: + class PriData; // Some structure to store progress indication data + + // Pimpl data for thread safe progress indication features + std::unique_ptr m_pri_data; + +public: + + AppControllerGui(); + + virtual ~AppControllerGui(); + + virtual FilePathList query_destination_paths( + const std::string& title, + const std::string& extensions, + const std::string& functionid, + const std::string& hint) const override; + + virtual FilePathList query_destination_dirs( + const std::string& /*title*/, + const std::string& /*functionid*/, + const std::string& /*hint*/) const override { return {}; } + + virtual FilePath query_destination_path( + const std::string& title, + const std::string& extensions, + const std::string& functionid, + const std::string& hint) const override; + + virtual bool report_issue(IssueType issuetype, + const std::string& description, + const std::string& brief = std::string()) override; + + virtual ProgresIndicatorPtr global_progress_indicator() override; + + virtual void global_progress_indicator(ProgresIndicatorPtr gpri) override; + + virtual bool is_main_thread() const override; + + virtual bool supports_asynch() const override; + + virtual void process_events() override; + + virtual ProgresIndicatorPtr create_progress_indicator( unsigned statenum, - const std::string& title) const; + const std::string& title, + const std::string& firstmsg) const override; + +protected: // This is a global progress indicator placeholder. In the Slic3r UI it can // contain the progress indicator on the statusbar. - ProgresIndicatorPtr global_progressind_; + ProgresIndicatorPtr m_global_progressind; +}; + +class AppControllerCli: public AppControllerBase { + + class CliProgress : public ProgressIndicator { + std::string m_msg, m_title; + public: + virtual void message(const std::string& msg) override { + m_msg = msg; + } + + virtual void title(const std::string& title) override { + m_title = title; + } + }; + +public: + + AppControllerCli() { + std::cout << "Cli AppController ready..." << std::endl; + m_global_progressind = std::make_shared(); + } + + virtual ~AppControllerCli() {} + + virtual FilePathList query_destination_paths( + const std::string& /*title*/, + const std::string& /*extensions*/, + const std::string& /*functionid*/, + const std::string& /*hint*/) const override { return {}; } + + virtual FilePathList query_destination_dirs( + const std::string& /*title*/, + const std::string& /*functionid*/, + const std::string& /*hint*/) const override { return {}; } + + virtual FilePath query_destination_path( + const std::string& /*title*/, + const std::string& /*extensions*/, + const std::string& /*functionid*/, + const std::string& /*hint*/) const override { return "out.zip"; } + + virtual bool report_issue(IssueType /*issuetype*/, + const std::string& description, + const std::string& brief) override { + std::cerr << brief << ": " << description << std::endl; + return true; + } + + virtual ProgresIndicatorPtr global_progress_indicator() override { + return m_global_progressind; + } + + virtual void global_progress_indicator(ProgresIndicatorPtr) override {} + + virtual bool is_main_thread() const override { return true; } + + virtual bool supports_asynch() const override { return false; } + + virtual void process_events() override {} + + virtual ProgresIndicatorPtr create_progress_indicator( + unsigned /*statenum*/, + const std::string& /*title*/, + const std::string& /*firstmsg*/) const override { + return std::make_shared(); + } + +protected: + + // This is a global progress indicator placeholder. In the Slic3r UI it can + // contain the progress indicator on the statusbar. + ProgresIndicatorPtr m_global_progressind; +}; + +class Zipper { + struct Impl; + std::unique_ptr m_impl; +public: + + Zipper(const std::string& zipfilepath); + ~Zipper(); + + void next_entry(const std::string& fname); + + std::string get_name() const; + + std::ostream& stream(); + + void close(); }; /** * @brief Implementation of the printing logic. */ -class PrintController: public AppControllerBoilerplate { - Print *print_ = nullptr; - std::function rempools_; +class PrintController { + Print *m_print = nullptr; + std::function m_rempools; protected: - void make_skirt() {} - void make_brim() {} - void make_wipe_tower() {} - - void make_perimeters(PrintObject *pobj) {} - void infill(PrintObject *pobj) {} - void gen_support_material(PrintObject *pobj) {} - // Data structure with the png export input data struct PngExportData { std::string zippath; // output zip file @@ -198,20 +324,14 @@ protected: PngExportData query_png_export_data(const DynamicPrintConfig&); // The previous export data, to pre-populate the dialog - PngExportData prev_expdata_; - - /** - * @brief Slice one pront object. - * @param pobj The print object. - */ - void slice(PrintObject *pobj); + PngExportData m_prev_expdata; void slice(ProgresIndicatorPtr pri); public: // Must be public for perl to use it - explicit inline PrintController(Print *print): print_(print) {} + explicit inline PrintController(Print *print): m_print(print) {} PrintController(const PrintController&) = delete; PrintController(PrintController&&) = delete; @@ -238,10 +358,10 @@ public: /** * @brief Top level controller. */ -class AppController: public AppControllerBoilerplate { - Model *model_ = nullptr; +class AppController { + Model *m_model = nullptr; PrintController::Ptr printctl; - std::atomic arranging_; + std::atomic m_arranging; public: /** @@ -258,7 +378,7 @@ public: * @param model A raw pointer to the model object. This can be used from * perl. */ - void set_model(Model *model) { model_ = model; } + void set_model(Model *model) { m_model = model; } /** * @brief Set the print object from perl. diff --git a/src/slic3r/AppControllerWx.cpp b/src/slic3r/AppControllerWx.cpp index 4d67d5f66..25cd739f3 100644 --- a/src/slic3r/AppControllerWx.cpp +++ b/src/slic3r/AppControllerWx.cpp @@ -1,5 +1,9 @@ #include "AppController.hpp" +#include +#include +#include + #include #include @@ -21,40 +25,43 @@ namespace Slic3r { -bool AppControllerBoilerplate::supports_asynch() const +bool AppControllerGui::supports_asynch() const { return true; } -void AppControllerBoilerplate::process_events() +void AppControllerGui::process_events() { wxYieldIfNeeded(); } -AppControllerBoilerplate::PathList -AppControllerBoilerplate::query_destination_paths( +FilePathList AppControllerGui::query_destination_paths( const std::string &title, - const std::string &extensions) const + const std::string &extensions, + const std::string &/*functionid*/, + const std::string& hint) const { wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) ); dlg.SetWildcard(extensions); - dlg.ShowModal(); + dlg.SetFilename(hint); - wxArrayString paths; - dlg.GetPaths(paths); + FilePathList ret; - PathList ret(paths.size(), ""); - for(auto& p : paths) ret.push_back(p.ToStdString()); + if(dlg.ShowModal() == wxID_OK) { + wxArrayString paths; + dlg.GetPaths(paths); + for(auto& p : paths) ret.push_back(p.ToStdString()); + } return ret; } -AppControllerBoilerplate::Path -AppControllerBoilerplate::query_destination_path( +FilePath AppControllerGui::query_destination_path( const std::string &title, const std::string &extensions, + const std::string &/*functionid*/, const std::string& hint) const { wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) ); @@ -62,16 +69,16 @@ AppControllerBoilerplate::query_destination_path( dlg.SetFilename(hint); - Path ret; + FilePath ret; if(dlg.ShowModal() == wxID_OK) { - ret = Path(dlg.GetPath()); + ret = FilePath(dlg.GetPath()); } return ret; } -bool AppControllerBoilerplate::report_issue(IssueType issuetype, +bool AppControllerGui::report_issue(IssueType issuetype, const std::string &description, const std::string &brief) { @@ -89,14 +96,52 @@ bool AppControllerBoilerplate::report_issue(IssueType issuetype, return ret != wxCANCEL; } -bool AppControllerBoilerplate::report_issue( - AppControllerBoilerplate::IssueType issuetype, - const std::string &description) +wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent); + +struct Zipper::Impl { + wxFileName fpath; + wxFFileOutputStream zipfile; + wxZipOutputStream zipstream; + wxStdOutputStream pngstream; + + Impl(const std::string& zipfile_path): + fpath(zipfile_path), + zipfile(zipfile_path), + zipstream(zipfile), + pngstream(zipstream) + { + if(!zipfile.IsOk()) + throw std::runtime_error(L("Cannot create zip file.")); + } +}; + +Zipper::Zipper(const std::string &zipfilepath) { - return report_issue(issuetype, description, std::string()); + m_impl.reset(new Impl(zipfilepath)); } -wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent); +Zipper::~Zipper() {} + +void Zipper::next_entry(const std::string &fname) +{ + m_impl->zipstream.PutNextEntry(fname); +} + +std::string Zipper::get_name() const +{ + return m_impl->fpath.GetName().ToStdString(); +} + +std::ostream &Zipper::stream() +{ + return m_impl->pngstream; +} + +void Zipper::close() +{ + m_impl->zipstream.Close(); + m_impl->zipfile.Close(); +} namespace { @@ -107,26 +152,26 @@ namespace { class GuiProgressIndicator: public ProgressIndicator, public wxEvtHandler { - wxProgressDialog gauge_; + wxProgressDialog m_gauge; using Base = ProgressIndicator; - wxString message_; - int range_; wxString title_; - bool is_asynch_ = false; + wxString m_message; + int m_range; wxString m_title; + bool m_is_asynch = false; - const int id_ = wxWindow::NewControlId(); + const int m_id = wxWindow::NewControlId(); // status update handler void _state( wxCommandEvent& evt) { unsigned st = evt.GetInt(); - message_ = evt.GetString(); + m_message = evt.GetString(); _state(st); } // Status update implementation void _state( unsigned st) { - if(!gauge_.IsShown()) gauge_.ShowModal(); + if(!m_gauge.IsShown()) m_gauge.ShowModal(); Base::state(st); - if(!gauge_.Update(static_cast(st), message_)) { + if(!m_gauge.Update(static_cast(st), m_message)) { cancel(); } } @@ -134,25 +179,25 @@ class GuiProgressIndicator: public: /// Setting whether it will be used from the UI thread or some worker thread - inline void asynch(bool is) { is_asynch_ = is; } + inline void asynch(bool is) { m_is_asynch = is; } /// Get the mode of parallel operation. - inline bool asynch() const { return is_asynch_; } + inline bool asynch() const { return m_is_asynch; } inline GuiProgressIndicator(int range, const wxString& title, const wxString& firstmsg) : - gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(), + m_gauge(title, firstmsg, range, wxTheApp->GetTopWindow(), wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT), - message_(firstmsg), - range_(range), title_(title) + m_message(firstmsg), + m_range(range), m_title(title) { Base::max(static_cast(range)); Base::states(static_cast(range)); Bind(PROGRESS_STATUS_UPDATE_EVENT, &GuiProgressIndicator::_state, - this, id_); + this, m_id); } virtual void state(float val) override { @@ -161,33 +206,32 @@ public: void state(unsigned st) { // send status update event - if(is_asynch_) { - auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_); + if(m_is_asynch) { + auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, m_id); evt->SetInt(st); - evt->SetString(message_); + evt->SetString(m_message); wxQueueEvent(this, evt); } else _state(st); } virtual void message(const std::string & msg) override { - message_ = _(msg); + m_message = _(msg); } virtual void messageFmt(const std::string& fmt, ...) { va_list arglist; va_start(arglist, fmt); - message_ = wxString::Format(_(fmt), arglist); + m_message = wxString::Format(_(fmt), arglist); va_end(arglist); } virtual void title(const std::string & title) override { - title_ = _(title); + m_title = _(title); } }; } -AppControllerBoilerplate::ProgresIndicatorPtr -AppControllerBoilerplate::create_progress_indicator( +ProgresIndicatorPtr AppControllerGui::create_progress_indicator( unsigned statenum, const std::string& title, const std::string& firstmsg) const @@ -202,30 +246,23 @@ AppControllerBoilerplate::create_progress_indicator( return pri; } -AppControllerBoilerplate::ProgresIndicatorPtr -AppControllerBoilerplate::create_progress_indicator( - unsigned statenum, const std::string &title) const -{ - return create_progress_indicator(statenum, title, std::string()); -} - namespace { class Wrapper: public ProgressIndicator, public wxEvtHandler { - ProgressStatusBar *sbar_; + ProgressStatusBar *m_sbar; using Base = ProgressIndicator; - wxString message_; - AppControllerBoilerplate& ctl_; + wxString m_message; + AppControllerBase& m_ctl; void showProgress(bool show = true) { - sbar_->show_progress(show); + m_sbar->show_progress(show); } void _state(unsigned st) { if( st <= ProgressIndicator::max() ) { Base::state(st); - sbar_->set_status_text(message_); - sbar_->set_progress(st); + m_sbar->set_status_text(m_message); + m_sbar->set_progress(st); } } @@ -239,11 +276,11 @@ class Wrapper: public ProgressIndicator, public wxEvtHandler { public: inline Wrapper(ProgressStatusBar *sbar, - AppControllerBoilerplate& ctl): - sbar_(sbar), ctl_(ctl) + AppControllerBase& ctl): + m_sbar(sbar), m_ctl(ctl) { - Base::max(static_cast(sbar_->get_range())); - Base::states(static_cast(sbar_->get_range())); + Base::max(static_cast(m_sbar->get_range())); + Base::states(static_cast(m_sbar->get_range())); Bind(PROGRESS_STATUS_UPDATE_EVENT, &Wrapper::_state, @@ -256,13 +293,13 @@ public: virtual void max(float val) override { if(val > 1.0) { - sbar_->set_range(static_cast(val)); + m_sbar->set_range(static_cast(val)); ProgressIndicator::max(val); } } void state(unsigned st) { - if(!ctl_.is_main_thread()) { + if(!m_ctl.is_main_thread()) { auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_); evt->SetInt(st); wxQueueEvent(this, evt); @@ -272,20 +309,20 @@ public: } virtual void message(const std::string & msg) override { - message_ = _(msg); + m_message = _(msg); } virtual void message_fmt(const std::string& fmt, ...) override { va_list arglist; va_start(arglist, fmt); - message_ = wxString::Format(_(fmt), arglist); + m_message = wxString::Format(_(fmt), arglist); va_end(arglist); } virtual void title(const std::string & /*title*/) override {} virtual void on_cancel(CancelFn fn) override { - sbar_->set_cancel_callback(fn); + m_sbar->set_cancel_callback(fn); Base::on_cancel(fn); } @@ -295,7 +332,8 @@ public: void AppController::set_global_progress_indicator(ProgressStatusBar *prsb) { if(prsb) { - global_progress_indicator(std::make_shared(prsb, *this)); + auto ctl = GUI::get_appctl(); + ctl->global_progress_indicator(std::make_shared(prsb, *ctl)); } } diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index e6f038042..5b830b30d 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -196,7 +196,11 @@ const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; GLVolume::GLVolume(float r, float g, float b, float a) : m_offset(Vec3d::Zero()) +#if ENABLE_MODELINSTANCE_3D_ROTATION + , m_rotation(Vec3d::Zero()) +#else , m_rotation(0.0) +#endif // ENABLE_MODELINSTANCE_3D_ROTATION , m_scaling_factor(1.0) , m_world_matrix(Transform3f::Identity()) , m_world_matrix_dirty(true) @@ -255,7 +259,24 @@ void GLVolume::set_render_color() set_render_color(color, 4); } -double GLVolume::get_rotation() +#if ENABLE_MODELINSTANCE_3D_ROTATION +const Vec3d& GLVolume::get_rotation() const +{ + return m_rotation; +} + +void GLVolume::set_rotation(const Vec3d& rotation) +{ + if (m_rotation != rotation) + { + m_rotation = rotation; + m_world_matrix_dirty = true; + m_transformed_bounding_box_dirty = true; + m_transformed_convex_hull_bounding_box_dirty = true; + } +} +#else +double GLVolume::get_rotation() const { return m_rotation; } @@ -270,6 +291,7 @@ void GLVolume::set_rotation(double rotation) m_transformed_convex_hull_bounding_box_dirty = true; } } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION const Vec3d& GLVolume::get_offset() const { @@ -327,7 +349,13 @@ const Transform3f& GLVolume::world_matrix() const { m_world_matrix = Transform3f::Identity(); m_world_matrix.translate(m_offset.cast()); +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation(2), Vec3f::UnitZ())); + m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation(1), Vec3f::UnitY())); + m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation(0), Vec3f::UnitX())); +#else m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation, Vec3f::UnitZ())); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION m_world_matrix.scale((float)m_scaling_factor); m_world_matrix_dirty = false; } @@ -403,7 +431,13 @@ void GLVolume::render() const ::glCullFace(GL_BACK); ::glPushMatrix(); ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); +#if ENABLE_MODELINSTANCE_3D_ROTATION + ::glRotated(m_rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glRotated(m_rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0); + ::glRotated(m_rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0); +#else ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); if (this->indexed_vertex_array.indexed()) this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); @@ -529,7 +563,13 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c ::glPushMatrix(); ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); +#if ENABLE_MODELINSTANCE_3D_ROTATION + ::glRotated(m_rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glRotated(m_rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0); + ::glRotated(m_rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0); +#else ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); if (n_triangles > 0) @@ -574,7 +614,13 @@ void GLVolume::render_legacy() const ::glPushMatrix(); ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); +#if ENABLE_MODELINSTANCE_3D_ROTATION + ::glRotated(m_rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glRotated(m_rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0); + ::glRotated(m_rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0); +#else ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); if (n_triangles > 0) @@ -698,7 +744,11 @@ std::vector GLVolumeCollection::load_object( #else v.set_offset(Vec3d(instance->offset(0), instance->offset(1), 0.0)); #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + v.set_rotation(instance->get_rotation()); +#else v.set_rotation(instance->rotation); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION v.set_scaling_factor(instance->scaling_factor); } } @@ -2067,12 +2117,30 @@ void _3DScene::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, vo void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback) { +#if !ENABLE_MODELINSTANCE_3D_ROTATION s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback); +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION +} + +void _3DScene::register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback) +{ +#if ENABLE_MODELINSTANCE_3D_ROTATION + s_canvas_mgr.register_on_gizmo_rotate_3D_callback(canvas, callback); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION } void _3DScene::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback) { +#if !ENABLE_MODELINSTANCE_3D_ROTATION s_canvas_mgr.register_on_gizmo_flatten_callback(canvas, callback); +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION +} + +void _3DScene::register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback) +{ +#if ENABLE_MODELINSTANCE_3D_ROTATION + s_canvas_mgr.register_on_gizmo_flatten_3D_callback(canvas, callback); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION } void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index f2d1c0786..8fc1661fc 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -256,8 +256,13 @@ public: private: // Offset of the volume to be rendered. Vec3d m_offset; +#if ENABLE_MODELINSTANCE_3D_ROTATION + // Rotation around three axes of the volume to be rendered. + Vec3d m_rotation; +#else // Rotation around Z axis of the volume to be rendered. double m_rotation; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION // Scale factor of the volume to be rendered. double m_scaling_factor; // World matrix of the volume to be rendered. @@ -327,8 +332,13 @@ public: // Sets render color in dependence of current state void set_render_color(); - double get_rotation(); +#if ENABLE_MODELINSTANCE_3D_ROTATION + const Vec3d& get_rotation() const; + void set_rotation(const Vec3d& rotation); +#else + double get_rotation() const; void set_rotation(double rotation); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION const Vec3d& get_offset() const; void set_offset(const Vec3d& offset); @@ -558,7 +568,9 @@ public: static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); + static void register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback); static void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback); + static void register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback); static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); static void register_action_add_callback(wxGLCanvas* canvas, void* callback); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index cb3250916..658e1731a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1095,6 +1095,9 @@ GLCanvas3D::Mouse::Drag::Drag() GLCanvas3D::Mouse::Mouse() : dragging(false) , position(DBL_MAX, DBL_MAX) +#if ENABLE_GIZMOS_RESET + , ignore_up_event(false) +#endif // ENABLE_GIZMOS_RESET { } @@ -1181,9 +1184,11 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) return false; } +#if !ENABLE_MODELINSTANCE_3D_ROTATION // temporary disable x and y grabbers gizmo->disable_grabber(0); gizmo->disable_grabber(1); +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); @@ -1349,6 +1354,18 @@ void GLCanvas3D::Gizmos::update(const Linef3& mouse_ray) curr->update(mouse_ray); } +#if ENABLE_GIZMOS_RESET +void GLCanvas3D::Gizmos::process_double_click() +{ + if (!m_enabled) + return; + + GLGizmoBase* curr = _get_current(); + if (curr != nullptr) + curr->process_double_click(); +} +#endif // ENABLE_GIZMOS_RESET + GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const { return m_current; @@ -1421,6 +1438,35 @@ void GLCanvas3D::Gizmos::set_scale(float scale) reinterpret_cast(it->second)->set_scale(scale); } +#if ENABLE_MODELINSTANCE_3D_ROTATION +Vec3d GLCanvas3D::Gizmos::get_rotation() const +{ + if (!m_enabled) + return Vec3d::Zero(); + + GizmosMap::const_iterator it = m_gizmos.find(Rotate); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_rotation() : Vec3d::Zero(); +} + +void GLCanvas3D::Gizmos::set_rotation(const Vec3d& rotation) +{ + if (!m_enabled) + return; + + GizmosMap::const_iterator it = m_gizmos.find(Rotate); + if (it != m_gizmos.end()) + reinterpret_cast(it->second)->set_rotation(rotation); +} + +Vec3d GLCanvas3D::Gizmos::get_flattening_rotation() const +{ + if (!m_enabled) + return Vec3d::Zero(); + + GizmosMap::const_iterator it = m_gizmos.find(Flatten); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_rotation() : Vec3d::Zero(); +} +#else float GLCanvas3D::Gizmos::get_angle_z() const { if (!m_enabled) @@ -1448,6 +1494,7 @@ Vec3d GLCanvas3D::Gizmos::get_flattening_normal() const GizmosMap::const_iterator it = m_gizmos.find(Flatten); return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_normal() : Vec3d::Zero(); } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object) { @@ -2385,7 +2432,11 @@ void GLCanvas3D::update_gizmos_data() m_gizmos.set_position(Vec3d(model_instance->offset(0), model_instance->offset(1), 0.0)); #endif // ENABLE_MODELINSTANCE_3D_OFFSET m_gizmos.set_scale(model_instance->scaling_factor); +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_gizmos.set_rotation(model_instance->get_rotation()); +#else m_gizmos.set_angle_z(model_instance->rotation); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION m_gizmos.set_flattening_data(model_object); } } @@ -2394,7 +2445,11 @@ void GLCanvas3D::update_gizmos_data() { m_gizmos.set_position(Vec3d::Zero()); m_gizmos.set_scale(1.0f); +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_gizmos.set_rotation(Vec3d::Zero()); +#else m_gizmos.set_angle_z(0.0f); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION m_gizmos.set_flattening_data(nullptr); } } @@ -2757,6 +2812,19 @@ void GLCanvas3D::register_on_gizmo_scale_uniformly_callback(void* callback) m_on_gizmo_scale_uniformly_callback.register_callback(callback); } +#if ENABLE_MODELINSTANCE_3D_ROTATION +void GLCanvas3D::register_on_gizmo_rotate_3D_callback(void* callback) +{ + if (callback != nullptr) + m_on_gizmo_rotate_3D_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_gizmo_flatten_3D_callback(void* callback) +{ + if (callback != nullptr) + m_on_gizmo_flatten_3D_callback.register_callback(callback); +} +#else void GLCanvas3D::register_on_gizmo_rotate_callback(void* callback) { if (callback != nullptr) @@ -2768,6 +2836,7 @@ void GLCanvas3D::register_on_gizmo_flatten_callback(void* callback) if (callback != nullptr) m_on_gizmo_flatten_callback.register_callback(callback); } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void GLCanvas3D::register_on_update_geometry_info_callback(void* callback) { @@ -3043,6 +3112,41 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_toolbar_action_running = true; m_toolbar.do_action((unsigned int)toolbar_contains_mouse); } +#if ENABLE_GIZMOS_RESET + else if (evt.LeftDClick() && m_gizmos.grabber_contains_mouse()) + { +#if ENABLE_GIZMOS_RESET + m_mouse.ignore_up_event = true; +#endif // ENABLE_GIZMOS_RESET + m_gizmos.process_double_click(); + switch (m_gizmos.get_current_type()) + { + case Gizmos::Scale: + { + m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale()); + update_scale_values(); + m_dirty = true; + break; + } + case Gizmos::Rotate: + { +#if ENABLE_MODELINSTANCE_3D_ROTATION + const Vec3d& rotation = m_gizmos.get_rotation(); + m_on_gizmo_rotate_3D_callback.call(rotation(0), rotation(1), rotation(2)); +#else + m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z()); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + update_rotation_values(); + m_dirty = true; + break; + } + default: + { + break; + } + } + } +#endif // ENABLE_GIZMOS_RESET else if (evt.LeftDown() || evt.RightDown()) { // If user pressed left or right button we first check whether this happened @@ -3083,6 +3187,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(selected_object_idx); if (m_gizmos.get_current_type() == Gizmos::Flatten) { +#if ENABLE_MODELINSTANCE_3D_ROTATION + // Rotate the object so the normal points downward: + const Vec3d& rotation = m_gizmos.get_flattening_rotation(); + m_on_gizmo_flatten_3D_callback.call(rotation(0), rotation(1), rotation(2)); +#else // Rotate the object so the normal points downward: Vec3d normal = m_gizmos.get_flattening_normal(); if (normal(0) != 0.0 || normal(1) != 0.0 || normal(2) != 0.0) { @@ -3090,6 +3199,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) float angle = acos(clamp(-1.0, 1.0, -normal(2))); m_on_gizmo_flatten_callback.call(angle, (float)axis(0), (float)axis(1), (float)axis(2)); } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION } m_dirty = true; @@ -3272,6 +3382,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } case Gizmos::Rotate: { +#if ENABLE_MODELINSTANCE_3D_ROTATION + // Apply new temporary rotation + Vec3d rotation = m_gizmos.get_rotation(); + for (GLVolume* v : volumes) + { + v->set_rotation(rotation); + } + update_rotation_value(rotation); +#else // Apply new temporary angle_z float angle_z = m_gizmos.get_angle_z(); for (GLVolume* v : volumes) @@ -3279,6 +3398,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) v->set_rotation((double)angle_z); } update_rotation_value((double)angle_z, Z); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION break; } default: @@ -3377,12 +3497,20 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled()) { // deselect and propagate event through callback +#if ENABLE_GIZMOS_RESET + if (!m_mouse.ignore_up_event && m_picking_enabled && !m_toolbar_action_running) +#else if (m_picking_enabled && !m_toolbar_action_running) +#endif // ENABLE_GIZMOS_RESET { deselect_volumes(); _on_select(-1, -1); update_gizmos_data(); } +#if ENABLE_GIZMOS_RESET + else if (m_mouse.ignore_up_event) + m_mouse.ignore_up_event = false; +#endif // ENABLE_GIZMOS_RESET } else if (evt.LeftUp() && m_gizmos.is_dragging()) { @@ -3416,14 +3544,19 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } case Gizmos::Rotate: { +#if ENABLE_MODELINSTANCE_3D_ROTATION + const Vec3d& rotation = m_gizmos.get_rotation(); + m_on_gizmo_rotate_3D_callback.call(rotation(0), rotation(1), rotation(2)); +#else m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z()); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION break; } default: break; } m_gizmos.stop_dragging(); - Slic3r::GUI::update_settings_value(); + update_settings_value(); } m_mouse.drag.move_volume_idx = -1; @@ -3863,8 +3996,13 @@ void GLCanvas3D::_deregister_callbacks() m_on_wipe_tower_moved_callback.deregister_callback(); m_on_enable_action_buttons_callback.deregister_callback(); m_on_gizmo_scale_uniformly_callback.deregister_callback(); +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_on_gizmo_rotate_3D_callback.deregister_callback(); + m_on_gizmo_flatten_3D_callback.deregister_callback(); +#else m_on_gizmo_rotate_callback.deregister_callback(); m_on_gizmo_flatten_callback.deregister_callback(); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION m_on_update_geometry_info_callback.deregister_callback(); m_action_add_callback.deregister_callback(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 528f73fc1..712ffbefc 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -315,6 +315,9 @@ class GLCanvas3D bool dragging; Vec2d position; Drag drag; +#if ENABLE_GIZMOS_RESET + bool ignore_up_event; +#endif // ENABLE_GIZMOS_RESET Mouse(); @@ -366,6 +369,9 @@ class GLCanvas3D bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const; bool grabber_contains_mouse() const; void update(const Linef3& mouse_ray); +#if ENABLE_GIZMOS_RESET + void process_double_click(); +#endif // ENABLE_GIZMOS_RESET EType get_current_type() const; @@ -381,11 +387,20 @@ class GLCanvas3D float get_scale() const; void set_scale(float scale); +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d get_rotation() const; + void set_rotation(const Vec3d& rotation); +#else float get_angle_z() const; void set_angle_z(float angle_z); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION - void set_flattening_data(const ModelObject* model_object); +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d get_flattening_rotation() const; +#else Vec3d get_flattening_normal() const; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + void set_flattening_data(const ModelObject* model_object); void render_current_gizmo(const BoundingBoxf3& box) const; @@ -501,8 +516,13 @@ class GLCanvas3D PerlCallback m_on_wipe_tower_moved_callback; PerlCallback m_on_enable_action_buttons_callback; PerlCallback m_on_gizmo_scale_uniformly_callback; +#if ENABLE_MODELINSTANCE_3D_ROTATION + PerlCallback m_on_gizmo_rotate_3D_callback; + PerlCallback m_on_gizmo_flatten_3D_callback; +#else PerlCallback m_on_gizmo_rotate_callback; PerlCallback m_on_gizmo_flatten_callback; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION PerlCallback m_on_update_geometry_info_callback; PerlCallback m_action_add_callback; @@ -626,8 +646,13 @@ public: void register_on_wipe_tower_moved_callback(void* callback); void register_on_enable_action_buttons_callback(void* callback); void register_on_gizmo_scale_uniformly_callback(void* callback); +#if ENABLE_MODELINSTANCE_3D_ROTATION + void register_on_gizmo_rotate_3D_callback(void* callback); + void register_on_gizmo_flatten_3D_callback(void* callback); +#else void register_on_gizmo_rotate_callback(void* callback); void register_on_gizmo_flatten_callback(void* callback); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void register_on_update_geometry_info_callback(void* callback); void register_action_add_callback(void* callback); diff --git a/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/GLCanvas3DManager.cpp index 495f49425..1ac588f6c 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -699,6 +699,21 @@ void GLCanvas3DManager::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* c it->second->register_on_gizmo_scale_uniformly_callback(callback); } +#if ENABLE_MODELINSTANCE_3D_ROTATION +void GLCanvas3DManager::register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_gizmo_rotate_3D_callback(callback); +} + +void GLCanvas3DManager::register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_gizmo_flatten_3D_callback(callback); +} +#else void GLCanvas3DManager::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -712,6 +727,7 @@ void GLCanvas3DManager::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, v if (it != m_canvases.end()) it->second->register_on_gizmo_flatten_callback(callback); } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void GLCanvas3DManager::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) { diff --git a/src/slic3r/GUI/GLCanvas3DManager.hpp b/src/slic3r/GUI/GLCanvas3DManager.hpp index 4922b6171..2331ec3f5 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -163,8 +163,13 @@ public: void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); +#if ENABLE_MODELINSTANCE_3D_ROTATION + void register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback); + void register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback); +#else void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); void register_action_add_callback(wxGLCanvas* canvas, void* callback); diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index e23958c1d..f9b481a31 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -759,6 +759,20 @@ void GLGizmoScale3D::on_update(const Linef3& mouse_ray) do_scale_uniform(mouse_ray); } +#if ENABLE_GIZMOS_RESET +void GLGizmoScale3D::on_process_double_click() +{ + if ((m_hover_id == 0) || (m_hover_id == 1)) + m_scale(0) = 1.0; + else if ((m_hover_id == 2) || (m_hover_id == 3)) + m_scale(1) = 1.0; + else if ((m_hover_id == 4) || (m_hover_id == 5)) + m_scale(2) = 1.0; + else if (m_hover_id >= 6) + m_scale = Vec3d::Ones(); +} +#endif // ENABLE_GIZMOS_RESET + void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const { if (m_grabbers[0].dragging || m_grabbers[1].dragging) @@ -1237,15 +1251,28 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + for (const InstanceData& inst : m_instances) { + Vec3d position = inst.position + dragged_offset; +#else for (Vec3d offset : m_instances_positions) { offset += dragged_offset; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else for (Vec2d offset : m_instances_positions) { offset += to_2d(dragged_offset); #endif // ENABLE_MODELINSTANCE_3D_OFFSET ::glPushMatrix(); #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + ::glTranslated(position(0), position(1), position(2)); + ::glRotated(inst.rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glRotated(inst.rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0); + ::glRotated(inst.rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0); + ::glScaled(inst.scaling_factor, inst.scaling_factor, inst.scaling_factor); +#else ::glTranslated(offset(0), offset(1), offset(2)); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else ::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f); #endif // ENABLE_MODELINSTANCE_3D_OFFSET @@ -1268,13 +1295,25 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const { ::glColor3f(1.0f, 1.0f, picking_color_component(i)); #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + for (const InstanceData& inst : m_instances) { +#else for (const Vec3d& offset : m_instances_positions) { +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else for (const Vec2d& offset : m_instances_positions) { #endif // ENABLE_MODELINSTANCE_3D_OFFSET ::glPushMatrix(); #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + ::glTranslated(inst.position(0), inst.position(1), inst.position(2)); + ::glRotated(inst.rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glRotated(inst.rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0); + ::glRotated(inst.rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0); + ::glScaled(inst.scaling_factor, inst.scaling_factor, inst.scaling_factor); +#else ::glTranslated(offset(0), offset(1), offset(2)); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else ::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f); #endif // ENABLE_MODELINSTANCE_3D_OFFSET @@ -1293,10 +1332,18 @@ void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) // ...and save the updated positions of the object instances: if (m_model_object && !m_model_object->instances.empty()) { +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_instances.clear(); +#else m_instances_positions.clear(); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION for (const auto* instance : m_model_object->instances) #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + m_instances.emplace_back(instance->get_offset(), instance->get_rotation(), instance->scaling_factor); +#else m_instances_positions.emplace_back(instance->get_offset()); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else m_instances_positions.emplace_back(instance->offset); #endif // ENABLE_MODELINSTANCE_3D_OFFSET @@ -1312,8 +1359,10 @@ void GLGizmoFlatten::update_planes() for (const ModelVolume* vol : m_model_object->volumes) ch.merge(vol->get_convex_hull()); ch = ch.convex_hull_3d(); +#if !ENABLE_MODELINSTANCE_3D_ROTATION ch.scale(m_model_object->instances.front()->scaling_factor); ch.rotate_z(m_model_object->instances.front()->rotation); +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION m_planes.clear(); @@ -1358,8 +1407,8 @@ void GLGizmoFlatten::update_planes() // if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected anyway): if (m_planes.back().vertices.size() == 3 && (m_planes.back().vertices[0] - m_planes.back().vertices[1]).norm() < 1.f - || (m_planes.back().vertices[0] - m_planes.back().vertices[2]).norm() < 1.f) - m_planes.pop_back(); + || (m_planes.back().vertices[0] - m_planes.back().vertices[2]).norm() < 1.f) + m_planes.pop_back(); } // Now we'll go through all the polygons, transform the points into xy plane to process them: @@ -1462,8 +1511,10 @@ void GLGizmoFlatten::update_planes() m_source_data.bounding_boxes.clear(); for (const auto& vol : m_model_object->volumes) m_source_data.bounding_boxes.push_back(vol->get_convex_hull().bounding_box()); +#if !ENABLE_MODELINSTANCE_3D_ROTATION m_source_data.scaling_factor = m_model_object->instances.front()->scaling_factor; m_source_data.rotation = m_model_object->instances.front()->rotation; +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex(); m_source_data.mesh_first_point = Vec3d((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]); } @@ -1475,10 +1526,14 @@ bool GLGizmoFlatten::is_plane_update_necessary() const if (m_state != On || !m_model_object || m_model_object->instances.empty()) return false; +#if ENABLE_MODELINSTANCE_3D_ROTATION + if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size()) +#else if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size() || m_model_object->instances.front()->scaling_factor != m_source_data.scaling_factor || m_model_object->instances.front()->rotation != m_source_data.rotation) - return true; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + return true; // now compare the bounding boxes: for (unsigned int i=0; ivolumes.size(); ++i) @@ -1493,11 +1548,22 @@ bool GLGizmoFlatten::is_plane_update_necessary() const return false; } +#if ENABLE_MODELINSTANCE_3D_ROTATION +Vec3d GLGizmoFlatten::get_flattening_rotation() const +{ + // calculates the rotations in model space + Eigen::Quaterniond q; + Vec3d angles = q.setFromTwoVectors(m_normal, -Vec3d::UnitZ()).toRotationMatrix().eulerAngles(2, 1, 0); + m_normal = Vec3d::Zero(); + return Vec3d(angles(2), angles(1), angles(0)); +} +#else Vec3d GLGizmoFlatten::get_flattening_normal() const { Vec3d normal = m_model_object->instances.front()->world_matrix(true).matrix().block(0, 0, 3, 3).inverse() * m_normal; m_normal = Vec3d::Zero(); return normal.normalized(); } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 2430b5af5..e0f2c73d0 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -94,6 +94,10 @@ public: void update(const Linef3& mouse_ray); +#if ENABLE_GIZMOS_RESET + void process_double_click() { on_process_double_click(); } +#endif // ENABLE_GIZMOS_RESET + void render(const BoundingBoxf3& box) const { on_render(box); } void render_for_picking(const BoundingBoxf3& box) const { on_render_for_picking(box); } @@ -106,6 +110,9 @@ protected: virtual void on_start_dragging(const BoundingBoxf3& box) {} virtual void on_stop_dragging() {} virtual void on_update(const Linef3& mouse_ray) = 0; +#if ENABLE_GIZMOS_RESET + virtual void on_process_double_click() {} +#endif // ENABLE_GIZMOS_RESET virtual void on_render(const BoundingBoxf3& box) const = 0; virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0; @@ -155,6 +162,9 @@ protected: virtual bool on_init(); virtual void on_start_dragging(const BoundingBoxf3& box); virtual void on_update(const Linef3& mouse_ray); +#if ENABLE_GIZMOS_RESET + virtual void on_process_double_click() { m_angle = 0.0; } +#endif // ENABLE_GIZMOS_RESET virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; @@ -178,6 +188,10 @@ class GLGizmoRotate3D : public GLGizmoBase public: explicit GLGizmoRotate3D(GLCanvas3D& parent); +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } + void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } +#else double get_angle_x() const { return m_gizmos[X].get_angle(); } void set_angle_x(double angle) { m_gizmos[X].set_angle(angle); } @@ -186,6 +200,7 @@ public: double get_angle_z() const { return m_gizmos[Z].get_angle(); } void set_angle_z(double angle) { m_gizmos[Z].set_angle(angle); } +#endif // ENABLE_MODELINSTANCE_3D_ROTATION protected: virtual bool on_init(); @@ -222,6 +237,13 @@ protected: g.update(mouse_ray); } } +#if ENABLE_GIZMOS_RESET + virtual void on_process_double_click() + { + if (m_hover_id != -1) + m_gizmos[m_hover_id].process_double_click(); + } +#endif // ENABLE_GIZMOS_RESET virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const { @@ -265,6 +287,9 @@ protected: virtual void on_start_dragging(const BoundingBoxf3& box); virtual void on_stop_dragging() { m_show_starting_box = false; } virtual void on_update(const Linef3& mouse_ray); +#if ENABLE_GIZMOS_RESET + virtual void on_process_double_click(); +#endif // ENABLE_GIZMOS_RESET virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; @@ -320,8 +345,10 @@ private: }; struct SourceDataSummary { std::vector bounding_boxes; // bounding boxes of convex hulls of individual volumes +#if !ENABLE_MODELINSTANCE_3D_ROTATION float scaling_factor; float rotation; +#endif // !ENABLE_MODELINSTANCE_3D_ROTATION Vec3d mesh_first_point; }; @@ -330,7 +357,19 @@ private: std::vector m_planes; #if ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + struct InstanceData + { + Vec3d position; + Vec3d rotation; + double scaling_factor; + + InstanceData(const Vec3d& position, const Vec3d& rotation, double scaling_factor) : position(position), rotation(rotation), scaling_factor(scaling_factor) {} + }; + std::vector m_instances; +#else Pointf3s m_instances_positions; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION #else std::vector m_instances_positions; #endif // ENABLE_MODELINSTANCE_3D_OFFSET @@ -344,7 +383,11 @@ public: explicit GLGizmoFlatten(GLCanvas3D& parent); void set_flattening_data(const ModelObject* model_object); +#if ENABLE_MODELINSTANCE_3D_ROTATION + Vec3d get_flattening_rotation() const; +#else Vec3d get_flattening_normal() const; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION protected: virtual bool on_init(); diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index e5ed917b3..2ad155b7d 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -1,4 +1,5 @@ #include "GUI.hpp" +#include "../AppController.hpp" #include "WipeTowerDialog.hpp" #include @@ -1405,4 +1406,23 @@ void desktop_open_datadir_folder() #endif } +namespace { +AppControllerPtr g_appctl; +} + +AppControllerPtr get_appctl() +{ + return g_appctl; +} + +void set_cli_appctl() +{ + g_appctl = std::make_shared(); +} + +void set_gui_appctl() +{ + g_appctl = std::make_shared(); +} + } } diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index 9b2fce5b4..7f470432d 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -42,6 +42,9 @@ class TabIface; class PreviewIface; class Print; class GCodePreviewData; +class AppControllerBase; + +using AppControllerPtr = std::shared_ptr; #define _(s) Slic3r::GUI::I18N::translate((s)) @@ -129,6 +132,10 @@ ProgressStatusBar* get_progress_status_bar(); wxNotebook * get_tab_panel(); wxNotebook* get_tab_panel(); +AppControllerPtr get_appctl(); +void set_cli_appctl(); +void set_gui_appctl(); + const wxColour& get_label_clr_modified(); const wxColour& get_label_clr_sys(); const wxColour& get_label_clr_default(); diff --git a/src/slic3r/GUI/GUI_ObjectParts.cpp b/src/slic3r/GUI/GUI_ObjectParts.cpp index 4fc3b88c0..96483721b 100644 --- a/src/slic3r/GUI/GUI_ObjectParts.cpp +++ b/src/slic3r/GUI/GUI_ObjectParts.cpp @@ -1802,11 +1802,15 @@ void update_scale_values(double scaling_factor) void update_rotation_values() { +#if ENABLE_MODELINSTANCE_3D_ROTATION + update_rotation_value((*m_objects)[m_selected_object_id]->instances.front()->get_rotation()); +#else auto og = get_optgroup(ogFrequentlyObjectSettings); auto instance = (*m_objects)[m_selected_object_id]->instances.front(); og->set_value("rotation_x", 0); og->set_value("rotation_y", 0); og->set_value("rotation_z", int(Geometry::rad2deg(instance->rotation))); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION } void update_rotation_value(double angle, Axis axis) @@ -1836,6 +1840,16 @@ void update_rotation_value(double angle, Axis axis) og->set_value(axis_str, int(Geometry::rad2deg(angle))); } +#if ENABLE_MODELINSTANCE_3D_ROTATION +void update_rotation_value(const Vec3d& rotation) +{ + auto og = get_optgroup(ogFrequentlyObjectSettings); + og->set_value("rotation_x", int(Geometry::rad2deg(rotation(0)))); + og->set_value("rotation_y", int(Geometry::rad2deg(rotation(1)))); + og->set_value("rotation_z", int(Geometry::rad2deg(rotation(2)))); +} +#endif // ENABLE_MODELINSTANCE_3D_ROTATION + void set_uniform_scaling(const bool uniform_scale) { g_is_uniform_scale = uniform_scale; diff --git a/src/slic3r/GUI/GUI_ObjectParts.hpp b/src/slic3r/GUI/GUI_ObjectParts.hpp index e66b4d1db..fdeb7a629 100644 --- a/src/slic3r/GUI/GUI_ObjectParts.hpp +++ b/src/slic3r/GUI/GUI_ObjectParts.hpp @@ -124,6 +124,9 @@ void update_scale_values(double scaling_factor); void update_rotation_values(); // update rotation value after "gizmos" void update_rotation_value(double angle, Axis axis); +#if ENABLE_MODELINSTANCE_3D_ROTATION +void update_rotation_value(const Vec3d& rotation); +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void set_uniform_scaling(const bool uniform_scale); void on_begin_drag(wxDataViewEvent &event); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index bfc8b5623..c66d1fd1f 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -152,20 +152,20 @@ bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, Print* prin bind_event_handlers(); // sets colors for gcode preview extrusion roles - std::vector extrusion_roles_colors = { - "FFFF66", // Perimeter - "FFA500", // External perimeter - "0000FF", // Overhang perimeter - "B1302A", // Internal infill - "D732D7", // Solid infill - "FF1A1A", // Top solid infill - "9999FF", // Bridge infill - "FFFFFF", // Gap fill - "845321", // Skirt - "00FF00", // Support material - "008000", // Support material interface - "B3E3AB", // Wipe tower - "28CC94" // Custom + std::vector extrusion_roles_colors = { + "Perimeter", "FFFF66", + "External perimeter", "FFA500", + "Overhang perimeter", "0000FF", + "Internal infill", "B1302A", + "Solid infill", "D732D7", + "Top solid infill", "FF1A1A", + "Bridge infill", "9999FF", + "Gap fill", "FFFFFF", + "Skirt", "845321", + "Support material", "00FF00", + "Support material interface", "008000", + "Wipe tower", "B3E3AB", + "Custom", "28CC94" }; m_gcode_preview_data->set_extrusion_paths_colors(extrusion_roles_colors); diff --git a/src/slic3r/GUI/ProgressIndicator.hpp b/src/slic3r/GUI/ProgressIndicator.hpp index 0cf8b4a17..280ba63ac 100644 --- a/src/slic3r/GUI/ProgressIndicator.hpp +++ b/src/slic3r/GUI/ProgressIndicator.hpp @@ -14,31 +14,31 @@ public: using CancelFn = std::function; // Cancel function signature. private: - float state_ = .0f, max_ = 1.f, step_; - CancelFn cancelfunc_ = [](){}; + float m_state = .0f, m_max = 1.f, m_step; + CancelFn m_cancelfunc = [](){}; public: inline virtual ~ProgressIndicator() {} /// Get the maximum of the progress range. - float max() const { return max_; } + float max() const { return m_max; } /// Get the current progress state - float state() const { return state_; } + float state() const { return m_state; } /// Set the maximum of the progress range - virtual void max(float maxval) { max_ = maxval; } + virtual void max(float maxval) { m_max = maxval; } /// Set the current state of the progress. - virtual void state(float val) { state_ = val; } + virtual void state(float val) { m_state = val; } /** * @brief Number of states int the progress. Can be used instead of giving a * maximum value. */ virtual void states(unsigned statenum) { - step_ = max_ / statenum; + m_step = m_max / statenum; } /// Message shown on the next status update. @@ -51,13 +51,13 @@ public: virtual void message_fmt(const std::string& fmt, ...); /// Set up a cancel callback for the operation if feasible. - virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; } + virtual void on_cancel(CancelFn func = CancelFn()) { m_cancelfunc = func; } /** * Explicitly shut down the progress indicator and call the associated * callback. */ - virtual void cancel() { cancelfunc_(); } + virtual void cancel() { m_cancelfunc(); } /// Convenience function to call message and status update in one function. void update(float st, const std::string& msg) { diff --git a/src/slic3r/GUI/ProgressStatusBar.cpp b/src/slic3r/GUI/ProgressStatusBar.cpp index 363e34cb2..0ca86084b 100644 --- a/src/slic3r/GUI/ProgressStatusBar.cpp +++ b/src/slic3r/GUI/ProgressStatusBar.cpp @@ -14,117 +14,117 @@ namespace Slic3r { ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id): self(new wxStatusBar(parent ? parent : GUI::get_main_frame(), id == -1? wxID_ANY : id)), - timer_(new wxTimer(self)), - prog_ (new wxGauge(self, + m_timer(new wxTimer(self)), + m_prog (new wxGauge(self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize)), - cancelbutton_(new wxButton(self, + m_cancelbutton(new wxButton(self, -1, "Cancel", wxDefaultPosition, wxDefaultSize)) { - prog_->Hide(); - cancelbutton_->Hide(); + m_prog->Hide(); + m_cancelbutton->Hide(); self->SetFieldsCount(3); int w[] = {-1, 150, 155}; self->SetStatusWidths(3, w); self->Bind(wxEVT_TIMER, [this](const wxTimerEvent&) { - if (prog_->IsShown()) timer_->Stop(); - if(is_busy()) prog_->Pulse(); + if (m_prog->IsShown()) m_timer->Stop(); + if(is_busy()) m_prog->Pulse(); }); self->Bind(wxEVT_SIZE, [this](wxSizeEvent& event){ wxRect rect; self->GetFieldRect(1, rect); auto offset = 0; - cancelbutton_->Move(rect.GetX() + offset, rect.GetY() + offset); - cancelbutton_->SetSize(rect.GetWidth() - offset, rect.GetHeight()); + m_cancelbutton->Move(rect.GetX() + offset, rect.GetY() + offset); + m_cancelbutton->SetSize(rect.GetWidth() - offset, rect.GetHeight()); self->GetFieldRect(2, rect); - prog_->Move(rect.GetX() + offset, rect.GetY() + offset); - prog_->SetSize(rect.GetWidth() - offset, rect.GetHeight()); + m_prog->Move(rect.GetX() + offset, rect.GetY() + offset); + m_prog->SetSize(rect.GetWidth() - offset, rect.GetHeight()); event.Skip(); }); - cancelbutton_->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) { - if(cancel_cb_) cancel_cb_(); + m_cancelbutton->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) { + if(m_cancel_cb) m_cancel_cb(); m_perl_cancel_callback.call(); - cancelbutton_->Hide(); + m_cancelbutton->Hide(); }); } ProgressStatusBar::~ProgressStatusBar() { - if(timer_->IsRunning()) timer_->Stop(); + if(m_timer->IsRunning()) m_timer->Stop(); } int ProgressStatusBar::get_progress() const { - return prog_->GetValue(); + return m_prog->GetValue(); } void ProgressStatusBar::set_progress(int val) { - if(!prog_->IsShown()) show_progress(true); + if(!m_prog->IsShown()) show_progress(true); - if(val == prog_->GetRange()) { - prog_->SetValue(0); + if(val == m_prog->GetRange()) { + m_prog->SetValue(0); show_progress(false); } else { - prog_->SetValue(val); + m_prog->SetValue(val); } } int ProgressStatusBar::get_range() const { - return prog_->GetRange(); + return m_prog->GetRange(); } void ProgressStatusBar::set_range(int val) { - if(val != prog_->GetRange()) { - prog_->SetRange(val); + if(val != m_prog->GetRange()) { + m_prog->SetRange(val); } } void ProgressStatusBar::show_progress(bool show) { - prog_->Show(show); - prog_->Pulse(); + m_prog->Show(show); + m_prog->Pulse(); } void ProgressStatusBar::start_busy(int rate) { - busy_ = true; + m_busy = true; show_progress(true); - if (!timer_->IsRunning()) { - timer_->Start(rate); + if (!m_timer->IsRunning()) { + m_timer->Start(rate); } } void ProgressStatusBar::stop_busy() { - timer_->Stop(); + m_timer->Stop(); show_progress(false); - prog_->SetValue(0); - busy_ = false; + m_prog->SetValue(0); + m_busy = false; } void ProgressStatusBar::set_cancel_callback(ProgressStatusBar::CancelFn ccb) { - cancel_cb_ = ccb; - if(ccb) cancelbutton_->Show(); - else cancelbutton_->Hide(); + m_cancel_cb = ccb; + if(ccb) m_cancelbutton->Show(); + else m_cancelbutton->Hide(); } void ProgressStatusBar::run(int rate) { - if(!timer_->IsRunning()) { - timer_->Start(rate); + if(!m_timer->IsRunning()) { + m_timer->Start(rate); } } @@ -141,12 +141,12 @@ void ProgressStatusBar::set_status_text(const wxString& txt) void ProgressStatusBar::show_cancel_button() { - cancelbutton_->Show(); + m_cancelbutton->Show(); } void ProgressStatusBar::hide_cancel_button() { - cancelbutton_->Hide(); + m_cancelbutton->Hide(); } } diff --git a/src/slic3r/GUI/ProgressStatusBar.hpp b/src/slic3r/GUI/ProgressStatusBar.hpp index 7c2171a5e..57ec9ae2b 100644 --- a/src/slic3r/GUI/ProgressStatusBar.hpp +++ b/src/slic3r/GUI/ProgressStatusBar.hpp @@ -24,9 +24,9 @@ namespace Slic3r { */ class ProgressStatusBar { wxStatusBar *self; // we cheat! It should be the base class but: perl! - wxTimer *timer_; - wxGauge *prog_; - wxButton *cancelbutton_; + wxTimer *m_timer; + wxGauge *m_prog; + wxButton *m_cancelbutton; public: /// Cancel callback function type @@ -42,7 +42,7 @@ public: void show_progress(bool); void start_busy(int = 100); void stop_busy(); - inline bool is_busy() const { return busy_; } + inline bool is_busy() const { return m_busy; } void set_cancel_callback(CancelFn = CancelFn()); inline void remove_cancel_callback() { set_cancel_callback(); } void run(int rate); @@ -55,8 +55,8 @@ public: PerlCallback m_perl_cancel_callback; private: - bool busy_ = false; - CancelFn cancel_cb_; + bool m_busy = false; + CancelFn m_cancel_cb; }; namespace GUI { diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index 22e2ff1d7..a1e3e4670 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -35,6 +35,12 @@ bool is_windows10() void set_wxapp(SV *ui) %code%{ Slic3r::GUI::set_wxapp((wxApp*)wxPli_sv_2_object(aTHX_ ui, "Wx::App")); %}; +void set_gui_appctl() + %code%{ Slic3r::GUI::set_gui_appctl(); %}; + +void set_cli_appctl() + %code%{ Slic3r::GUI::set_cli_appctl(); %}; + void set_progress_status_bar(ProgressStatusBar *prs) %code%{ Slic3r::GUI::set_progress_status_bar(prs); %}; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index c3e4ba3b7..0a3d11ed6 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -651,6 +651,13 @@ register_on_gizmo_rotate_callback(canvas, callback) CODE: _3DScene::register_on_gizmo_rotate_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_on_gizmo_rotate_3D_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_gizmo_rotate_3D_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + void register_on_gizmo_flatten_callback(canvas, callback) SV *canvas; @@ -658,6 +665,13 @@ register_on_gizmo_flatten_callback(canvas, callback) CODE: _3DScene::register_on_gizmo_flatten_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_on_gizmo_flatten_3D_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_gizmo_flatten_3D_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + void register_on_update_geometry_info_callback(canvas, callback) SV *canvas; diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 101faf564..892ecd861 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -364,8 +364,13 @@ ModelMaterial::attributes() Ref object() %code%{ RETVAL = THIS->get_object(); %}; +#if ENABLE_MODELINSTANCE_3D_ROTATION + double rotation() + %code%{ RETVAL = THIS->get_rotation(Z); %}; +#else double rotation() %code%{ RETVAL = THIS->rotation; %}; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION double scaling_factor() %code%{ RETVAL = THIS->scaling_factor; %}; #if ENABLE_MODELINSTANCE_3D_OFFSET @@ -376,8 +381,17 @@ ModelMaterial::attributes() %code%{ RETVAL = &THIS->offset; %}; #endif // ENABLE_MODELINSTANCE_3D_OFFSET +#if ENABLE_MODELINSTANCE_3D_ROTATION + void set_rotation(double val) + %code%{ THIS->set_rotation(Z, val); THIS->get_object()->invalidate_bounding_box(); %}; + + void set_rotations(Vec3d *rotation) + %code%{ THIS->set_rotation(*rotation); THIS->get_object()->invalidate_bounding_box(); %}; + +#else void set_rotation(double val) %code%{ THIS->rotation = val; THIS->get_object()->invalidate_bounding_box(); %}; +#endif // ENABLE_MODELINSTANCE_3D_ROTATION void set_scaling_factor(double val) %code%{ THIS->scaling_factor = val; THIS->get_object()->invalidate_bounding_box(); %}; #if ENABLE_MODELINSTANCE_3D_OFFSET