Cut along X and Y axes too

This commit is contained in:
Alessandro Ranellucci 2016-12-17 19:51:29 +01:00
parent b23e50603e
commit a778cd9820
16 changed files with 346 additions and 130 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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 <cstdio>
#include <string>
@ -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;
}
}

View File

@ -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;
}

View File

@ -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];

View File

@ -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<X>(&volume->mesh).cut(z, &upper_mesh, &lower_mesh);
} else if (axis == Y) {
TriangleMeshSlicer<Y>(&volume->mesh).cut(z, &upper_mesh, &lower_mesh);
} else if (axis == Z) {
TriangleMeshSlicer<Z>(&volume->mesh).cut(z, &upper_mesh, &lower_mesh);
}
upper_mesh.repair();
lower_mesh.repair();
upper_mesh.reset_repair_stats();

View File

@ -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;

View File

@ -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.";

View File

@ -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);

View File

@ -49,7 +49,7 @@ SLAPrint::slice()
slice_z.push_back(this->layers[i].slice_z);
std::vector<ExPolygons> slices;
TriangleMeshSlicer(&mesh).slice(slice_z, &slices);
TriangleMeshSlicer<Z>(&mesh).slice(slice_z, &slices);
for (size_t i = 0; i < slices.size(); ++i)
this->layers[i].slices.expolygons = slices[i];

View File

