Refactored Model.cpp/hpp to C++x11 loops,
simplified the mesh / bbox handling.
This commit is contained in:
bubnikv 2017-06-13 11:35:24 +02:00
parent 21ddcb8487
commit 5cae4cc614
13 changed files with 314 additions and 500 deletions

View File

@ -547,7 +547,8 @@ sub mouse_wheel_event {
$zoom /= 10; $zoom /= 10;
$zoom = $self->_zoom / (1-$zoom); $zoom = $self->_zoom / (1-$zoom);
# Don't allow to zoom too far outside the scene. # Don't allow to zoom too far outside the scene.
my $zoom_min = $self->get_zoom_to_bounding_box_factor($self->max_bounding_box) * 0.4; my $zoom_min = $self->get_zoom_to_bounding_box_factor($self->max_bounding_box);
$zoom_min *= 0.4 if defined $zoom_min;
$zoom = $zoom_min if defined $zoom_min && $zoom < $zoom_min; $zoom = $zoom_min if defined $zoom_min && $zoom < $zoom_min;
$self->_zoom($zoom); $self->_zoom($zoom);

View File

@ -290,7 +290,7 @@ sub _load_stl {
$dialog->Destroy; $dialog->Destroy;
my $model = Slic3r::Model->read_from_file($input_file); my $model = Slic3r::Model->read_from_file($input_file);
my $mesh = $model->raw_mesh; my $mesh = $model->mesh;
my $expolygons = $mesh->horizontal_projection; my $expolygons = $mesh->horizontal_projection;
if (@$expolygons == 0) { if (@$expolygons == 0) {

View File

@ -707,7 +707,7 @@ sub load_file {
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
my $model = eval { Slic3r::Model->read_from_file($input_file) }; my $model = eval { Slic3r::Model->read_from_file($input_file, 0) };
Slic3r::GUI::show_error($self, $@) if $@; Slic3r::GUI::show_error($self, $@) if $@;
my @obj_idx = (); my @obj_idx = ();
@ -1007,7 +1007,6 @@ sub rotate {
$self->reset_thumbnail($obj_idx); $self->reset_thumbnail($obj_idx);
} }
$model_object->update_bounding_box;
# update print and start background processing # update print and start background processing
$self->{print}->add_model_object($model_object, $obj_idx); $self->{print}->add_model_object($model_object, $obj_idx);
@ -1032,7 +1031,6 @@ sub mirror {
} }
$model_object->mirror($axis); $model_object->mirror($axis);
$model_object->update_bounding_box;
# realign object to Z = 0 # realign object to Z = 0
$model_object->center_around_origin; $model_object->center_around_origin;
@ -1112,7 +1110,6 @@ sub changescale {
$_->set_scaling_factor($scale) for @{ $model_object->instances }; $_->set_scaling_factor($scale) for @{ $model_object->instances };
$object->transform_thumbnail($self->{model}, $obj_idx); $object->transform_thumbnail($self->{model}, $obj_idx);
} }
$model_object->update_bounding_box;
# update print and start background processing # update print and start background processing
$self->stop_background_process; $self->stop_background_process;
@ -1133,6 +1130,7 @@ sub arrange {
# ignore arrange failures on purpose: user has visual feedback and we don't need to warn him # ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
# when parts don't fit in print bed # when parts don't fit in print bed
# Force auto center of the aligned grid of of objects on the print bed.
$self->update(1); $self->update(1);
} }
@ -2198,6 +2196,7 @@ sub make_thumbnail {
# make method idempotent # make method idempotent
$self->thumbnail->clear; $self->thumbnail->clear;
# raw_mesh is the non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
my $mesh = $model->objects->[$obj_idx]->raw_mesh; my $mesh = $model->objects->[$obj_idx]->raw_mesh;
#FIXME The "correct" variant could be extremely slow. #FIXME The "correct" variant could be extremely slow.
# if ($mesh->facets_count <= 5000) { # if ($mesh->facets_count <= 5000) {

View File

@ -246,7 +246,6 @@ sub mouse_event {
unscale($point->[X] - $self->{drag_start_pos}[X]), unscale($point->[X] - $self->{drag_start_pos}[X]),
unscale($point->[Y] - $self->{drag_start_pos}[Y]), unscale($point->[Y] - $self->{drag_start_pos}[Y]),
)); ));
$model_object->update_bounding_box;
$self->Refresh; $self->Refresh;
} elsif ($event->Moving) { } elsif ($event->Moving) {
my $cursor = wxSTANDARD_CURSOR; my $cursor = wxSTANDARD_CURSOR;

View File

@ -6,8 +6,8 @@ use List::Util qw(first max any);
use Slic3r::Geometry qw(X Y Z move_points); use Slic3r::Geometry qw(X Y Z move_points);
sub read_from_file { sub read_from_file {
my $class = shift; my ($class, $input_file, $add_default_instances) = @_;
my ($input_file) = @_; $add_default_instances //= 1;
my $model = $input_file =~ /\.stl$/i ? Slic3r::Model->load_stl(Slic3r::encode_path($input_file), basename($input_file)) my $model = $input_file =~ /\.stl$/i ? Slic3r::Model->load_stl(Slic3r::encode_path($input_file), basename($input_file))
: $input_file =~ /\.obj$/i ? Slic3r::Model->load_obj(Slic3r::encode_path($input_file), basename($input_file)) : $input_file =~ /\.obj$/i ? Slic3r::Model->load_obj(Slic3r::encode_path($input_file), basename($input_file))
@ -19,6 +19,7 @@ sub read_from_file {
if $model->objects_count == 0; if $model->objects_count == 0;
$_->set_input_file($input_file) for @{$model->objects}; $_->set_input_file($input_file) for @{$model->objects};
$model->add_default_instances if $add_default_instances;
return $model; return $model;
} }

View File

@ -144,7 +144,6 @@ if (@ARGV) { # slicing from command line
foreach my $file (@ARGV) { foreach my $file (@ARGV) {
$file = Slic3r::decode_path($file); $file = Slic3r::decode_path($file);
my $model = Slic3r::Model->read_from_file($file); my $model = Slic3r::Model->read_from_file($file);
$model->add_default_instances;
my $mesh = $model->mesh; my $mesh = $model->mesh;
$mesh->translate(0, 0, -$mesh->bounding_box->z_min); $mesh->translate(0, 0, -$mesh->bounding_box->z_min);
my $upper = Slic3r::TriangleMesh->new; my $upper = Slic3r::TriangleMesh->new;
@ -164,7 +163,6 @@ if (@ARGV) { # slicing from command line
foreach my $file (@ARGV) { foreach my $file (@ARGV) {
$file = Slic3r::decode_path($file); $file = Slic3r::decode_path($file);
my $model = Slic3r::Model->read_from_file($file); my $model = Slic3r::Model->read_from_file($file);
$model->add_default_instances;
my $mesh = $model->mesh; my $mesh = $model->mesh;
$mesh->repair; $mesh->repair;

View File

@ -28,9 +28,6 @@ my %opt = ();
{ {
my $model = Slic3r::Model->read_from_file($ARGV[0]); my $model = Slic3r::Model->read_from_file($ARGV[0]);
# make sure all objects have at least one defined instance
$model->add_default_instances;
$_->center_around_origin for @{$model->objects}; # and align to Z = 0 $_->center_around_origin for @{$model->objects}; # and align to Z = 0
my $app = Slic3r::ViewMesh->new; my $app = Slic3r::ViewMesh->new;

View File

@ -38,7 +38,6 @@ my %opt = (
{ {
# load model # load model
my $model = Slic3r::Model->read_from_file($ARGV[0]); my $model = Slic3r::Model->read_from_file($ARGV[0]);
$model->add_default_instances;
$model->center_instances_around_point(Slic3r::Pointf->new(100,100)); $model->center_instances_around_point(Slic3r::Pointf->new(100,100));
my $mesh = $model->mesh; my $mesh = $model->mesh;
$mesh->translate(0, 0, -$mesh->bounding_box->z_min); $mesh->translate(0, 0, -$mesh->bounding_box->z_min);

View File

@ -3,28 +3,24 @@
namespace Slic3r { namespace Slic3r {
Model::Model() {}
Model::Model(const Model &other) Model::Model(const Model &other)
{ {
// copy materials // copy materials
for (ModelMaterialMap::const_iterator i = other.materials.begin(); i != other.materials.end(); ++i) for (const auto &m : other.materials)
this->add_material(i->first, *i->second); this->add_material(m.first, *m.second);
// copy objects // copy objects
this->objects.reserve(other.objects.size()); this->objects.reserve(other.objects.size());
for (ModelObjectPtrs::const_iterator i = other.objects.begin(); i != other.objects.end(); ++i) for (const ModelObject *o : other.objects)
this->add_object(**i, true); this->add_object(*o, true);
} }
Model& Model::operator= (Model other) Model& Model::operator=(Model other)
{ {
this->swap(other); this->swap(other);
return *this; return *this;
} }
void void Model::swap(Model &other)
Model::swap(Model &other)
{ {
std::swap(this->materials, other.materials); std::swap(this->materials, other.materials);
std::swap(this->objects, other.objects); std::swap(this->objects, other.objects);
@ -36,16 +32,13 @@ Model::~Model()
this->clear_materials(); this->clear_materials();
} }
ModelObject* ModelObject* Model::add_object()
Model::add_object()
{ {
ModelObject* new_object = new ModelObject(this); this->objects.emplace_back(new ModelObject(this));
this->objects.push_back(new_object); return this->objects.back();
return new_object;
} }
ModelObject* ModelObject* Model::add_object(const char *name, const char *path, const TriangleMesh &mesh)
Model::add_object(const char *name, const char *path, const TriangleMesh &mesh)
{ {
ModelObject* new_object = new ModelObject(this); ModelObject* new_object = new ModelObject(this);
this->objects.push_back(new_object); this->objects.push_back(new_object);
@ -57,8 +50,7 @@ Model::add_object(const char *name, const char *path, const TriangleMesh &mesh)
return new_object; return new_object;
} }
ModelObject* ModelObject* Model::add_object(const char *name, const char *path, TriangleMesh &&mesh)
Model::add_object(const char *name, const char *path, TriangleMesh &&mesh)
{ {
ModelObject* new_object = new ModelObject(this); ModelObject* new_object = new ModelObject(this);
this->objects.push_back(new_object); this->objects.push_back(new_object);
@ -70,32 +62,28 @@ Model::add_object(const char *name, const char *path, TriangleMesh &&mesh)
return new_object; return new_object;
} }
ModelObject* ModelObject* Model::add_object(const ModelObject &other, bool copy_volumes)
Model::add_object(const ModelObject &other, bool copy_volumes)
{ {
ModelObject* new_object = new ModelObject(this, other, copy_volumes); ModelObject* new_object = new ModelObject(this, other, copy_volumes);
this->objects.push_back(new_object); this->objects.push_back(new_object);
return new_object; return new_object;
} }
void void Model::delete_object(size_t idx)
Model::delete_object(size_t idx)
{ {
ModelObjectPtrs::iterator i = this->objects.begin() + idx; ModelObjectPtrs::iterator i = this->objects.begin() + idx;
delete *i; delete *i;
this->objects.erase(i); this->objects.erase(i);
} }
void void Model::clear_objects()
Model::clear_objects()
{ {
// int instead of size_t because it can be -1 when vector is empty for (ModelObject *o : this->objects)
for (int i = this->objects.size()-1; i >= 0; --i) delete o;
this->delete_object(i); this->objects.clear();
} }
void void Model::delete_material(t_model_material_id material_id)
Model::delete_material(t_model_material_id material_id)
{ {
ModelMaterialMap::iterator i = this->materials.find(material_id); ModelMaterialMap::iterator i = this->materials.find(material_id);
if (i != this->materials.end()) { if (i != this->materials.end()) {
@ -104,145 +92,79 @@ Model::delete_material(t_model_material_id material_id)
} }
} }
void void Model::clear_materials()
Model::clear_materials()
{ {
while (!this->materials.empty()) for (auto &m : this->materials)
this->delete_material( this->materials.begin()->first ); delete m.second;
this->materials.clear();
} }
ModelMaterial* ModelMaterial* Model::add_material(t_model_material_id material_id)
Model::add_material(t_model_material_id material_id)
{ {
ModelMaterial* material = this->get_material(material_id); ModelMaterial* material = this->get_material(material_id);
if (material == NULL) { if (material == nullptr)
material = this->materials[material_id] = new ModelMaterial(this); material = this->materials[material_id] = new ModelMaterial(this);
}
return material; return material;
} }
ModelMaterial* ModelMaterial* Model::add_material(t_model_material_id material_id, const ModelMaterial &other)
Model::add_material(t_model_material_id material_id, const ModelMaterial &other)
{ {
// delete existing material if any // delete existing material if any
ModelMaterial* material = this->get_material(material_id); ModelMaterial* material = this->get_material(material_id);
if (material != NULL) { delete material;
delete material;
}
// set new material // set new material
material = new ModelMaterial(this, other); material = new ModelMaterial(this, other);
this->materials[material_id] = material; this->materials[material_id] = material;
return material; return material;
} }
ModelMaterial*
Model::get_material(t_model_material_id material_id)
{
ModelMaterialMap::iterator i = this->materials.find(material_id);
if (i == this->materials.end()) {
return NULL;
} else {
return i->second;
}
}
bool
Model::has_objects_with_no_instances() const
{
for (ModelObjectPtrs::const_iterator i = this->objects.begin();
i != this->objects.end(); ++i)
{
if ((*i)->instances.empty()) {
return true;
}
}
return false;
}
// makes sure all objects have at least one instance // makes sure all objects have at least one instance
bool bool Model::add_default_instances()
Model::add_default_instances()
{ {
// apply a default position to all objects not having one // apply a default position to all objects not having one
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { for (ModelObject *o : this->objects)
if ((*o)->instances.empty()) { if (o->instances.empty())
(*o)->add_instance(); o->add_instance();
}
}
return true; return true;
} }
// this returns the bounding box of the *transformed* instances // this returns the bounding box of the *transformed* instances
BoundingBoxf3 BoundingBoxf3 Model::bounding_box()
Model::bounding_box() const
{ {
BoundingBoxf3 bb; BoundingBoxf3 bb;
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { for (ModelObject *o : this->objects)
bb.merge((*o)->bounding_box()); bb.merge(o->bounding_box());
}
return bb; return bb;
} }
void void Model::center_instances_around_point(const Pointf &point)
Model::center_instances_around_point(const Pointf &point)
{ {
BoundingBoxf3 bb = this->bounding_box(); // BoundingBoxf3 bb = this->bounding_box();
BoundingBoxf3 bb;
for (ModelObject *o : this->objects)
for (size_t i = 0; i < o->instances.size(); ++ i)
bb.merge(o->instance_bounding_box(i, false));
Sizef3 size = bb.size(); Sizef3 size = bb.size();
coordf_t shift_x = -bb.min.x + point.x - size.x/2; coordf_t shift_x = -bb.min.x + point.x - size.x/2;
coordf_t shift_y = -bb.min.y + point.y - size.y/2; coordf_t shift_y = -bb.min.y + point.y - size.y/2;
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { for (ModelObject *o : this->objects) {
for (ModelInstancePtrs::const_iterator i = (*o)->instances.begin(); i != (*o)->instances.end(); ++i) { for (ModelInstance *i : o->instances)
(*i)->offset.translate(shift_x, shift_y); i->offset.translate(shift_x, shift_y);
} o->invalidate_bounding_box();
(*o)->invalidate_bounding_box();
}
}
void
Model::align_instances_to_origin()
{
BoundingBoxf3 bb = this->bounding_box();
Pointf new_center = (Pointf)bb.size();
new_center.translate(-new_center.x/2, -new_center.y/2);
this->center_instances_around_point(new_center);
}
void
Model::translate(coordf_t x, coordf_t y, coordf_t z)
{
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) {
(*o)->translate(x, y, z);
} }
} }
// flattens everything to a single mesh // flattens everything to a single mesh
TriangleMesh TriangleMesh Model::mesh() const
Model::mesh() const
{ {
TriangleMesh mesh; TriangleMesh mesh;
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { for (const ModelObject *o : this->objects)
mesh.merge((*o)->mesh()); mesh.merge(o->mesh());
}
return mesh; return mesh;
} }
// flattens everything to a single mesh static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out)
TriangleMesh
Model::raw_mesh() const
{
TriangleMesh mesh;
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) {
mesh.merge((*o)->raw_mesh());
}
return mesh;
}
bool
Model::_arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out) const
{ {
// we supply unscaled data to arrange() // we supply unscaled data to arrange()
return Slic3r::Geometry::arrange( return Slic3r::Geometry::arrange(
@ -256,75 +178,74 @@ Model::_arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Poi
/* arrange objects preserving their instance count /* arrange objects preserving their instance count
but altering their instance positions */ but altering their instance positions */
bool bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
{ {
// get the (transformed) size of each instance so that we take // get the (transformed) size of each instance so that we take
// into account their different transformations when packing // into account their different transformations when packing
Pointfs instance_sizes; Pointfs instance_sizes;
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { Pointfs instance_centers;
for (size_t i = 0; i < (*o)->instances.size(); ++i) { for (const ModelObject *o : this->objects)
instance_sizes.push_back((*o)->instance_bounding_box(i).size()); for (size_t i = 0; i < o->instances.size(); ++ i) {
// an accurate snug bounding box around the transformed mesh.
BoundingBoxf3 bbox(o->instance_bounding_box(i, true));
instance_sizes.push_back(bbox.size());
instance_centers.push_back(bbox.center());
} }
}
Pointfs positions; Pointfs positions;
if (! this->_arrange(instance_sizes, dist, bb, positions)) if (! _arrange(instance_sizes, dist, bb, positions))
return false; return false;
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { size_t idx = 0;
for (ModelInstancePtrs::const_iterator i = (*o)->instances.begin(); i != (*o)->instances.end(); ++i) { for (ModelObject *o : this->objects) {
(*i)->offset = positions.back(); for (ModelInstance *i : o->instances) {
positions.pop_back(); i->offset = positions[idx] - instance_centers[idx];
++ idx;
} }
(*o)->invalidate_bounding_box(); o->invalidate_bounding_box();
} }
return true; return true;
} }
/* duplicate the entire model preserving instance relative positions */ // Duplicate the entire model preserving instance relative positions.
void void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
{ {
Pointfs model_sizes(copies_num-1, this->bounding_box().size()); Pointfs model_sizes(copies_num-1, this->bounding_box().size());
Pointfs positions; Pointfs positions;
if (! this->_arrange(model_sizes, dist, bb, positions)) if (! _arrange(model_sizes, dist, bb, positions))
CONFESS("Cannot duplicate part as the resulting objects would not fit on the print bed.\n"); CONFESS("Cannot duplicate part as the resulting objects would not fit on the print bed.\n");
// note that this will leave the object count unaltered // note that this will leave the object count unaltered
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { for (ModelObject *o : this->objects) {
// make a copy of the pointers in order to avoid recursion when appending their copies // make a copy of the pointers in order to avoid recursion when appending their copies
ModelInstancePtrs instances = (*o)->instances; ModelInstancePtrs instances = o->instances;
for (ModelInstancePtrs::const_iterator i = instances.begin(); i != instances.end(); ++i) { for (const ModelInstance *i : instances) {
for (Pointfs::const_iterator pos = positions.begin(); pos != positions.end(); ++pos) { for (const Pointf &pos : positions) {
ModelInstance* instance = (*o)->add_instance(**i); ModelInstance *instance = o->add_instance(*i);
instance->offset.translate(*pos); instance->offset.translate(pos);
} }
} }
(*o)->invalidate_bounding_box(); o->invalidate_bounding_box();
} }
} }
/* this will append more instances to each object /* this will append more instances to each object
and then automatically rearrange everything */ and then automatically rearrange everything */
void void Model::duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
Model::duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
{ {
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { for (ModelObject *o : this->objects) {
// make a copy of the pointers in order to avoid recursion when appending their copies // make a copy of the pointers in order to avoid recursion when appending their copies
ModelInstancePtrs instances = (*o)->instances; ModelInstancePtrs instances = o->instances;
for (ModelInstancePtrs::const_iterator i = instances.begin(); i != instances.end(); ++i) { for (const ModelInstance *i : instances)
for (size_t k = 2; k <= copies_num; ++k) for (size_t k = 2; k <= copies_num; ++ k)
(*o)->add_instance(**i); o->add_instance(*i);
}
} }
this->arrange_objects(dist, bb); this->arrange_objects(dist, bb);
} }
void void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
{ {
if (this->objects.size() > 1) throw "Grid duplication is not supported with multiple objects"; if (this->objects.size() > 1) throw "Grid duplication is not supported with multiple objects";
if (this->objects.empty()) throw "No objects!"; if (this->objects.empty()) throw "No objects!";
@ -343,26 +264,8 @@ Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
} }
} }
ModelMaterial::ModelMaterial(Model *model) : model(model) {} ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) :
ModelMaterial::ModelMaterial(Model *model, const ModelMaterial &other) name(other.name),
: attributes(other.attributes), config(other.config), model(model)
{}
void
ModelMaterial::apply(const t_model_material_attributes &attributes)
{
this->attributes.insert(attributes.begin(), attributes.end());
}
ModelObject::ModelObject(Model *model) :
model(model),
_bounding_box_valid(false),
layer_height_profile_valid(false)
{}
ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes)
: name(other.name),
input_file(other.input_file), input_file(other.input_file),
instances(), instances(),
volumes(), volumes(),
@ -371,9 +274,9 @@ ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volum
layer_height_profile(other.layer_height_profile), layer_height_profile(other.layer_height_profile),
layer_height_profile_valid(other.layer_height_profile_valid), layer_height_profile_valid(other.layer_height_profile_valid),
origin_translation(other.origin_translation), origin_translation(other.origin_translation),
_bounding_box(other._bounding_box), m_bounding_box(other.m_bounding_box),
_bounding_box_valid(other._bounding_box_valid), m_bounding_box_valid(other.m_bounding_box_valid),
model(model) m_model(model)
{ {
if (copy_volumes) { if (copy_volumes) {
this->volumes.reserve(other.volumes.size()); this->volumes.reserve(other.volumes.size());
@ -392,8 +295,7 @@ ModelObject& ModelObject::operator=(ModelObject other)
return *this; return *this;
} }
void void ModelObject::swap(ModelObject &other)
ModelObject::swap(ModelObject &other)
{ {
std::swap(this->input_file, other.input_file); std::swap(this->input_file, other.input_file);
std::swap(this->instances, other.instances); std::swap(this->instances, other.instances);
@ -403,8 +305,8 @@ ModelObject::swap(ModelObject &other)
std::swap(this->layer_height_profile, other.layer_height_profile); std::swap(this->layer_height_profile, other.layer_height_profile);
std::swap(this->layer_height_profile_valid, other.layer_height_profile_valid); std::swap(this->layer_height_profile_valid, other.layer_height_profile_valid);
std::swap(this->origin_translation, other.origin_translation); std::swap(this->origin_translation, other.origin_translation);
std::swap(this->_bounding_box, other._bounding_box); std::swap(m_bounding_box, other.m_bounding_box);
std::swap(this->_bounding_box_valid, other._bounding_box_valid); std::swap(m_bounding_box_valid, other.m_bounding_box_valid);
} }
ModelObject::~ModelObject() ModelObject::~ModelObject()
@ -413,8 +315,7 @@ ModelObject::~ModelObject()
this->clear_instances(); this->clear_instances();
} }
ModelVolume* ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh)
ModelObject::add_volume(const TriangleMesh &mesh)
{ {
ModelVolume* v = new ModelVolume(this, mesh); ModelVolume* v = new ModelVolume(this, mesh);
this->volumes.push_back(v); this->volumes.push_back(v);
@ -422,8 +323,7 @@ ModelObject::add_volume(const TriangleMesh &mesh)
return v; return v;
} }
ModelVolume* ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh)
ModelObject::add_volume(TriangleMesh &&mesh)
{ {
ModelVolume* v = new ModelVolume(this, std::move(mesh)); ModelVolume* v = new ModelVolume(this, std::move(mesh));
this->volumes.push_back(v); this->volumes.push_back(v);
@ -431,8 +331,7 @@ ModelObject::add_volume(TriangleMesh &&mesh)
return v; return v;
} }
ModelVolume* ModelVolume* ModelObject::add_volume(const ModelVolume &other)
ModelObject::add_volume(const ModelVolume &other)
{ {
ModelVolume* v = new ModelVolume(this, other); ModelVolume* v = new ModelVolume(this, other);
this->volumes.push_back(v); this->volumes.push_back(v);
@ -440,8 +339,7 @@ ModelObject::add_volume(const ModelVolume &other)
return v; return v;
} }
void void ModelObject::delete_volume(size_t idx)
ModelObject::delete_volume(size_t idx)
{ {
ModelVolumePtrs::iterator i = this->volumes.begin() + idx; ModelVolumePtrs::iterator i = this->volumes.begin() + idx;
delete *i; delete *i;
@ -449,16 +347,15 @@ ModelObject::delete_volume(size_t idx)
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
void void ModelObject::clear_volumes()
ModelObject::clear_volumes()
{ {
// int instead of size_t because it can be -1 when vector is empty for (ModelVolume *v : this->volumes)
for (int i = this->volumes.size()-1; i >= 0; --i) delete v;
this->delete_volume(i); this->volumes.clear();
this->invalidate_bounding_box();
} }
ModelInstance* ModelInstance* ModelObject::add_instance()
ModelObject::add_instance()
{ {
ModelInstance* i = new ModelInstance(this); ModelInstance* i = new ModelInstance(this);
this->instances.push_back(i); this->instances.push_back(i);
@ -466,8 +363,7 @@ ModelObject::add_instance()
return i; return i;
} }
ModelInstance* ModelInstance* ModelObject::add_instance(const ModelInstance &other)
ModelObject::add_instance(const ModelInstance &other)
{ {
ModelInstance* i = new ModelInstance(this, other); ModelInstance* i = new ModelInstance(this, other);
this->instances.push_back(i); this->instances.push_back(i);
@ -475,8 +371,7 @@ ModelObject::add_instance(const ModelInstance &other)
return i; return i;
} }
void void ModelObject::delete_instance(size_t idx)
ModelObject::delete_instance(size_t idx)
{ {
ModelInstancePtrs::iterator i = this->instances.begin() + idx; ModelInstancePtrs::iterator i = this->instances.begin() + idx;
delete *i; delete *i;
@ -484,108 +379,93 @@ ModelObject::delete_instance(size_t idx)
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
void void ModelObject::delete_last_instance()
ModelObject::delete_last_instance()
{ {
this->delete_instance(this->instances.size() - 1); this->delete_instance(this->instances.size() - 1);
} }
void void ModelObject::clear_instances()
ModelObject::clear_instances()
{ {
for (size_t i = 0; i < this->instances.size(); ++i) for (ModelInstance *i : this->instances)
this->delete_instance(i); delete i;
this->instances.clear();
this->invalidate_bounding_box();
} }
// this returns the bounding box of the *transformed* instances // Returns the bounding box of the transformed instances.
BoundingBoxf3 // This bounding box is approximate and not snug.
ModelObject::bounding_box() BoundingBoxf3 ModelObject::bounding_box()
{ {
if (!this->_bounding_box_valid) this->update_bounding_box(); if (! m_bounding_box_valid) {
return this->_bounding_box; BoundingBoxf3 raw_bbox;
} for (const ModelVolume *v : this->volumes)
if (! v->modifier)
void raw_bbox.merge(v->mesh.bounding_box());
ModelObject::invalidate_bounding_box() BoundingBoxf3 bb;
{ for (const ModelInstance *i : this->instances)
this->_bounding_box_valid = false; bb.merge(i->transform_bounding_box(raw_bbox));
} m_bounding_box = bb;
m_bounding_box_valid = true;
void
ModelObject::update_bounding_box()
{
// this->_bounding_box = this->mesh().bounding_box();
BoundingBoxf3 raw_bbox;
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) {
if ((*v)->modifier) continue;
raw_bbox.merge((*v)->mesh.bounding_box());
} }
BoundingBoxf3 bb; return m_bounding_box;
for (ModelInstancePtrs::const_iterator i = this->instances.begin(); i != this->instances.end(); ++i)
bb.merge((*i)->transform_bounding_box(raw_bbox));
this->_bounding_box = bb;
this->_bounding_box_valid = true;
} }
// flattens all volumes and instances into a single mesh // A mesh containing all transformed instances of this object.
TriangleMesh TriangleMesh ModelObject::mesh() const
ModelObject::mesh() const
{ {
TriangleMesh mesh; TriangleMesh mesh;
TriangleMesh raw_mesh = this->raw_mesh(); TriangleMesh raw_mesh = this->raw_mesh();
for (const ModelInstance *i : this->instances) {
for (ModelInstancePtrs::const_iterator i = this->instances.begin(); i != this->instances.end(); ++i) {
TriangleMesh m = raw_mesh; TriangleMesh m = raw_mesh;
(*i)->transform_mesh(&m); i->transform_mesh(&m);
mesh.merge(m); mesh.merge(m);
} }
return mesh; return mesh;
} }
TriangleMesh // Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
ModelObject::raw_mesh() const // Currently used by ModelObject::mesh(), to calculate the 2D envelope for 2D platter
// and to display the object statistics at ModelObject::print_info().
TriangleMesh ModelObject::raw_mesh() const
{ {
TriangleMesh mesh; TriangleMesh mesh;
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { for (const ModelVolume *v : this->volumes)
if ((*v)->modifier) continue; if (! v->modifier)
mesh.merge((*v)->mesh); mesh.merge(v->mesh);
}
return mesh; return mesh;
} }
BoundingBoxf3 // A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
ModelObject::raw_bounding_box() const // This bounding box is only used for the actual slicing.
BoundingBoxf3 ModelObject::raw_bounding_box() const
{ {
BoundingBoxf3 bb; BoundingBoxf3 bb;
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { for (const ModelVolume *v : this->volumes)
if ((*v)->modifier) continue; if (! v->modifier) {
if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances"); if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
bb.merge(this->instances.front()->transform_mesh_bounding_box(&(*v)->mesh, true)); bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true));
} }
return bb; return bb;
} }
// this returns the bounding box of the *transformed* given instance // This returns an accurate snug bounding box of the transformed object instance, without the translation applied.
BoundingBoxf3 BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_translate) const
ModelObject::instance_bounding_box(size_t instance_idx) const
{ {
BoundingBoxf3 bb; BoundingBoxf3 bb;
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { for (ModelVolume *v : this->volumes)
if ((*v)->modifier) continue; if (! v->modifier)
bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&(*v)->mesh, true)); bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate));
}
return bb; return bb;
} }
void void ModelObject::center_around_origin()
ModelObject::center_around_origin()
{ {
// calculate the displacements needed to // calculate the displacements needed to
// center this object around the origin // center this object around the origin
BoundingBoxf3 bb; BoundingBoxf3 bb;
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) for (ModelVolume *v : this->volumes)
if (! (*v)->modifier) if (! v->modifier)
bb.merge((*v)->mesh.bounding_box()); bb.merge(v->mesh.bounding_box());
// first align to origin on XYZ // first align to origin on XYZ
Vectorf3 vector(-bb.min.x, -bb.min.y, -bb.min.z); Vectorf3 vector(-bb.min.x, -bb.min.y, -bb.min.z);
@ -599,98 +479,77 @@ ModelObject::center_around_origin()
this->origin_translation.translate(vector); this->origin_translation.translate(vector);
if (!this->instances.empty()) { if (!this->instances.empty()) {
for (ModelInstancePtrs::const_iterator i = this->instances.begin(); i != this->instances.end(); ++i) { for (ModelInstance *i : this->instances) {
// apply rotation and scaling to vector as well before translating instance, // apply rotation and scaling to vector as well before translating instance,
// in order to leave final position unaltered // in order to leave final position unaltered
Vectorf3 v = vector.negative(); Vectorf3 v = vector.negative();
v.rotate((*i)->rotation, (*i)->offset); v.rotate(i->rotation, i->offset);
v.scale((*i)->scaling_factor); v.scale(i->scaling_factor);
(*i)->offset.translate(v.x, v.y); i->offset.translate(v.x, v.y);
} }
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
} }
void void ModelObject::translate(coordf_t x, coordf_t y, coordf_t z)
ModelObject::translate(const Vectorf3 &vector)
{ {
this->translate(vector.x, vector.y, vector.z); for (ModelVolume *v : this->volumes)
v->mesh.translate(float(x), float(y), float(z));
if (m_bounding_box_valid)
m_bounding_box.translate(x, y, z);
} }
void void ModelObject::scale(const Pointf3 &versor)
ModelObject::translate(coordf_t x, coordf_t y, coordf_t z)
{ {
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { for (ModelVolume *v : this->volumes)
(*v)->mesh.translate(x, y, z); v->mesh.scale(versor);
}
if (this->_bounding_box_valid) this->_bounding_box.translate(x, y, z);
}
void
ModelObject::scale(const Pointf3 &versor)
{
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) {
(*v)->mesh.scale(versor);
}
// reset origin translation since it doesn't make sense anymore // reset origin translation since it doesn't make sense anymore
this->origin_translation = Pointf3(0,0,0); this->origin_translation = Pointf3(0,0,0);
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
void void ModelObject::rotate(float angle, const Axis &axis)
ModelObject::rotate(float angle, const Axis &axis)
{ {
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { for (ModelVolume *v : this->volumes)
(*v)->mesh.rotate(angle, axis); v->mesh.rotate(angle, axis);
}
this->origin_translation = Pointf3(0,0,0); this->origin_translation = Pointf3(0,0,0);
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
void void ModelObject::mirror(const Axis &axis)
ModelObject::mirror(const Axis &axis)
{ {
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { for (ModelVolume *v : this->volumes)
(*v)->mesh.mirror(axis); v->mesh.mirror(axis);
}
this->origin_translation = Pointf3(0,0,0); this->origin_translation = Pointf3(0,0,0);
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
size_t size_t ModelObject::materials_count() const
ModelObject::materials_count() const
{ {
std::set<t_model_material_id> material_ids; std::set<t_model_material_id> material_ids;
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { for (const ModelVolume *v : this->volumes)
material_ids.insert((*v)->material_id()); material_ids.insert(v->material_id());
}
return material_ids.size(); return material_ids.size();
} }
size_t size_t ModelObject::facets_count() const
ModelObject::facets_count() const
{ {
size_t num = 0; size_t num = 0;
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { for (const ModelVolume *v : this->volumes)
if ((*v)->modifier) continue; if (! v->modifier)
num += (*v)->mesh.stl.stats.number_of_facets; num += v->mesh.stl.stats.number_of_facets;
}
return num; return num;
} }
bool bool ModelObject::needed_repair() const
ModelObject::needed_repair() const
{ {
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { for (const ModelVolume *v : this->volumes)
if ((*v)->modifier) continue; if (! v->modifier && v->mesh.needed_repair())
if ((*v)->mesh.needed_repair()) return true; return true;
}
return false; return false;
} }
void void ModelObject::cut(coordf_t z, Model* model) const
ModelObject::cut(coordf_t z, Model* model) const
{ {
// clone this one to duplicate instances, materials etc. // clone this one to duplicate instances, materials etc.
ModelObject* upper = model->add_object(*this); ModelObject* upper = model->add_object(*this);
@ -700,8 +559,7 @@ ModelObject::cut(coordf_t z, Model* model) const
upper->input_file = ""; upper->input_file = "";
lower->input_file = ""; lower->input_file = "";
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { for (ModelVolume *volume : this->volumes) {
ModelVolume* volume = *v;
if (volume->modifier) { if (volume->modifier) {
// don't cut modifiers // don't cut modifiers
upper->add_volume(*volume); upper->add_volume(*volume);
@ -757,8 +615,7 @@ ModelObject::cut(coordf_t z, Model* model) const
} }
} }
void void ModelObject::split(ModelObjectPtrs* new_objects)
ModelObject::split(ModelObjectPtrs* new_objects)
{ {
if (this->volumes.size() > 1) { if (this->volumes.size() > 1) {
// We can't split meshes if there's more than one volume, because // We can't split meshes if there's more than one volume, because
@ -772,7 +629,7 @@ ModelObject::split(ModelObjectPtrs* new_objects)
for (TriangleMeshPtrs::iterator mesh = meshptrs.begin(); mesh != meshptrs.end(); ++mesh) { for (TriangleMeshPtrs::iterator mesh = meshptrs.begin(); mesh != meshptrs.end(); ++mesh) {
(*mesh)->repair(); (*mesh)->repair();
ModelObject* new_object = this->model->add_object(*this, false); ModelObject* new_object = m_model->add_object(*this, false);
new_object->input_file = ""; new_object->input_file = "";
ModelVolume* new_volume = new_object->add_volume(**mesh); ModelVolume* new_volume = new_object->add_volume(**mesh);
new_volume->name = volume->name; new_volume->name = volume->name;
@ -787,30 +644,7 @@ ModelObject::split(ModelObjectPtrs* new_objects)
return; return;
} }
void ModelVolume::material_id(t_model_material_id material_id)
ModelVolume::ModelVolume(ModelObject* object, const TriangleMesh &mesh)
: mesh(mesh), modifier(false), object(object)
{}
ModelVolume::ModelVolume(ModelObject* object, TriangleMesh &&mesh)
: mesh(std::move(mesh)), modifier(false), object(object)
{}
ModelVolume::ModelVolume(ModelObject* object, const ModelVolume &other)
: name(other.name), mesh(other.mesh), config(other.config),
modifier(other.modifier), object(object)
{
this->material_id(other.material_id());
}
t_model_material_id
ModelVolume::material_id() const
{
return this->_material_id;
}
void
ModelVolume::material_id(t_model_material_id material_id)
{ {
this->_material_id = material_id; this->_material_id = material_id;
@ -818,21 +652,18 @@ ModelVolume::material_id(t_model_material_id material_id)
(void)this->object->get_model()->add_material(material_id); (void)this->object->get_model()->add_material(material_id);
} }
ModelMaterial* ModelMaterial* ModelVolume::material() const
ModelVolume::material() const {
{
return this->object->get_model()->get_material(this->_material_id); return this->object->get_model()->get_material(this->_material_id);
} }
void void ModelVolume::set_material(t_model_material_id material_id, const ModelMaterial &material)
ModelVolume::set_material(t_model_material_id material_id, const ModelMaterial &material)
{ {
this->_material_id = material_id; this->_material_id = material_id;
(void)this->object->get_model()->add_material(material_id, material); (void)this->object->get_model()->add_material(material_id, material);
} }
ModelMaterial* ModelMaterial* ModelVolume::assign_unique_material()
ModelVolume::assign_unique_material()
{ {
Model* model = this->get_object()->get_model(); Model* model = this->get_object()->get_model();
@ -841,17 +672,7 @@ ModelVolume::assign_unique_material()
return model->add_material(this->_material_id); return model->add_material(this->_material_id);
} }
void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
ModelInstance::ModelInstance(ModelObject *object)
: rotation(0), scaling_factor(1), object(object)
{}
ModelInstance::ModelInstance(ModelObject *object, const ModelInstance &other)
: rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object)
{}
void
ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
{ {
mesh->rotate_z(this->rotation); // rotate around mesh origin mesh->rotate_z(this->rotation); // rotate around mesh origin
mesh->scale(this->scaling_factor); // scale around mesh origin mesh->scale(this->scaling_factor); // scale around mesh origin
@ -861,7 +682,7 @@ ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate) const BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate) const
{ {
// rotate around mesh origin // Rotate around mesh origin.
double c = cos(this->rotation); double c = cos(this->rotation);
double s = sin(this->rotation); double s = sin(this->rotation);
BoundingBoxf3 bbox; BoundingBoxf3 bbox;
@ -873,16 +694,27 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
double yold = v.y; double yold = v.y;
v.x = float(c * xold - s * yold); v.x = float(c * xold - s * yold);
v.y = float(s * xold + c * yold); v.y = float(s * xold + c * yold);
v.x *= float(this->scaling_factor);
v.y *= float(this->scaling_factor);
v.z *= float(this->scaling_factor);
if (!dont_translate) {
v.x += this->offset.x;
v.y += this->offset.y;
}
bbox.merge(Pointf3(v.x, v.y, v.z)); bbox.merge(Pointf3(v.x, v.y, v.z));
} }
} }
if (! empty(bbox)) {
// Scale the bounding box uniformly.
if (std::abs(this->scaling_factor - 1.) > EPSILON) {
bbox.min.x *= float(this->scaling_factor);
bbox.min.y *= float(this->scaling_factor);
bbox.min.z *= float(this->scaling_factor);
bbox.max.x *= float(this->scaling_factor);
bbox.max.y *= float(this->scaling_factor);
bbox.max.z *= float(this->scaling_factor);
}
// Translate the bounding box.
if (! dont_translate) {
bbox.min.x += float(this->offset.x);
bbox.min.y += float(this->offset.y);
bbox.max.x += float(this->offset.x);
bbox.max.y += float(this->offset.y);
}
}
return bbox; return bbox;
} }
@ -907,7 +739,7 @@ BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, b
v.x *= this->scaling_factor; v.x *= this->scaling_factor;
v.y *= this->scaling_factor; v.y *= this->scaling_factor;
v.z *= this->scaling_factor; v.z *= this->scaling_factor;
if (!dont_translate) { if (! dont_translate) {
v.x += this->offset.x; v.x += this->offset.x;
v.y += this->offset.y; v.y += this->offset.y;
} }
@ -916,8 +748,7 @@ BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, b
return out; return out;
} }
void void ModelInstance::transform_polygon(Polygon* polygon) const
ModelInstance::transform_polygon(Polygon* polygon) const
{ {
polygon->rotate(this->rotation); // rotate around polygon origin polygon->rotate(this->rotation); // rotate around polygon origin
polygon->scale(this->scaling_factor); // scale around polygon origin polygon->scale(this->scaling_factor); // scale around polygon origin

View File

@ -28,53 +28,6 @@ typedef std::vector<ModelObject*> ModelObjectPtrs;
typedef std::vector<ModelVolume*> ModelVolumePtrs; typedef std::vector<ModelVolume*> ModelVolumePtrs;
typedef std::vector<ModelInstance*> ModelInstancePtrs; typedef std::vector<ModelInstance*> ModelInstancePtrs;
// The print bed content.
// Description of a triangular model with multiple materials, multiple instances with various affine transformations
// and with multiple modifier meshes.
// A model groups multiple objects, each object having possibly multiple instances,
// all objects may share mutliple materials.
class Model
{
public:
// Materials are owned by a model and referenced by objects through t_model_material_id.
// Single material may be shared by multiple models.
ModelMaterialMap materials;
// Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation).
ModelObjectPtrs objects;
Model();
Model(const Model &other);
Model& operator= (Model other);
void swap(Model &other);
~Model();
ModelObject* add_object();
ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh);
ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh);
ModelObject* add_object(const ModelObject &other, bool copy_volumes = true);
void delete_object(size_t idx);
void clear_objects();
ModelMaterial* add_material(t_model_material_id material_id);
ModelMaterial* add_material(t_model_material_id material_id, const ModelMaterial &other);
ModelMaterial* get_material(t_model_material_id material_id);
void delete_material(t_model_material_id material_id);
void clear_materials();
bool has_objects_with_no_instances() const;
bool add_default_instances();
BoundingBoxf3 bounding_box() const;
void center_instances_around_point(const Pointf &point);
void align_instances_to_origin();
void translate(coordf_t x, coordf_t y, coordf_t z);
TriangleMesh mesh() const;
TriangleMesh raw_mesh() const;
bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out) const;
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
// Croaks if the duplicated objects do not fit the print bed.
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
};
// Material, which may be shared across multiple ModelObjects of a single Model. // Material, which may be shared across multiple ModelObjects of a single Model.
class ModelMaterial class ModelMaterial
{ {
@ -85,15 +38,16 @@ public:
// Dynamic configuration storage for the object specific configuration values, overriding the global configuration. // Dynamic configuration storage for the object specific configuration values, overriding the global configuration.
DynamicPrintConfig config; DynamicPrintConfig config;
Model* get_model() const { return this->model; }; Model* get_model() const { return m_model; };
void apply(const t_model_material_attributes &attributes); void apply(const t_model_material_attributes &attributes)
{ this->attributes.insert(attributes.begin(), attributes.end()); }
private: private:
// Parent, owning this material. // Parent, owning this material.
Model* model; Model *m_model;
ModelMaterial(Model *model); ModelMaterial(Model *model) : m_model(model) {}
ModelMaterial(Model *model, const ModelMaterial &other); ModelMaterial(Model *model, const ModelMaterial &other) : attributes(other.attributes), config(other.config), m_model(model) {}
}; };
// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), // A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
@ -104,37 +58,33 @@ class ModelObject
{ {
friend class Model; friend class Model;
public: public:
std::string name; std::string name;
std::string input_file; std::string input_file;
// Instances of this ModelObject. Each instance defines a shift on the print bed, rotation around the Z axis and a uniform scaling. // Instances of this ModelObject. Each instance defines a shift on the print bed, rotation around the Z axis and a uniform scaling.
// Instances are owned by this ModelObject. // Instances are owned by this ModelObject.
ModelInstancePtrs instances; ModelInstancePtrs instances;
// Printable and modifier volumes, each with its material ID and a set of override parameters. // Printable and modifier volumes, each with its material ID and a set of override parameters.
// ModelVolumes are owned by this ModelObject. // ModelVolumes are owned by this ModelObject.
ModelVolumePtrs volumes; ModelVolumePtrs volumes;
// Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings. // Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings.
DynamicPrintConfig config; DynamicPrintConfig config;
// Variation of a layer thickness for spans of Z coordinates. // Variation of a layer thickness for spans of Z coordinates.
t_layer_height_ranges layer_height_ranges; t_layer_height_ranges layer_height_ranges;
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS. // The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
std::vector<coordf_t> layer_height_profile; std::vector<coordf_t> layer_height_profile;
// layer_height_profile is initialized when the layer editing mode is entered. // layer_height_profile is initialized when the layer editing mode is entered.
// Only if the user really modified the layer height, layer_height_profile_valid is set // Only if the user really modified the layer height, layer_height_profile_valid is set
// and used subsequently by the PrintObject. // and used subsequently by the PrintObject.
bool layer_height_profile_valid; bool layer_height_profile_valid;
/* This vector accumulates the total translation applied to the object by the /* This vector accumulates the total translation applied to the object by the
center_around_origin() method. Callers might want to apply the same translation center_around_origin() method. Callers might want to apply the same translation
to new volumes before adding them to this object in order to preserve alignment to new volumes before adding them to this object in order to preserve alignment
when user expects that. */ when user expects that. */
Pointf3 origin_translation; Pointf3 origin_translation;
// these should be private but we need to expose them via XS until all methods are ported Model* get_model() const { return m_model; };
BoundingBoxf3 _bounding_box;
bool _bounding_box_valid;
Model* get_model() const { return this->model; };
ModelVolume* add_volume(const TriangleMesh &mesh); ModelVolume* add_volume(const TriangleMesh &mesh);
ModelVolume* add_volume(TriangleMesh &&mesh); ModelVolume* add_volume(TriangleMesh &&mesh);
@ -148,15 +98,23 @@ public:
void delete_last_instance(); void delete_last_instance();
void clear_instances(); void clear_instances();
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
BoundingBoxf3 bounding_box(); BoundingBoxf3 bounding_box();
void invalidate_bounding_box(); void invalidate_bounding_box() { m_bounding_box_valid = false; }
// A mesh containing all transformed instances of this object.
TriangleMesh mesh() const; TriangleMesh mesh() const;
// Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
// Currently used by ModelObject::mesh() and to calculate the 2D envelope for 2D platter.
TriangleMesh raw_mesh() const; TriangleMesh raw_mesh() const;
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
// This bounding box is only used for the actual slicing.
BoundingBoxf3 raw_bounding_box() const; BoundingBoxf3 raw_bounding_box() const;
BoundingBoxf3 instance_bounding_box(size_t instance_idx) const; // A snug bounding box around the transformed non-modifier object volumes.
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
void center_around_origin(); void center_around_origin();
void translate(const Vectorf3 &vector); void translate(const Vectorf3 &vector) { this->translate(vector.x, vector.y, vector.z); }
void translate(coordf_t x, coordf_t y, coordf_t z); void translate(coordf_t x, coordf_t y, coordf_t z);
void scale(const Pointf3 &versor); void scale(const Pointf3 &versor);
void rotate(float angle, const Axis &axis); void rotate(float angle, const Axis &axis);
@ -166,17 +124,19 @@ public:
bool needed_repair() const; bool needed_repair() const;
void cut(coordf_t z, Model* model) const; void cut(coordf_t z, Model* model) const;
void split(ModelObjectPtrs* new_objects); 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
private: private:
// Parent object, owning this ModelObject. ModelObject(Model *model) : m_model(model), m_bounding_box_valid(false), layer_height_profile_valid(false) {}
Model* model;
ModelObject(Model *model);
ModelObject(Model *model, const ModelObject &other, bool copy_volumes = true); ModelObject(Model *model, const ModelObject &other, bool copy_volumes = true);
ModelObject& operator= (ModelObject other); ModelObject& operator= (ModelObject other);
void swap(ModelObject &other); void swap(ModelObject &other);
~ModelObject(); ~ModelObject();
// Parent object, owning this ModelObject.
Model *m_model;
// Bounding box, cached.
BoundingBoxf3 m_bounding_box;
bool m_bounding_box_valid;
}; };
// An object STL, or a modifier volume, over which a different set of parameters shall be applied. // An object STL, or a modifier volume, over which a different set of parameters shall be applied.
@ -196,7 +156,7 @@ public:
// A parent object owning this modifier volume. // A parent object owning this modifier volume.
ModelObject* get_object() const { return this->object; }; ModelObject* get_object() const { return this->object; };
t_model_material_id material_id() const; t_model_material_id material_id() const { return this->_material_id; }
void material_id(t_model_material_id material_id); void material_id(t_model_material_id material_id);
ModelMaterial* material() const; ModelMaterial* material() const;
void set_material(t_model_material_id material_id, const ModelMaterial &material); void set_material(t_model_material_id material_id, const ModelMaterial &material);
@ -208,9 +168,11 @@ private:
ModelObject* object; ModelObject* object;
t_model_material_id _material_id; t_model_material_id _material_id;
ModelVolume(ModelObject *object, const TriangleMesh &mesh); ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object) {}
ModelVolume(ModelObject *object, TriangleMesh &&mesh); ModelVolume(ModelObject *object, TriangleMesh &&mesh) : mesh(std::move(mesh)), modifier(false), object(object) {}
ModelVolume(ModelObject *object, const ModelVolume &other); ModelVolume(ModelObject *object, const ModelVolume &other) :
name(other.name), mesh(other.mesh), config(other.config), modifier(other.modifier), object(object)
{ this->material_id(other.material_id()); }
}; };
// A single instance of a ModelObject. // A single instance of a ModelObject.
@ -237,9 +199,58 @@ public:
private: private:
// Parent object, owning this instance. // Parent object, owning this instance.
ModelObject* object; ModelObject* object;
ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), object(object) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object) {}
};
// The print bed content.
// Description of a triangular model with multiple materials, multiple instances with various affine transformations
// and with multiple modifier meshes.
// A model groups multiple objects, each object having possibly multiple instances,
// all objects may share mutliple materials.
class Model
{
public:
// Materials are owned by a model and referenced by objects through t_model_material_id.
// Single material may be shared by multiple models.
ModelMaterialMap materials;
// Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation).
ModelObjectPtrs objects;
ModelInstance(ModelObject *object); Model() {}
ModelInstance(ModelObject *object, const ModelInstance &other); Model(const Model &other);
Model& operator= (Model other);
void swap(Model &other);
~Model();
ModelObject* add_object();
ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh);
ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh);
ModelObject* add_object(const ModelObject &other, bool copy_volumes = true);
void delete_object(size_t idx);
void clear_objects();
ModelMaterial* add_material(t_model_material_id material_id);
ModelMaterial* add_material(t_model_material_id material_id, const ModelMaterial &other);
ModelMaterial* get_material(t_model_material_id material_id) {
ModelMaterialMap::iterator i = this->materials.find(material_id);
return (i == this->materials.end()) ? nullptr : i->second;
}
void delete_material(t_model_material_id material_id);
void clear_materials();
bool add_default_instances();
BoundingBoxf3 bounding_box();
void center_instances_around_point(const Pointf &point);
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
TriangleMesh mesh() const;
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
// Croaks if the duplicated objects do not fit the print bed.
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
}; };
} }

