From a778cd9820d7d53f6b0463f512c2dbcac0827eda Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Dec 2016 19:51:29 +0100 Subject: [PATCH] Cut along X and Y axes too --- lib/Slic3r/GUI/3DScene.pm | 47 +++++-- lib/Slic3r/GUI/OptionsGroup/Field.pm | 6 + lib/Slic3r/GUI/Plater.pm | 17 +-- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 73 ++++++++--- src/slic3r.cpp | 42 +++++-- utils/view-mesh.pl | 3 +- xs/src/admesh/stl.h | 10 +- xs/src/libslic3r/Model.cpp | 32 ++++- xs/src/libslic3r/Model.hpp | 3 +- xs/src/libslic3r/PrintConfig.cpp | 18 +++ xs/src/libslic3r/PrintConfig.hpp | 6 + xs/src/libslic3r/SLAPrint.cpp | 2 +- xs/src/libslic3r/TriangleMesh.cpp | 151 +++++++++++++---------- xs/src/libslic3r/TriangleMesh.hpp | 39 +++++- xs/xsp/Model.xsp | 8 +- xs/xsp/TriangleMesh.xsp | 19 ++- 16 files changed, 346 insertions(+), 130 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 7a71375c1..268b5f615 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -23,6 +23,7 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init on_move volumes _sphi _stheta + cutting_plane_axis cutting_plane_z cut_lines_vertices bed_shape @@ -505,19 +506,35 @@ sub select_volume { } sub SetCuttingPlane { - my ($self, $z, $expolygons) = @_; + my ($self, $axis, $z, $expolygons) = @_; + $self->cutting_plane_axis($axis); $self->cutting_plane_z($z); # grow slices in order to display them better $expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1); + my $bb = $self->volumes_bounding_box; + my @verts = (); foreach my $line (map @{$_->lines}, map @$_, @$expolygons) { - push @verts, ( - unscale($line->a->x), unscale($line->a->y), $z, #)) - unscale($line->b->x), unscale($line->b->y), $z, #)) - ); + if ($axis == X) { + push @verts, ( + $bb->x_min + $z, unscale($line->a->x), unscale($line->a->y), #)) + $bb->x_min + $z, unscale($line->b->x), unscale($line->b->y), #)) + ); + } elsif ($axis == Y) { + push @verts, ( + unscale($line->a->y), $bb->y_min + $z, unscale($line->a->x), #)) + unscale($line->b->y), $bb->y_min + $z, unscale($line->b->x), #)) + ); + } else { + push @verts, ( + unscale($line->a->x), unscale($line->a->y), $z, #)) + unscale($line->b->x), unscale($line->b->y), $z, #)) + ); + } + } $self->cut_lines_vertices(OpenGL::Array->new_list(GL_FLOAT, @verts)); } @@ -932,10 +949,22 @@ sub Render { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBegin(GL_QUADS); glColor4f(0.8, 0.8, 0.8, 0.5); - glVertex3f($bb->x_min-20, $bb->y_min-20, $plane_z); - glVertex3f($bb->x_max+20, $bb->y_min-20, $plane_z); - glVertex3f($bb->x_max+20, $bb->y_max+20, $plane_z); - glVertex3f($bb->x_min-20, $bb->y_max+20, $plane_z); + if ($self->cutting_plane_axis == X) { + glVertex3f($bb->x_min+$plane_z, $bb->y_min-20, $bb->z_min-20); + glVertex3f($bb->x_min+$plane_z, $bb->y_max+20, $bb->z_min-20); + glVertex3f($bb->x_min+$plane_z, $bb->y_max+20, $bb->z_max+20); + glVertex3f($bb->x_min+$plane_z, $bb->y_min-20, $bb->z_max+20); + } elsif ($self->cutting_plane_axis == Y) { + glVertex3f($bb->x_min-20, $bb->y_min+$plane_z, $bb->z_min-20); + glVertex3f($bb->x_max+20, $bb->y_min+$plane_z, $bb->z_min-20); + glVertex3f($bb->x_max+20, $bb->y_min+$plane_z, $bb->z_max+20); + glVertex3f($bb->x_min-20, $bb->y_min+$plane_z, $bb->z_max+20); + } elsif ($self->cutting_plane_axis == Z) { + glVertex3f($bb->x_min-20, $bb->y_min-20, $bb->z_min+$plane_z); + glVertex3f($bb->x_max+20, $bb->y_min-20, $bb->z_min+$plane_z); + glVertex3f($bb->x_max+20, $bb->y_max+20, $bb->z_min+$plane_z); + glVertex3f($bb->x_min-20, $bb->y_max+20, $bb->z_min+$plane_z); + } glEnd(); glEnable(GL_CULL_FACE); glDisable(GL_BLEND); diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm index fc17a65e7..d2cf76ce6 100644 --- a/lib/Slic3r/GUI/OptionsGroup/Field.pm +++ b/lib/Slic3r/GUI/OptionsGroup/Field.pm @@ -592,4 +592,10 @@ sub disable { $self->textctrl->SetEditable(0); } +sub set_range { + my ($self, $min, $max) = @_; + + $self->slider->SetRange($min * $self->scale, $max * $self->scale); +} + 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index b4412a256..2fb7c2979 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -812,15 +812,12 @@ sub rotate { if ($axis == Z) { my $new_angle = deg2rad($angle); - $_->set_rotation($new_angle) for @{ $model_object->instances }; + $_->set_rotation($_->rotation + $new_angle) for @{ $model_object->instances }; $object->transform_thumbnail($self->{model}, $obj_idx); } else { # rotation around X and Y needs to be performed on mesh # so we first apply any Z rotation - if ($model_instance->rotation != 0) { - $model_object->rotate($model_instance->rotation, Z); - $_->set_rotation(0) for @{ $model_object->instances }; - } + $model_object->transform_by_instance($model_instance, 1); $model_object->rotate(deg2rad($angle), $axis); # realign object to Z = 0 @@ -847,10 +844,7 @@ sub mirror { my $model_instance = $model_object->instances->[0]; # apply Z rotation before mirroring - if ($model_instance->rotation != 0) { - $model_object->rotate($model_instance->rotation, Z); - $_->set_rotation(0) for @{ $model_object->instances }; - } + $model_object->transform_by_instance($model_instance, 1); $model_object->mirror($axis); $model_object->update_bounding_box; @@ -904,10 +898,7 @@ sub changescale { } # apply Z rotation before scaling - if ($model_instance->rotation != 0) { - $model_object->rotate($model_instance->rotation, Z); - $_->set_rotation(0) for @{ $model_object->instances }; - } + $model_object->transform_by_instance($model_instance, 1); my $versor = [1,1,1]; $versor->[$axis] = $scale/100; diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index cfdeba649..74dcae834 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -6,7 +6,7 @@ use strict; use warnings; use utf8; -use Slic3r::Geometry qw(PI X); +use Slic3r::Geometry qw(PI X Y Z); use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL); use Wx::Event qw(EVT_CLOSE EVT_BUTTON); use base 'Wx::Dialog'; @@ -24,12 +24,16 @@ sub new { # Note whether the window was already closed, so a pending update is not executed. $self->{already_closed} = 0; + $self->{model_object}->transform_by_instance($self->{model_object}->get_instance(0), 1); + # cut options + my $size_z = $self->{model_object}->instance_bounding_box(0)->size->z; $self->{cut_options} = { - z => 0, - keep_upper => 1, + axis => Z, + z => $size_z/2, + keep_upper => 0, keep_lower => 1, - rotate_lower => 1, + rotate_lower => 0, preview => 1, }; @@ -54,13 +58,21 @@ sub new { }, label_width => 120, ); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'axis', + type => 'select', + label => 'Axis', + labels => ['X','Y','Z'], + values => [X,Y,Z], + default => $self->{cut_options}{axis}, + )); $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'z', type => 'slider', label => 'Z', default => $self->{cut_options}{z}, min => 0, - max => $self->{model_object}->bounding_box->size->z, + max => $size_z, full_width => 1, )); { @@ -133,10 +145,10 @@ sub new { # Adjust position / orientation of the split object halves. if ($self->{new_model_objects}{lower}) { - if ($self->{cut_options}{rotate_lower}) { + if ($self->{cut_options}{rotate_lower} && $self->{cut_options}{axis} == Z) { $self->{new_model_objects}{lower}->rotate(PI, X); - $self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0 } + $self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0 } if ($self->{new_model_objects}{upper}) { $self->{new_model_objects}{upper}->center_around_origin; # align to Z = 0 @@ -165,7 +177,15 @@ sub new { sub _mesh_slice_z_pos { my ($self) = @_; - return $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor; + + my $bb = $self->{model_object}->instance_bounding_box(0); + my $z = $self->{cut_options}{axis} == X ? $bb->x_min + : $self->{cut_options}{axis} == Y ? $bb->y_min + : $bb->z_min; + + $z += $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor; + + return $z; } # Only perform live preview if just a single part of the object shall survive. @@ -184,8 +204,8 @@ sub _perform_cut return if $self->{mesh_cut_valid}; my $z = $self->_mesh_slice_z_pos(); - - my ($new_model) = $self->{model_object}->cut($z); + + my ($new_model) = $self->{model_object}->cut($self->{cut_options}{axis}, $z); my ($upper_object, $lower_object) = @{$new_model->objects}; $self->{new_model} = $new_model; $self->{new_model_objects} = {}; @@ -210,12 +230,11 @@ sub _update { # Only recalculate the cut, if the live cut preview is active. my $life_preview_active = $self->_life_preview_active(); $self->_perform_cut() if $life_preview_active; - + { # scale Z down to original size since we're using the transformed mesh for 3D preview # and cut dialog but ModelObject::cut() needs Z without any instance transformation my $z = $self->_mesh_slice_z_pos(); - # update canvas if ($self->{canvas}) { @@ -226,25 +245,38 @@ sub _update { } else { push @objects, $self->{model_object}; } - + # get section contour my @expolygons = (); foreach my $volume (@{$self->{model_object}->volumes}) { next if !$volume->mesh; next if $volume->modifier; - my $expp = $volume->mesh->slice([ $z + $volume->mesh->bounding_box->z_min ])->[0]; + my $expp = $volume->mesh->slice_at($self->{cut_options}{axis}, $z); push @expolygons, @$expp; } + + my $offset = $self->{model_object}->instances->[0]->offset; foreach my $expolygon (@expolygons) { $self->{model_object}->instances->[0]->transform_polygon($_) for @$expolygon; - $expolygon->translate(map Slic3r::Geometry::scale($_), @{ $self->{model_object}->instances->[0]->offset }); + + if ($self->{cut_options}{axis} != X) { + $expolygon->translate(0, Slic3r::Geometry::scale($offset->y)); #) + } + if ($self->{cut_options}{axis} != Y) { + $expolygon->translate(Slic3r::Geometry::scale($offset->x), 0); + } } $self->{canvas}->reset_objects; $self->{canvas}->load_object($_, undef, [0]) for @objects; + + my $plane_z = $self->{cut_options}{z}; + $plane_z += 0.02 if !$self->{cut_options}{keep_upper}; + $plane_z -= 0.02 if !$self->{cut_options}{keep_lower}; $self->{canvas}->SetCuttingPlane( - $self->{cut_options}{z}, + $self->{cut_options}{axis}, + $plane_z, [@expolygons], ); $self->{canvas}->Render; @@ -255,9 +287,16 @@ sub _update { { my $z = $self->{cut_options}{z}; my $optgroup = $self->{optgroup}; + { + my $bb = $self->{model_object}->instance_bounding_box(0); + my $max = $self->{cut_options}{axis} == X ? $bb->size->x + : $self->{cut_options}{axis} == Y ? $bb->size->y ### + : $bb->size->z; + $optgroup->get_field('z')->set_range(0, $max); + } $optgroup->get_field('keep_upper')->toggle(my $have_upper = abs($z - $optgroup->get_option('z')->max) > 0.1); $optgroup->get_field('keep_lower')->toggle(my $have_lower = $z > 0.1); - $optgroup->get_field('rotate_lower')->toggle($z > 0 && $self->{cut_options}{keep_lower}); + $optgroup->get_field('rotate_lower')->toggle($z > 0 && $self->{cut_options}{keep_lower} && $self->{cut_options}{axis} == Z); $optgroup->get_field('preview')->toggle($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower}); # update cut button diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 2c86ed853..f97177187 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -1,8 +1,9 @@ #include "Config.hpp" -#include "Model.hpp" +#include "Geometry.hpp" #include "IO.hpp" -#include "TriangleMesh.hpp" +#include "Model.hpp" #include "SLAPrint.hpp" +#include "TriangleMesh.hpp" #include "libslic3r.h" #include #include @@ -69,7 +70,7 @@ main(const int argc, const char **argv) Model model; // TODO: read other file formats with Model::read_from_file() try { - Slic3r::IO::STL::read(*it, &model); + IO::STL::read(*it, &model); } catch (std::exception &e) { std::cout << *it << ": " << e.what() << std::endl; exit(1); @@ -88,7 +89,7 @@ main(const int argc, const char **argv) (*o)->scale_to_fit(cli_config.scale_to_fit.value); (*o)->scale(cli_config.scale.value); - (*o)->rotate(deg2rad(cli_config.rotate.value), Z); + (*o)->rotate(Geometry::deg2rad(cli_config.rotate.value), Z); } // TODO: handle --merge @@ -105,7 +106,7 @@ main(const int argc, const char **argv) TriangleMesh mesh = model->mesh(); mesh.repair(); - Slic3r::IO::OBJ::write(mesh, outfile); + IO::OBJ::write(mesh, outfile); printf("File exported to %s\n", outfile.c_str()); } else if (cli_config.export_pov) { std::string outfile = cli_config.output.value; @@ -113,7 +114,7 @@ main(const int argc, const char **argv) TriangleMesh mesh = model->mesh(); mesh.repair(); - Slic3r::IO::POV::write(mesh, outfile); + IO::POV::write(mesh, outfile); printf("File exported to %s\n", outfile.c_str()); } else if (cli_config.export_svg) { std::string outfile = cli_config.output.value; @@ -124,8 +125,35 @@ main(const int argc, const char **argv) print.slice(); print.write_svg(outfile); printf("SVG file exported to %s\n", outfile.c_str()); + } else if (cli_config.cut_x > 0 || cli_config.cut_y > 0 || cli_config.cut > 0) { + model->repair(); + model->translate(0, 0, -model->bounding_box().min.z); + + if (!model->objects.empty()) { + // FIXME: cut all objects + Model out; + if (cli_config.cut_x > 0) { + model->objects.front()->cut(X, cli_config.cut_x, &out); + } else if (cli_config.cut_y > 0) { + model->objects.front()->cut(Y, cli_config.cut_y, &out); + } else { + model->objects.front()->cut(Z, cli_config.cut, &out); + } + + ModelObject &upper = *out.objects[0]; + ModelObject &lower = *out.objects[1]; + + if (upper.facets_count() > 0) { + TriangleMesh m = upper.mesh(); + IO::STL::write(m, upper.input_file + "_upper.stl"); + } + if (lower.facets_count() > 0) { + TriangleMesh m = lower.mesh(); + IO::STL::write(m, lower.input_file + "_lower.stl"); + } + } } else { - std::cerr << "error: only --export-svg and --export-obj are currently supported" << std::endl; + std::cerr << "error: command not supported" << std::endl; return 1; } } diff --git a/utils/view-mesh.pl b/utils/view-mesh.pl index 36461534e..91cd20a29 100644 --- a/utils/view-mesh.pl +++ b/utils/view-mesh.pl @@ -12,6 +12,7 @@ BEGIN { use Getopt::Long qw(:config no_auto_abbrev); use Slic3r; +use Slic3r::Geometry qw(Z); use Slic3r::GUI; use Slic3r::GUI::3DScene; $|++; @@ -40,7 +41,7 @@ my %opt = (); $app->{canvas}->load_object($model, 0); $app->{canvas}->set_auto_bed_shape; $app->{canvas}->zoom_to_volumes; - $app->{canvas}->SetCuttingPlane($opt{cut}) if defined $opt{cut}; + $app->{canvas}->SetCuttingPlane(Z, $opt{cut}) if defined $opt{cut}; $app->MainLoop; } diff --git a/xs/src/admesh/stl.h b/xs/src/admesh/stl.h index c27571993..7871c0351 100644 --- a/xs/src/admesh/stl.h +++ b/xs/src/admesh/stl.h @@ -61,15 +61,7 @@ typedef struct { static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect"); #endif -typedef struct { - float x; - float y; - float z; -} stl_normal; - -#ifdef static_assert -static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect"); -#endif +typedef stl_vertex stl_normal; typedef char stl_extra[2]; diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 73adc56c9..bb5b8c89f 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -660,6 +660,24 @@ ModelObject::mirror(const Axis &axis) this->invalidate_bounding_box(); } +void +ModelObject::transform_by_instance(const ModelInstance &instance, bool dont_translate) +{ + this->rotate(instance.rotation, Z); + this->scale(instance.scaling_factor); + if (!dont_translate) + this->translate(instance.offset.x, instance.offset.y, 0); + + for (ModelInstancePtrs::iterator i = this->instances.begin(); i != this->instances.end(); ++i) { + (*i)->rotation -= instance.rotation; + (*i)->scaling_factor /= instance.scaling_factor; + if (!dont_translate) + (*i)->offset.translate(-instance.offset.x, -instance.offset.y); + } + this->origin_translation = Pointf3(0,0,0); + this->invalidate_bounding_box(); +} + size_t ModelObject::materials_count() const { @@ -692,7 +710,7 @@ ModelObject::needed_repair() const } void -ModelObject::cut(coordf_t z, Model* model) const +ModelObject::cut(Axis axis, coordf_t z, Model* model) const { // clone this one to duplicate instances, materials etc. ModelObject* upper = model->add_object(*this); @@ -707,10 +725,16 @@ ModelObject::cut(coordf_t z, Model* model) const upper->add_volume(*volume); lower->add_volume(*volume); } else { - TriangleMeshSlicer tms(&volume->mesh); TriangleMesh upper_mesh, lower_mesh; - // TODO: shouldn't we use object bounding box instead of per-volume bb? - tms.cut(z + volume->mesh.bounding_box().min.z, &upper_mesh, &lower_mesh); + + if (axis == X) { + TriangleMeshSlicer(&volume->mesh).cut(z, &upper_mesh, &lower_mesh); + } else if (axis == Y) { + TriangleMeshSlicer(&volume->mesh).cut(z, &upper_mesh, &lower_mesh); + } else if (axis == Z) { + TriangleMeshSlicer(&volume->mesh).cut(z, &upper_mesh, &lower_mesh); + } + upper_mesh.repair(); lower_mesh.repair(); upper_mesh.reset_repair_stats(); diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 8d93325f3..16ea81155 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -156,10 +156,11 @@ class ModelObject void scale_to_fit(const Sizef3 &size); void rotate(float angle, const Axis &axis); void mirror(const Axis &axis); + void transform_by_instance(const ModelInstance &instance, bool dont_translate = false); size_t materials_count() const; size_t facets_count() const; bool needed_repair() const; - void cut(coordf_t z, Model* model) const; + void cut(Axis axis, coordf_t z, Model* model) const; void split(ModelObjectPtrs* new_objects); void update_bounding_box(); // this is a private method but we expose it until we need to expose it via XS void print_info() const; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 6eeb6dc69..6ffdfefe6 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1398,6 +1398,24 @@ CLIConfigDef::CLIConfigDef() { ConfigOptionDef* def; + def = this->add("cut", coFloat); + def->label = "Cut"; + def->tooltip = "Cut model at the given Z."; + def->cli = "cut"; + def->default_value = new ConfigOptionFloat(0); + + def = this->add("cut_x", coFloat); + def->label = "Cut"; + def->tooltip = "Cut model at the given X."; + def->cli = "cut-x"; + def->default_value = new ConfigOptionFloat(0); + + def = this->add("cut_y", coFloat); + def->label = "Cut"; + def->tooltip = "Cut model at the given Y."; + def->cli = "cut-y"; + def->default_value = new ConfigOptionFloat(0); + def = this->add("export_obj", coBool); def->label = "Export SVG"; def->tooltip = "Export the model as OBJ."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 9a446cf77..df2c33af3 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -589,6 +589,9 @@ class CLIConfig : public virtual ConfigBase, public StaticConfig { public: + ConfigOptionFloat cut; + ConfigOptionFloat cut_x; + ConfigOptionFloat cut_y; ConfigOptionBool export_obj; ConfigOptionBool export_pov; ConfigOptionBool export_svg; @@ -606,6 +609,9 @@ class CLIConfig }; virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { + OPT_PTR(cut); + OPT_PTR(cut_x); + OPT_PTR(cut_y); OPT_PTR(export_obj); OPT_PTR(export_pov); OPT_PTR(export_svg); diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 2fdf16fb4..57fe64832 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -49,7 +49,7 @@ SLAPrint::slice() slice_z.push_back(this->layers[i].slice_z); std::vector slices; - TriangleMeshSlicer(&mesh).slice(slice_z, &slices); + TriangleMeshSlicer(&mesh).slice(slice_z, &slices); for (size_t i = 0; i < slices.size(); ++i) this->layers[i].slices.expolygons = slices[i]; diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index c0a2063cd..992cd59a7 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -477,8 +477,9 @@ TriangleMesh::extrude_tin(float offset) this->repair(); } +template void -TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const +TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const { /* This method gets called with a list of unscaled Z coordinates and outputs @@ -513,7 +514,7 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la parallelize( 0, this->mesh->stl.stats.number_of_facets-1, - boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z) + boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z) ); } @@ -524,25 +525,26 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la parallelize( 0, lines.size()-1, - boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers) + boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers) ); } +template void -TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector* lines, boost::mutex* lines_mutex, +TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const { const stl_facet &facet = this->mesh->stl.facet_start[facet_idx]; // find facet extents - const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z)); - const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z)); + const float min_z = fminf(_z(facet.vertex[0]), fminf(_z(facet.vertex[1]), _z(facet.vertex[2]))); + const float max_z = fmaxf(_z(facet.vertex[0]), fmaxf(_z(facet.vertex[1]), _z(facet.vertex[2]))); #ifdef SLIC3R_DEBUG - printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, - facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z, - facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z, - facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z); + printf("\n==> FACET %zu (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, + _x(facet.vertex[0]), _y(facet.vertex[0]), _z(facet.vertex[0]), + _x(facet.vertex[1]), _y(facet.vertex[1]), _z(facet.vertex[1]), + _x(facet.vertex[2]), _y(facet.vertex[2]), _z(facet.vertex[2])); printf("z: min = %.2f, max = %.2f\n", min_z, max_z); #endif @@ -560,8 +562,9 @@ TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector* } } +template void -TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const +TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const { std::vector layers_p; this->slice(z, &layers_p); @@ -577,8 +580,20 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* } } +template void -TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, +TriangleMeshSlicer::slice(float z, ExPolygons* slices) const +{ + std::vector zz; + zz.push_back(z); + std::vector layers; + this->slice(zz, &layers); + append_to(*slices, layers.front()); +} + +template +void +TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector* lines, boost::mutex* lines_mutex) const { @@ -590,10 +605,10 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int this is needed to get all intersection lines in a consistent order (external on the right of the line) */ int i = 0; - if (facet.vertex[1].z == min_z) { + if (_z(facet.vertex[1]) == min_z) { // vertex 1 has lowest Z i = 1; - } else if (facet.vertex[2].z == min_z) { + } else if (_z(facet.vertex[2]) == min_z) { // vertex 2 has lowest Z i = 2; } @@ -604,7 +619,7 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int stl_vertex* a = &this->v_scaled_shared[a_id]; stl_vertex* b = &this->v_scaled_shared[b_id]; - if (a->z == b->z && a->z == slice_z) { + if (_z(*a) == _z(*b) && _z(*a) == slice_z) { // edge is horizontal and belongs to the current layer stl_vertex &v0 = this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[0] ]; @@ -613,23 +628,23 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int IntersectionLine line; if (min_z == max_z) { line.edge_type = feHorizontal; - if (this->mesh->stl.facet_start[facet_idx].normal.z < 0) { + if (_z(this->mesh->stl.facet_start[facet_idx].normal) < 0) { /* if normal points downwards this is a bottom horizontal facet so we reverse its point order */ std::swap(a, b); std::swap(a_id, b_id); } - } else if (v0.z < slice_z || v1.z < slice_z || v2.z < slice_z) { + } else if (_z(v0) < slice_z || _z(v1) < slice_z || _z(v2) < slice_z) { line.edge_type = feTop; std::swap(a, b); std::swap(a_id, b_id); } else { line.edge_type = feBottom; } - line.a.x = a->x; - line.a.y = a->y; - line.b.x = b->x; - line.b.y = b->y; + line.a.x = _x(*a); + line.a.y = _y(*a); + line.b.x = _x(*b); + line.b.y = _y(*b); line.a_id = a_id; line.b_id = b_id; if (lines_mutex != NULL) { @@ -645,26 +660,26 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int // because we won't find anything interesting if (line.edge_type != feHorizontal) return; - } else if (a->z == slice_z) { + } else if (_z(*a) == slice_z) { IntersectionPoint point; - point.x = a->x; - point.y = a->y; + point.x = _x(*a); + point.y = _y(*a); point.point_id = a_id; points.push_back(point); points_on_layer.push_back(points.size()-1); - } else if (b->z == slice_z) { + } else if (_z(*b) == slice_z) { IntersectionPoint point; - point.x = b->x; - point.y = b->y; + point.x = _x(*b); + point.y = _y(*b); point.point_id = b_id; points.push_back(point); points_on_layer.push_back(points.size()-1); - } else if ((a->z < slice_z && b->z > slice_z) || (b->z < slice_z && a->z > slice_z)) { + } else if ((_z(*a) < slice_z && _z(*b) > slice_z) || (_z(*b) < slice_z && _z(*a) > slice_z)) { // edge intersects the current layer; calculate intersection IntersectionPoint point; - point.x = b->x + (a->x - b->x) * (slice_z - b->z) / (a->z - b->z); - point.y = b->y + (a->y - b->y) * (slice_z - b->z) / (a->z - b->z); + point.x = _x(*b) + (_x(*a) - _x(*b)) * (slice_z - _z(*b)) / (_z(*a) - _z(*b)); + point.y = _y(*b) + (_y(*a) - _y(*b)) * (slice_z - _z(*b)) / (_z(*a) - _z(*b)); point.edge_id = edge_id; points.push_back(point); } @@ -700,14 +715,16 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int } } +template void -TriangleMeshSlicer::_make_loops_do(size_t i, std::vector* lines, std::vector* layers) const +TriangleMeshSlicer::_make_loops_do(size_t i, std::vector* lines, std::vector* layers) const { this->make_loops((*lines)[i], &(*layers)[i]); } +template void -TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) const +TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) const { /* SVG svg("lines.svg"); @@ -847,8 +864,9 @@ class _area_comp { std::vector* abs_area; }; +template void -TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, ExPolygons* slices) const +TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, ExPolygons* slices) const { Polygons loops; this->make_loops(lines, &loops); @@ -881,8 +899,9 @@ TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, } } +template void -TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const +TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const { /* Input loops are not suitable for evenodd nor nonzero fill types, as we might get @@ -944,26 +963,28 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) c slices->insert(slices->end(), ex_slices.begin(), ex_slices.end()); } +template void -TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPolygons* slices) const +TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPolygons* slices) const { Polygons pp; this->make_loops(lines, &pp); this->make_expolygons(pp, slices); } +template void -TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const +TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const { IntersectionLines upper_lines, lower_lines; - float scaled_z = scale_(z); + const float scaled_z = scale_(z); for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; // find facet extents - float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z)); - float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z)); + float min_z = fminf(_z(facet->vertex[0]), fminf(_z(facet->vertex[1]), _z(facet->vertex[2]))); + float max_z = fmaxf(_z(facet->vertex[0]), fmaxf(_z(facet->vertex[1]), _z(facet->vertex[2]))); // intersect facet with cutting plane IntersectionLines lines; @@ -992,9 +1013,9 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const // look for the vertex on whose side of the slicing plane there are no other vertices int isolated_vertex; - if ( (facet->vertex[0].z > z) == (facet->vertex[1].z > z) ) { + if ( (_z(facet->vertex[0]) > z) == (_z(facet->vertex[1]) > z) ) { isolated_vertex = 2; - } else if ( (facet->vertex[1].z > z) == (facet->vertex[2].z > z) ) { + } else if ( (_z(facet->vertex[1]) > z) == (_z(facet->vertex[2]) > z) ) { isolated_vertex = 0; } else { isolated_vertex = 1; @@ -1007,12 +1028,12 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const // intersect v0-v1 and v2-v0 with cutting plane and make new vertices stl_vertex v0v1, v2v0; - v0v1.x = v1->x + (v0->x - v1->x) * (z - v1->z) / (v0->z - v1->z); - v0v1.y = v1->y + (v0->y - v1->y) * (z - v1->z) / (v0->z - v1->z); - v0v1.z = z; - v2v0.x = v2->x + (v0->x - v2->x) * (z - v2->z) / (v0->z - v2->z); - v2v0.y = v2->y + (v0->y - v2->y) * (z - v2->z) / (v0->z - v2->z); - v2v0.z = z; + _x(v0v1) = _x(*v1) + (_x(*v0) - _x(*v1)) * (z - _z(*v1)) / (_z(*v0) - _z(*v1)); + _y(v0v1) = _y(*v1) + (_y(*v0) - _y(*v1)) * (z - _z(*v1)) / (_z(*v0) - _z(*v1)); + _z(v0v1) = z; + _x(v2v0) = _x(*v2) + (_x(*v0) - _x(*v2)) * (z - _z(*v2)) / (_z(*v0) - _z(*v2)); + _y(v2v0) = _y(*v2) + (_y(*v0) - _y(*v2)) * (z - _z(*v2)) / (_z(*v0) - _z(*v2)); + _z(v2v0) = z; // build the triangular facet stl_facet triangle; @@ -1032,7 +1053,7 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const quadrilateral[1].vertex[1] = v2v0; quadrilateral[1].vertex[2] = v0v1; - if (v0->z > z) { + if (_z(*v0) > z) { if (upper != NULL) stl_add_facet(&upper->stl, &triangle); if (lower != NULL) { stl_add_facet(&lower->stl, &quadrilateral[0]); @@ -1064,13 +1085,13 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const Polygon p = *polygon; p.reverse(); stl_facet facet; - facet.normal.x = 0; - facet.normal.y = 0; - facet.normal.z = -1; + _x(facet.normal) = 0; + _y(facet.normal) = 0; + _z(facet.normal) = -1; for (size_t i = 0; i <= 2; ++i) { - facet.vertex[i].x = unscale(p.points[i].x); - facet.vertex[i].y = unscale(p.points[i].y); - facet.vertex[i].z = z; + _x(facet.vertex[i]) = unscale(p.points[i].x); + _y(facet.vertex[i]) = unscale(p.points[i].y); + _z(facet.vertex[i]) = z; } stl_add_facet(&upper->stl, &facet); } @@ -1090,13 +1111,13 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const // convert triangles to facets and append them to mesh for (Polygons::const_iterator polygon = triangles.begin(); polygon != triangles.end(); ++polygon) { stl_facet facet; - facet.normal.x = 0; - facet.normal.y = 0; - facet.normal.z = 1; + _x(facet.normal) = 0; + _y(facet.normal) = 0; + _z(facet.normal) = 1; for (size_t i = 0; i <= 2; ++i) { - facet.vertex[i].x = unscale(polygon->points[i].x); - facet.vertex[i].y = unscale(polygon->points[i].y); - facet.vertex[i].z = z; + _x(facet.vertex[i]) = unscale(polygon->points[i].x); + _y(facet.vertex[i]) = unscale(polygon->points[i].y); + _z(facet.vertex[i]) = z; } stl_add_facet(&lower->stl, &facet); } @@ -1107,7 +1128,8 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const stl_get_size(&(lower->stl)); } -TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL) +template +TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL) { // build a table to map a facet_idx to its three edge indices this->mesh->require_shared_vertices(); @@ -1166,9 +1188,14 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_sca } } -TriangleMeshSlicer::~TriangleMeshSlicer() +template +TriangleMeshSlicer::~TriangleMeshSlicer() { if (this->v_scaled_shared != NULL) free(this->v_scaled_shared); } +template class TriangleMeshSlicer; +template class TriangleMeshSlicer; +template class TriangleMeshSlicer; + } diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index dc8450a51..4a9bb9e5d 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -14,7 +14,7 @@ namespace Slic3r { class TriangleMesh; -class TriangleMeshSlicer; +template class TriangleMeshSlicer; typedef std::vector TriangleMeshPtrs; class TriangleMesh @@ -61,7 +61,9 @@ class TriangleMesh private: void require_shared_vertices(); - friend class TriangleMeshSlicer; + friend class TriangleMeshSlicer; + friend class TriangleMeshSlicer; + friend class TriangleMeshSlicer; }; enum FacetEdgeType { feNone, feTop, feBottom, feHorizontal }; @@ -88,6 +90,7 @@ class IntersectionLine : public Line typedef std::vector IntersectionLines; typedef std::vector IntersectionLinePtrs; +template class TriangleMeshSlicer { public: @@ -96,11 +99,15 @@ class TriangleMeshSlicer ~TriangleMeshSlicer(); void slice(const std::vector &z, std::vector* layers) const; void slice(const std::vector &z, std::vector* layers) const; + void slice(float z, ExPolygons* slices) const; void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector* lines, boost::mutex* lines_mutex = NULL) const; + void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; + static void cut(TriangleMesh* mesh, Axis axis, float z, TriangleMesh* upper, TriangleMesh* lower); + private: typedef std::vector< std::vector > t_facets_edges; t_facets_edges facets_edges; @@ -111,8 +118,36 @@ class TriangleMeshSlicer void make_expolygons(const Polygons &loops, ExPolygons* slices) const; void make_expolygons_simple(std::vector &lines, ExPolygons* slices) const; void make_expolygons(std::vector &lines, ExPolygons* slices) const; + + float& _x(stl_vertex &vertex) const; + float& _y(stl_vertex &vertex) const; + float& _z(stl_vertex &vertex) const; + const float& _x(stl_vertex const &vertex) const; + const float& _y(stl_vertex const &vertex) const; + const float& _z(stl_vertex const &vertex) const; }; +template<> inline float& TriangleMeshSlicer::_x(stl_vertex &vertex) const { return vertex.y; } +template<> inline float& TriangleMeshSlicer::_y(stl_vertex &vertex) const { return vertex.z; } +template<> inline float& TriangleMeshSlicer::_z(stl_vertex &vertex) const { return vertex.x; } +template<> inline float const& TriangleMeshSlicer::_x(stl_vertex const &vertex) const { return vertex.y; } +template<> inline float const& TriangleMeshSlicer::_y(stl_vertex const &vertex) const { return vertex.z; } +template<> inline float const& TriangleMeshSlicer::_z(stl_vertex const &vertex) const { return vertex.x; } + +template<> inline float& TriangleMeshSlicer::_x(stl_vertex &vertex) const { return vertex.z; } +template<> inline float& TriangleMeshSlicer::_y(stl_vertex &vertex) const { return vertex.x; } +template<> inline float& TriangleMeshSlicer::_z(stl_vertex &vertex) const { return vertex.y; } +template<> inline float const& TriangleMeshSlicer::_x(stl_vertex const &vertex) const { return vertex.z; } +template<> inline float const& TriangleMeshSlicer::_y(stl_vertex const &vertex) const { return vertex.x; } +template<> inline float const& TriangleMeshSlicer::_z(stl_vertex const &vertex) const { return vertex.y; } + +template<> inline float& TriangleMeshSlicer::_x(stl_vertex &vertex) const { return vertex.x; } +template<> inline float& TriangleMeshSlicer::_y(stl_vertex &vertex) const { return vertex.y; } +template<> inline float& TriangleMeshSlicer::_z(stl_vertex &vertex) const { return vertex.z; } +template<> inline float const& TriangleMeshSlicer::_x(stl_vertex const &vertex) const { return vertex.x; } +template<> inline float const& TriangleMeshSlicer::_y(stl_vertex const &vertex) const { return vertex.y; } +template<> inline float const& TriangleMeshSlicer::_z(stl_vertex const &vertex) const { return vertex.z; } + } #endif diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 83576c814..0825867be 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -153,6 +153,8 @@ ModelMaterial::attributes() void clear_instances(); int instances_count() %code%{ RETVAL = THIS->instances.size(); %}; + Ref get_instance(int idx) + %code%{ RETVAL = THIS->instances.at(idx); %}; std::string name() %code%{ RETVAL = THIS->name; %}; @@ -187,11 +189,13 @@ ModelMaterial::attributes() %code{% THIS->scale(*versor); %}; void rotate(float angle, Axis axis); void mirror(Axis axis); + void transform_by_instance(ModelInstance* instance, bool dont_translate = false) + %code{% THIS->transform_by_instance(*instance, dont_translate); %}; - Model* cut(double z) + Model* cut(Axis axis, double z) %code%{ RETVAL = new Model(); - THIS->cut(z, RETVAL); + THIS->cut(axis, z, RETVAL); %}; ModelObjectPtrs* split_object() diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 3b8f1022c..ec76d1aff 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -185,7 +185,7 @@ TriangleMesh::slice(z) std::vector z_f(z.begin(), z.end()); std::vector layers; - TriangleMeshSlicer mslicer(THIS); + TriangleMeshSlicer mslicer(THIS); mslicer.slice(z_f, &layers); AV* layers_av = newAV(); @@ -205,13 +205,28 @@ TriangleMesh::slice(z) OUTPUT: RETVAL +ExPolygons +TriangleMesh::slice_at(axis, z) + Axis axis + double z + CODE: + if (axis == X) { + TriangleMeshSlicer(THIS).slice(z, &RETVAL); + } else if (axis == Y) { + TriangleMeshSlicer(THIS).slice(z, &RETVAL); + } else if (axis == Z) { + TriangleMeshSlicer(THIS).slice(z, &RETVAL); + } + OUTPUT: + RETVAL + void TriangleMesh::cut(z, upper, lower) float z; TriangleMesh* upper; TriangleMesh* lower; CODE: - TriangleMeshSlicer mslicer(THIS); + TriangleMeshSlicer mslicer(THIS); mslicer.cut(z, upper, lower); std::vector