@ -477,8 +477,9 @@ TriangleMesh::extrude_tin(float offset)
this->repair();
}
template <Axis A>
void
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
TriangleMeshSlicer<A>::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
{
/*
This method gets called with a list of unscaled Z coordinates and outputs
@ -513,7 +514,7 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
parallelize<int>(
0,
this->mesh->stl.stats.number_of_facets-1,
boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z)
boost::bind(&TriangleMeshSlicer<A>::_slice_do, this, _1, &lines, &lines_mutex, z)
);
}
@ -524,25 +525,26 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
parallelize<size_t>(
0,
lines.size()-1,
boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers)
boost::bind(&TriangleMeshSlicer<A>::_make_loops_do, this, _1, &lines, layers)
);
}
template <Axis A>
void
TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex,
TriangleMeshSlicer<A>::_slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex,
const std::vector<float> &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<IntersectionLines>*
}
}
template <Axis A>
void
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
TriangleMeshSlicer<A>::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
{
std::vector<Polygons> layers_p;
this->slice(z, &layers_p);
@ -577,8 +580,20 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>*
}
}
template <Axis A>
void
TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx,
TriangleMeshSlicer<A>::slice(float z, ExPolygons* slices) const
{
std::vector<float> zz;
zz.push_back(z);
std::vector<ExPolygons> layers;
this->slice(zz, &layers);
append_to(*slices, layers.front());
}
template <Axis A>
void
TriangleMeshSlicer<A>::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx,
const float &min_z, const float &max_z, std::vector<IntersectionLine>* 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 <Axis A>
void
TriangleMeshSlicer::_make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const
TriangleMeshSlicer<A>::_make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const
{
this->make_loops((*lines)[i], &(*layers)[i]);
}
template <Axis A>
void
TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
TriangleMeshSlicer<A>::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
{
/*
SVG svg("lines.svg");
@ -847,8 +864,9 @@ class _area_comp {
std::vector<double>* abs_area;
};
template <Axis A>
void
TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
TriangleMeshSlicer<A>::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
{
Polygons loops;
this->make_loops(lines, &loops);
@ -881,8 +899,9 @@ TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines,
}
}
template <Axis A>
void
TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const
TriangleMeshSlicer<A>::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 <Axis A>
void
TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
TriangleMeshSlicer<A>::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
{
Polygons pp;
this->make_loops(lines, &pp);
this->make_expolygons(pp, slices);
}
template <Axis A>
void
TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const
TriangleMeshSlicer<A>::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 <Axis A>
TriangleMeshSlicer<A>::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 <Axis A>
TriangleMeshSlicer<A>::~TriangleMeshSlicer()
{
if (this->v_scaled_shared != NULL) free(this->v_scaled_shared);
}
template class TriangleMeshSlicer<X>;
template class TriangleMeshSlicer<Y>;
template class TriangleMeshSlicer<Z>;
}

View File

@ -14,7 +14,7 @@
namespace Slic3r {
class TriangleMesh;
class TriangleMeshSlicer;
template <Axis A> class TriangleMeshSlicer;
typedef std::vector<TriangleMesh*> TriangleMeshPtrs;
class TriangleMesh
@ -61,7 +61,9 @@ class TriangleMesh
private:
void require_shared_vertices();
friend class TriangleMeshSlicer;
friend class TriangleMeshSlicer<X>;
friend class TriangleMeshSlicer<Y>;
friend class TriangleMeshSlicer<Z>;
};
enum FacetEdgeType { feNone, feTop, feBottom, feHorizontal };
@ -88,6 +90,7 @@ class IntersectionLine : public Line
typedef std::vector<IntersectionLine> IntersectionLines;
typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
template <Axis A>
class TriangleMeshSlicer
{
public:
@ -96,11 +99,15 @@ class TriangleMeshSlicer
~TriangleMeshSlicer();
void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const;
void slice(const std::vector<float> &z, std::vector<ExPolygons>* 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<IntersectionLine>* 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<int> > 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<IntersectionLine> &lines, ExPolygons* slices) const;
void make_expolygons(std::vector<IntersectionLine> &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>::_x(stl_vertex &vertex) const { return vertex.y; }
template<> inline float& TriangleMeshSlicer<X>::_y(stl_vertex &vertex) const { return vertex.z; }
template<> inline float& TriangleMeshSlicer<X>::_z(stl_vertex &vertex) const { return vertex.x; }
template<> inline float const& TriangleMeshSlicer<X>::_x(stl_vertex const &vertex) const { return vertex.y; }
template<> inline float const& TriangleMeshSlicer<X>::_y(stl_vertex const &vertex) const { return vertex.z; }
template<> inline float const& TriangleMeshSlicer<X>::_z(stl_vertex const &vertex) const { return vertex.x; }
template<> inline float& TriangleMeshSlicer<Y>::_x(stl_vertex &vertex) const { return vertex.z; }
template<> inline float& TriangleMeshSlicer<Y>::_y(stl_vertex &vertex) const { return vertex.x; }
template<> inline float& TriangleMeshSlicer<Y>::_z(stl_vertex &vertex) const { return vertex.y; }
template<> inline float const& TriangleMeshSlicer<Y>::_x(stl_vertex const &vertex) const { return vertex.z; }
template<> inline float const& TriangleMeshSlicer<Y>::_y(stl_vertex const &vertex) const { return vertex.x; }
template<> inline float const& TriangleMeshSlicer<Y>::_z(stl_vertex const &vertex) const { return vertex.y; }
template<> inline float& TriangleMeshSlicer<Z>::_x(stl_vertex &vertex) const { return vertex.x; }
template<> inline float& TriangleMeshSlicer<Z>::_y(stl_vertex &vertex) const { return vertex.y; }
template<> inline float& TriangleMeshSlicer<Z>::_z(stl_vertex &vertex) const { return vertex.z; }
template<> inline float const& TriangleMeshSlicer<Z>::_x(stl_vertex const &vertex) const { return vertex.x; }
template<> inline float const& TriangleMeshSlicer<Z>::_y(stl_vertex const &vertex) const { return vertex.y; }
template<> inline float const& TriangleMeshSlicer<Z>::_z(stl_vertex const &vertex) const { return vertex.z; }
}
#endif

View File

@ -153,6 +153,8 @@ ModelMaterial::attributes()
void clear_instances();
int instances_count()
%code%{ RETVAL = THIS->instances.size(); %};
Ref<ModelInstance> 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()

View File

@ -185,7 +185,7 @@ TriangleMesh::slice(z)
std::vector<float> z_f(z.begin(), z.end());
std::vector<ExPolygons> layers;
TriangleMeshSlicer mslicer(THIS);
TriangleMeshSlicer<Z> 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<X>(THIS).slice(z, &RETVAL);
} else if (axis == Y) {
TriangleMeshSlicer<Y>(THIS).slice(z, &RETVAL);
} else if (axis == Z) {
TriangleMeshSlicer<Z>(THIS).slice(z, &RETVAL);
}
OUTPUT:
RETVAL
void
TriangleMesh::cut(z, upper, lower)
float z;
TriangleMesh* upper;
TriangleMesh* lower;
CODE:
TriangleMeshSlicer mslicer(THIS);
TriangleMeshSlicer<Z> mslicer(THIS);
mslicer.cut(z, upper, lower);
std::vector<double>