View File

@ -509,9 +509,7 @@ std::string Print::validate() const
{ {
Polygons convex_hulls_other; Polygons convex_hulls_other;
for (PrintObject *object : this->objects) { for (PrintObject *object : this->objects) {
// Get convex hull of all meshes assigned to this print object // Get convex hull of all meshes assigned to this print object.
// (this is the same as model_object()->raw_mesh.convex_hull()
// but probably more efficient.
Polygon convex_hull; Polygon convex_hull;
{ {
Polygons mesh_convex_hulls; Polygons mesh_convex_hulls;

View File

@ -58,15 +58,12 @@
size_t material_count() const size_t material_count() const
%code%{ RETVAL = THIS->materials.size(); %}; %code%{ RETVAL = THIS->materials.size(); %};
bool has_objects_with_no_instances();
bool add_default_instances(); bool add_default_instances();
Clone<BoundingBoxf3> bounding_box(); Clone<BoundingBoxf3> bounding_box();
void center_instances_around_point(Pointf* point) void center_instances_around_point(Pointf* point)
%code%{ THIS->center_instances_around_point(*point); %}; %code%{ THIS->center_instances_around_point(*point); %};
void align_instances_to_origin();
void translate(double x, double y, double z); void translate(double x, double y, double z);
Clone<TriangleMesh> mesh(); Clone<TriangleMesh> mesh();
Clone<TriangleMesh> raw_mesh();
ModelObjectPtrs* objects() ModelObjectPtrs* objects()
%code%{ RETVAL = &THIS->objects; %}; %code%{ RETVAL = &THIS->objects; %};
@ -183,25 +180,10 @@ ModelMaterial::attributes()
%code%{ RETVAL = &THIS->instances; %}; %code%{ RETVAL = &THIS->instances; %};
void invalidate_bounding_box(); void invalidate_bounding_box();
void update_bounding_box();
Clone<TriangleMesh> mesh(); Clone<TriangleMesh> mesh();
Clone<TriangleMesh> raw_mesh(); Clone<TriangleMesh> raw_mesh();
Clone<BoundingBoxf3> raw_bounding_box(); Clone<BoundingBoxf3> instance_bounding_box(int idx)
Clone<BoundingBoxf3> instance_bounding_box(int idx); %code%{ RETVAL = THIS->instance_bounding_box(idx, true); %};
Ref<BoundingBoxf3> _bounding_box(BoundingBoxf3* new_bbox = NULL)
%code{%
if (NULL != new_bbox) {
THIS->_bounding_box = *new_bbox;
THIS->_bounding_box_valid = true;
}
if (!THIS->_bounding_box_valid) {
XSRETURN_UNDEF;
}
RETVAL = &THIS->_bounding_box;
%};
Clone<BoundingBoxf3> bounding_box(); Clone<BoundingBoxf3> bounding_box();
%name{_add_volume} Ref<ModelVolume> add_volume(TriangleMesh* mesh) %name{_add_volume} Ref<ModelVolume> add_volume(TriangleMesh* mesh)

View File

@ -64,8 +64,6 @@ _constant()
Ref<Point3> size() Ref<Point3> size()
%code%{ RETVAL = &THIS->size; %}; %code%{ RETVAL = &THIS->size; %};
Clone<BoundingBox> bounding_box(); Clone<BoundingBox> bounding_box();
Ref<Point> _copies_shift()
%code%{ RETVAL = &THIS->_copies_shift; %};
bool typed_slices() bool typed_slices()
%code%{ RETVAL = THIS->typed_slices; %}; %code%{ RETVAL = THIS->typed_slices; %};