void ModelObject::print_info() const { using namespace std; cout << fixed; cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl; TriangleMesh mesh = this->raw_mesh(); mesh.check_topology(); BoundingBoxf3 bb = mesh.bounding_box(); Sizef3 size = bb.size(); cout << "size_x = " << size.x << endl; cout << "size_y = " << size.y << endl; cout << "size_z = " << size.z << endl; cout << "min_x = " << bb.min.x << endl; cout << "min_y = " << bb.min.y << endl; cout << "min_z = " << bb.min.z << endl; cout << "max_x = " << bb.max.x << endl; cout << "max_y = " << bb.max.y << endl; cout << "max_z = " << bb.max.z << endl; cout << "number_of_facets = " << mesh.stl.stats.number_of_facets << endl; cout << "manifold = " << (mesh.is_manifold() ? "yes" : "no") << endl; mesh.repair(); // this calculates number_of_parts if (mesh.needed_repair()) { mesh.repair(); if (mesh.stl.stats.degenerate_facets > 0) cout << "degenerate_facets = " << mesh.stl.stats.degenerate_facets << endl; if (mesh.stl.stats.edges_fixed > 0) cout << "edges_fixed = " << mesh.stl.stats.edges_fixed << endl; if (mesh.stl.stats.facets_removed > 0) cout << "facets_removed = " << mesh.stl.stats.facets_removed << endl; if (mesh.stl.stats.facets_added > 0) cout << "facets_added = " << mesh.stl.stats.facets_added << endl; if (mesh.stl.stats.facets_reversed > 0) cout << "facets_reversed = " << mesh.stl.stats.facets_reversed << endl; if (mesh.stl.stats.backwards_edges > 0) cout << "backwards_edges = " << mesh.stl.stats.backwards_edges << endl; } cout << "number_of_parts = " << mesh.stl.stats.number_of_parts << endl; cout << "volume = " << mesh.volume() << endl; }
bool STL::read_file(std::string input_file, Model* model) { // TODO: encode file name // TODO: check that file exists TriangleMesh mesh; mesh.ReadSTLFile(input_file); mesh.repair(); if (mesh.facets_count() == 0) throw std::runtime_error("This STL file couldn't be read because it's empty."); ModelObject* object = model->add_object(); object->name = input_file; // TODO: use basename() object->input_file = input_file; ModelVolume* volume = object->add_volume(mesh); volume->name = input_file; // TODO: use basename() return true; }
void SLAPrint::slice() { TriangleMesh mesh = this->model->mesh(); mesh.repair(); // align to origin taking raft into account this->bb = mesh.bounding_box(); if (this->config.raft_layers > 0) { this->bb.min.x -= this->config.raft_offset.value; this->bb.min.y -= this->config.raft_offset.value; this->bb.max.x += this->config.raft_offset.value; this->bb.max.y += this->config.raft_offset.value; } mesh.translate(0, 0, -bb.min.z); this->bb.translate(0, 0, -bb.min.z); // if we are generating a raft, first_layer_height will not affect mesh slicing const float lh = this->config.layer_height.value; const float first_lh = this->config.first_layer_height.value; // generate the list of Z coordinates for mesh slicing // (we slice each layer at half of its thickness) this->layers.clear(); { const float first_slice_lh = (this->config.raft_layers > 0) ? lh : first_lh; this->layers.push_back(Layer(first_slice_lh/2, first_slice_lh)); } while (this->layers.back().print_z + lh/2 <= mesh.stl.stats.max.z) { this->layers.push_back(Layer(this->layers.back().print_z + lh/2, this->layers.back().print_z + lh)); } // perform slicing and generate layers { std::vector<float> slice_z; for (size_t i = 0; i < this->layers.size(); ++i) slice_z.push_back(this->layers[i].slice_z); std::vector<ExPolygons> slices; TriangleMeshSlicer(&mesh).slice(slice_z, &slices); for (size_t i = 0; i < slices.size(); ++i) this->layers[i].slices.expolygons = slices[i]; } // generate infill if (this->config.fill_density < 100) { std::auto_ptr<Fill> fill(Fill::new_from_type(this->config.fill_pattern.value)); fill->bounding_box.merge(Point::new_scale(bb.min.x, bb.min.y)); fill->bounding_box.merge(Point::new_scale(bb.max.x, bb.max.y)); fill->spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); fill->angle = Geometry::deg2rad(this->config.fill_angle.value); fill->density = this->config.fill_density.value/100; parallelize<size_t>( 0, this->layers.size()-1, boost::bind(&SLAPrint::_infill_layer, this, _1, fill.get()), this->config.threads.value ); } // generate support material this->sm_pillars.clear(); ExPolygons overhangs; if (this->config.support_material) { // flatten and merge all the overhangs { Polygons pp; for (std::vector<Layer>::const_iterator it = this->layers.begin()+1; it != this->layers.end(); ++it) pp += diff(it->slices, (it - 1)->slices); overhangs = union_ex(pp); } // generate points following the shape of each island Points pillars_pos; const coordf_t spacing = scale_(this->config.support_material_spacing); const coordf_t radius = scale_(this->sm_pillars_radius()); for (ExPolygons::const_iterator it = overhangs.begin(); it != overhangs.end(); ++it) { // leave a radius/2 gap between pillars and contour to prevent lateral adhesion for (float inset = radius * 1.5;; inset += spacing) { // inset according to the configured spacing Polygons curr = offset(*it, -inset); if (curr.empty()) break; // generate points along the contours for (Polygons::const_iterator pg = curr.begin(); pg != curr.end(); ++pg) { Points pp = pg->equally_spaced_points(spacing); for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p) pillars_pos.push_back(*p); } } } // for each pillar, check which layers it applies to for (Points::const_iterator p = pillars_pos.begin(); p != pillars_pos.end(); ++p) { SupportPillar pillar(*p); bool object_hit = false; // check layers top-down for (int i = this->layers.size()-1; i >= 0; --i) { // check whether point is void in this layer if (!this->layers[i].slices.contains(*p)) { // no slice contains the point, so it's in the void if (pillar.top_layer > 0) { // we have a pillar, so extend it pillar.bottom_layer = i + this->config.raft_layers; } else if (object_hit) { // we don't have a pillar and we're below the object, so create one pillar.top_layer = i + this->config.raft_layers; } } else { if (pillar.top_layer > 0) { // we have a pillar which is not needed anymore, so store it and initialize a new potential pillar this->sm_pillars.push_back(pillar); pillar = SupportPillar(*p); } object_hit = true; } } if (pillar.top_layer > 0) this->sm_pillars.push_back(pillar); } } // generate a solid raft if requested // (do this after support material because we take support material shape into account) if (this->config.raft_layers > 0) { ExPolygons raft = this->layers.front().slices + overhangs; // take support material into account raft = offset_ex(raft, scale_(this->config.raft_offset)); for (int i = this->config.raft_layers; i >= 1; --i) { this->layers.insert(this->layers.begin(), Layer(0, first_lh + lh * (i-1))); this->layers.front().slices = raft; } // prepend total raft height to all sliced layers for (size_t i = this->config.raft_layers; i < this->layers.size(); ++i) this->layers[i].print_z += first_lh + lh * (this->config.raft_layers-1); } }
int main(const int argc, const char **argv) { // parse all command line options into a DynamicConfig ConfigDef config_def; config_def.merge(cli_config_def); config_def.merge(print_config_def); DynamicConfig config(&config_def); t_config_option_keys input_files; config.read_cli(argc, argv, &input_files); // apply command line options to a more handy CLIConfig CLIConfig cli_config; cli_config.apply(config, true); DynamicPrintConfig print_config; // load config files supplied via --load for (std::vector<std::string>::const_iterator file = cli_config.load.values.begin(); file != cli_config.load.values.end(); ++file) { if (!boost::filesystem::exists(*file)) { std::cout << "No such file: " << *file << std::endl; exit(1); } DynamicPrintConfig c; try { c.load(*file); } catch (std::exception &e) { std::cout << "Error while reading config file: " << e.what() << std::endl; exit(1); } c.normalize(); print_config.apply(c); } // apply command line options to a more specific DynamicPrintConfig which provides normalize() // (command line options override --load files) print_config.apply(config, true); print_config.normalize(); // write config if requested if (!cli_config.save.value.empty()) print_config.save(cli_config.save.value); // read input file(s) if any std::vector<Model> models; for (t_config_option_keys::const_iterator it = input_files.begin(); it != input_files.end(); ++it) { if (!boost::filesystem::exists(*it)) { std::cout << "No such file: " << *it << std::endl; exit(1); } Model model; // TODO: read other file formats with Model::read_from_file() try { Slic3r::IO::STL::read(*it, &model); } catch (std::exception &e) { std::cout << *it << ": " << e.what() << std::endl; exit(1); } if (model.objects.empty()) { printf("Error: file is empty: %s\n", it->c_str()); continue; } model.add_default_instances(); // apply command line transform options for (ModelObjectPtrs::iterator o = model.objects.begin(); o != model.objects.end(); ++o) { if (cli_config.scale_to_fit.is_positive_volume()) (*o)->scale_to_fit(cli_config.scale_to_fit.value); (*o)->scale(cli_config.scale.value); (*o)->rotate(cli_config.rotate.value, Z); } // TODO: handle --merge models.push_back(model); } for (std::vector<Model>::iterator model = models.begin(); model != models.end(); ++model) { if (cli_config.info) { // --info works on unrepaired model model->print_info(); } else if (cli_config.export_obj) { std::string outfile = cli_config.output.value; if (outfile.empty()) outfile = model->objects.front()->input_file + ".obj"; TriangleMesh mesh = model->mesh(); mesh.repair(); Slic3r::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; if (outfile.empty()) outfile = model->objects.front()->input_file + ".pov"; TriangleMesh mesh = model->mesh(); mesh.repair(); Slic3r::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; if (outfile.empty()) outfile = model->objects.front()->input_file + ".svg"; SLAPrint print(&*model); print.config.apply(print_config, true); print.slice(); print.write_svg(outfile); printf("SVG file exported to %s\n", outfile.c_str()); } else { std::cerr << "error: only --export-svg and --export-obj are currently supported" << std::endl; return 1; } } return 0; }