mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-23 00:31:11 -06:00
Merge branch 'master' of https://github.com/prusa3d/Slic3r into gcode_preview
This commit is contained in:
commit
99f7bd90db
63 changed files with 6132 additions and 12448 deletions
|
@ -94,6 +94,14 @@ public:
|
|||
void translate(const Pointf3 &pos) { this->translate(pos.x, pos.y, pos.z); }
|
||||
void offset(coordf_t delta);
|
||||
PointClass center() const;
|
||||
|
||||
bool contains(const PointClass &point) const {
|
||||
return BoundingBoxBase<PointClass>::contains(point) && point.z >= this->min.z && point.z <= this->max.z;
|
||||
}
|
||||
|
||||
bool contains(const BoundingBox3Base<PointClass>& other) const {
|
||||
return contains(other.min) && contains(other.max);
|
||||
}
|
||||
};
|
||||
|
||||
class BoundingBox : public BoundingBoxBase<Point>
|
||||
|
|
|
@ -1427,10 +1427,12 @@ Polylines FillTriangles::fill_surface(const Surface *surface, const FillParams &
|
|||
// Each linear fill covers 1/3 of the target coverage.
|
||||
FillParams params2 = params;
|
||||
params2.density *= 0.333333333f;
|
||||
FillParams params3 = params2;
|
||||
params3.dont_connect = true;
|
||||
Polylines polylines_out;
|
||||
if (! fill_surface_by_lines(surface, params2, 0.f, 0., polylines_out) ||
|
||||
! fill_surface_by_lines(surface, params2, float(M_PI / 3.), 0., polylines_out) ||
|
||||
! fill_surface_by_lines(surface, params2, float(2. * M_PI / 3.), 0., polylines_out)) {
|
||||
! fill_surface_by_lines(surface, params3, float(2. * M_PI / 3.), 0., polylines_out)) {
|
||||
printf("FillTriangles::fill_surface() failed to fill a region.\n");
|
||||
}
|
||||
return polylines_out;
|
||||
|
@ -1441,10 +1443,12 @@ Polylines FillStars::fill_surface(const Surface *surface, const FillParams ¶
|
|||
// Each linear fill covers 1/3 of the target coverage.
|
||||
FillParams params2 = params;
|
||||
params2.density *= 0.333333333f;
|
||||
FillParams params3 = params2;
|
||||
params3.dont_connect = true;
|
||||
Polylines polylines_out;
|
||||
if (! fill_surface_by_lines(surface, params2, 0.f, 0., polylines_out) ||
|
||||
! fill_surface_by_lines(surface, params2, float(M_PI / 3.), 0., polylines_out) ||
|
||||
! fill_surface_by_lines(surface, params2, float(2. * M_PI / 3.), 0.5 * this->spacing / params2.density, polylines_out)) {
|
||||
! fill_surface_by_lines(surface, params3, float(2. * M_PI / 3.), 0.5 * this->spacing / params2.density, polylines_out)) {
|
||||
printf("FillStars::fill_surface() failed to fill a region.\n");
|
||||
}
|
||||
return polylines_out;
|
||||
|
@ -1455,12 +1459,14 @@ Polylines FillCubic::fill_surface(const Surface *surface, const FillParams ¶
|
|||
// Each linear fill covers 1/3 of the target coverage.
|
||||
FillParams params2 = params;
|
||||
params2.density *= 0.333333333f;
|
||||
FillParams params3 = params2;
|
||||
params3.dont_connect = true;
|
||||
Polylines polylines_out;
|
||||
coordf_t dx = sqrt(0.5) * z;
|
||||
if (! fill_surface_by_lines(surface, params2, 0.f, dx, polylines_out) ||
|
||||
! fill_surface_by_lines(surface, params2, float(M_PI / 3.), - dx, polylines_out) ||
|
||||
// Rotated by PI*2/3 + PI to achieve reverse sloping wall.
|
||||
! fill_surface_by_lines(surface, params2, float(M_PI * 2. / 3.), dx, polylines_out)) {
|
||||
! fill_surface_by_lines(surface, params3, float(M_PI * 2. / 3.), dx, polylines_out)) {
|
||||
printf("FillCubic::fill_surface() failed to fill a region.\n");
|
||||
}
|
||||
return polylines_out;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -107,9 +107,11 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
|
|||
TYPE_G1 = 1 << 5,
|
||||
TYPE_ADJUSTABLE = 1 << 6,
|
||||
TYPE_EXTERNAL_PERIMETER = 1 << 7,
|
||||
TYPE_WIPE = 1 << 8,
|
||||
TYPE_G4 = 1 << 9,
|
||||
TYPE_G92 = 1 << 10,
|
||||
// The line sets a feedrate.
|
||||
TYPE_HAS_F = 1 << 8,
|
||||
TYPE_WIPE = 1 << 9,
|
||||
TYPE_G4 = 1 << 10,
|
||||
TYPE_G92 = 1 << 11,
|
||||
};
|
||||
|
||||
Line(unsigned int type, size_t line_start, size_t line_end) :
|
||||
|
@ -187,9 +189,13 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
|
|||
(*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : size_t(-1);
|
||||
if (axis != size_t(-1)) {
|
||||
new_pos[axis] = float(atof(++c));
|
||||
if (axis == 4)
|
||||
if (axis == 4) {
|
||||
// Convert mm/min to mm/sec.
|
||||
new_pos[4] /= 60.f;
|
||||
if ((line.type & Adjustment::Line::TYPE_G92) == 0)
|
||||
// This is G0 or G1 line and it sets the feedrate. This mark is used for reducing the duplicate F calls.
|
||||
line.type |= Adjustment::Line::TYPE_HAS_F;
|
||||
}
|
||||
}
|
||||
// Skip this word.
|
||||
for (; *c != ' ' && *c != '\t' && *c != 0; ++ c);
|
||||
|
@ -227,6 +233,8 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
|
|||
if ((line.type & Adjustment::Line::TYPE_ADJUSTABLE) || active_speed_modifier != size_t(-1))
|
||||
line.time_max = (min_print_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / min_print_speed);
|
||||
if (active_speed_modifier < adjustment->lines.size() && (line.type & Adjustment::Line::TYPE_G1)) {
|
||||
// Inside the ";_EXTRUDE_SET_SPEED" blocks, there must not be a G1 Fxx entry.
|
||||
assert((line.type & Adjustment::Line::TYPE_HAS_F) == 0);
|
||||
Adjustment::Line &sm = adjustment->lines[active_speed_modifier];
|
||||
sm.length += line.length;
|
||||
sm.time += line.time;
|
||||
|
@ -415,17 +423,20 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
|
|||
};
|
||||
change_extruder_set_fan();
|
||||
|
||||
size_t pos = 0;
|
||||
const char *pos = gcode.c_str();
|
||||
int current_feedrate = 0;
|
||||
for (const Adjustment::Line *line : lines) {
|
||||
if (line->line_start > pos)
|
||||
new_gcode.append(gcode.c_str() + pos, line->line_start - pos);
|
||||
const char *line_start = gcode.c_str() + line->line_start;
|
||||
const char *line_end = gcode.c_str() + line->line_end;
|
||||
if (line_start > pos)
|
||||
new_gcode.append(pos, line_start - pos);
|
||||
if (line->type & Adjustment::Line::TYPE_SET_TOOL) {
|
||||
unsigned int new_extruder = (unsigned int)atoi(gcode.c_str() + line->line_start + toolchange_prefix.size());
|
||||
unsigned int new_extruder = (unsigned int)atoi(line_start + toolchange_prefix.size());
|
||||
if (new_extruder != m_current_extruder) {
|
||||
m_current_extruder = new_extruder;
|
||||
change_extruder_set_fan();
|
||||
}
|
||||
new_gcode.append(gcode.c_str() + line->line_start, line->line_end - line->line_start);
|
||||
new_gcode.append(line_start, line_end - line_start);
|
||||
} else if (line->type & Adjustment::Line::TYPE_BRIDGE_FAN_START) {
|
||||
if (bridge_fan_control)
|
||||
new_gcode += m_gcodegen.writer().set_fan(bridge_fan_speed, true);
|
||||
|
@ -434,40 +445,80 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
|
|||
new_gcode += m_gcodegen.writer().set_fan(fan_speed, true);
|
||||
} else if (line->type & Adjustment::Line::TYPE_EXTRUDE_END) {
|
||||
// Just remove this comment.
|
||||
} else if (line->type & (Adjustment::Line::TYPE_ADJUSTABLE | Adjustment::Line::TYPE_EXTERNAL_PERIMETER | Adjustment::Line::TYPE_WIPE)) {
|
||||
// Start of the comment. The line type indicates there must be some comment present.
|
||||
const char *end = strchr(gcode.c_str() + line->line_start, ';');
|
||||
} else if (line->type & (Adjustment::Line::TYPE_ADJUSTABLE | Adjustment::Line::TYPE_EXTERNAL_PERIMETER | Adjustment::Line::TYPE_WIPE | Adjustment::Line::TYPE_HAS_F)) {
|
||||
// Find the start of a comment, or roll to the end of line.
|
||||
const char *end = line_start;
|
||||
for (; end < line_end && *end != ';'; ++ end);
|
||||
// Find the 'F' word.
|
||||
const char *fpos = strstr(line_start + 2, " F") + 2;
|
||||
int new_feedrate = current_feedrate;
|
||||
bool modify = false;
|
||||
assert(fpos != nullptr);
|
||||
if (line->slowdown) {
|
||||
// Replace the feedrate.
|
||||
const char *pos = strstr(gcode.c_str() + line->line_start + 2, " F") + 2;
|
||||
new_gcode.append(gcode.c_str() + line->line_start, pos - gcode.c_str() - line->line_start);
|
||||
char buf[64];
|
||||
sprintf(buf, "%d", int(floor(60. * (line->length / line->time) + 0.5)));
|
||||
new_gcode += buf;
|
||||
// Skip the non-whitespaces up to the comment.
|
||||
for (; *pos != ' ' && *pos != ';'; ++ pos);
|
||||
// Append the rest of the line without the comment.
|
||||
if (pos < end)
|
||||
new_gcode.append(pos, end - pos);
|
||||
modify = true;
|
||||
new_feedrate = int(floor(60. * (line->length / line->time) + 0.5));
|
||||
} else {
|
||||
// Append the line without the comment.
|
||||
new_gcode.append(gcode.c_str() + line->line_start, end - gcode.c_str() - line->line_start);
|
||||
new_feedrate = atoi(fpos);
|
||||
if (new_feedrate != current_feedrate) {
|
||||
// Append the line without the comment.
|
||||
new_gcode.append(line_start, end - line_start);
|
||||
current_feedrate = new_feedrate;
|
||||
} else if ((line->type & (Adjustment::Line::TYPE_ADJUSTABLE | Adjustment::Line::TYPE_EXTERNAL_PERIMETER | Adjustment::Line::TYPE_WIPE)) || line->length == 0.) {
|
||||
// Feedrate does not change and this line does not move the print head. Skip the complete G-code line including the G-code comment.
|
||||
end = line_end;
|
||||
} else {
|
||||
// Remove the feedrate from the G0/G1 line.
|
||||
modify = true;
|
||||
}
|
||||
}
|
||||
if (modify) {
|
||||
if (new_feedrate != current_feedrate) {
|
||||
// Replace the feedrate.
|
||||
new_gcode.append(line_start, fpos - line_start);
|
||||
current_feedrate = new_feedrate;
|
||||
char buf[64];
|
||||
sprintf(buf, "%d", int(current_feedrate));
|
||||
new_gcode += buf;
|
||||
} else {
|
||||
// Remove the feedrate word.
|
||||
const char *f = fpos;
|
||||
// Roll the pointer before the 'F' word.
|
||||
for (f -= 2; f > line_start && (*f == ' ' || *f == '\t'); -- f);
|
||||
// Append up to the F word, without the trailing whitespace.
|
||||
new_gcode.append(line_start, f - line_start + 1);
|
||||
}
|
||||
// Skip the non-whitespaces of the F parameter up the comment or end of line.
|
||||
for (; fpos != end && *fpos != ' ' && *fpos != ';' && *fpos != '\n'; ++fpos);
|
||||
// Append the rest of the line without the comment.
|
||||
if (fpos < end)
|
||||
new_gcode.append(fpos, end - fpos);
|
||||
// There should never be an empty G1 statement emited by the filter. Such lines should be removed completely.
|
||||
assert(new_gcode.size() < 4 || new_gcode.substr(new_gcode.size() - 4) != "G1 \n");
|
||||
}
|
||||
// Process the rest of the line.
|
||||
if (end < line_end) {
|
||||
if (line->type & (Adjustment::Line::TYPE_ADJUSTABLE | Adjustment::Line::TYPE_EXTERNAL_PERIMETER | Adjustment::Line::TYPE_WIPE)) {
|
||||
// Process comments, remove ";_EXTRUDE_SET_SPEED", ";_EXTERNAL_PERIMETER", ";_WIPE"
|
||||
std::string comment(end, line_end);
|
||||
boost::replace_all(comment, ";_EXTRUDE_SET_SPEED", "");
|
||||
if (line->type & Adjustment::Line::TYPE_EXTERNAL_PERIMETER)
|
||||
boost::replace_all(comment, ";_EXTERNAL_PERIMETER", "");
|
||||
if (line->type & Adjustment::Line::TYPE_WIPE)
|
||||
boost::replace_all(comment, ";_WIPE", "");
|
||||
new_gcode += comment;
|
||||
} else {
|
||||
// Just attach the rest of the source line.
|
||||
new_gcode.append(end, line_end - end);
|
||||
}
|
||||
}
|
||||
// Process the comments, remove ";_EXTRUDE_SET_SPEED", ";_EXTERNAL_PERIMETER", ";_WIPE"
|
||||
std::string comment(end, gcode.c_str() + line->line_end);
|
||||
boost::replace_all(comment, ";_EXTRUDE_SET_SPEED", "");
|
||||
if (line->type & Adjustment::Line::TYPE_EXTERNAL_PERIMETER)
|
||||
boost::replace_all(comment, ";_EXTERNAL_PERIMETER", "");
|
||||
if (line->type & Adjustment::Line::TYPE_WIPE)
|
||||
boost::replace_all(comment, ";_WIPE", "");
|
||||
new_gcode += comment;
|
||||
} else {
|
||||
new_gcode.append(gcode.c_str() + line->line_start, line->line_end - line->line_start);
|
||||
new_gcode.append(line_start, line_end - line_start);
|
||||
}
|
||||
pos = line->line_end;
|
||||
pos = line_end;
|
||||
}
|
||||
if (pos < gcode.size())
|
||||
new_gcode.append(gcode.c_str() + pos, gcode.size() - pos);
|
||||
const char *gcode_end = gcode.c_str() + gcode.size();
|
||||
if (pos < gcode_end)
|
||||
new_gcode.append(pos, gcode_end - pos);
|
||||
|
||||
return new_gcode;
|
||||
}
|
||||
|
|
|
@ -222,7 +222,7 @@ bool Model::add_default_instances()
|
|||
}
|
||||
|
||||
// this returns the bounding box of the *transformed* instances
|
||||
BoundingBoxf3 Model::bounding_box()
|
||||
BoundingBoxf3 Model::bounding_box() const
|
||||
{
|
||||
BoundingBoxf3 bb;
|
||||
for (ModelObject *o : this->objects)
|
||||
|
@ -230,6 +230,14 @@ BoundingBoxf3 Model::bounding_box()
|
|||
return bb;
|
||||
}
|
||||
|
||||
BoundingBoxf3 Model::transformed_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3 bb;
|
||||
for (const ModelObject* obj : this->objects)
|
||||
bb.merge(obj->tight_bounding_box(false));
|
||||
return bb;
|
||||
}
|
||||
|
||||
void Model::center_instances_around_point(const Pointf &point)
|
||||
{
|
||||
// BoundingBoxf3 bb = this->bounding_box();
|
||||
|
@ -409,6 +417,51 @@ void Model::convert_multipart_object()
|
|||
this->objects.push_back(object);
|
||||
}
|
||||
|
||||
void Model::adjust_min_z()
|
||||
{
|
||||
if (objects.empty())
|
||||
return;
|
||||
|
||||
if (bounding_box().min.z < 0.0)
|
||||
{
|
||||
for (ModelObject* obj : objects)
|
||||
{
|
||||
if (obj != nullptr)
|
||||
{
|
||||
coordf_t obj_min_z = obj->bounding_box().min.z;
|
||||
if (obj_min_z < 0.0)
|
||||
obj->translate(0.0, 0.0, -obj_min_z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Model::fits_print_volume(const DynamicPrintConfig* config) const
|
||||
{
|
||||
if (config == nullptr)
|
||||
return false;
|
||||
|
||||
if (objects.empty())
|
||||
return true;
|
||||
|
||||
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
|
||||
if (opt == nullptr)
|
||||
return false;
|
||||
|
||||
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
|
||||
BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height")));
|
||||
return print_volume.contains(transformed_bounding_box());
|
||||
}
|
||||
|
||||
bool Model::fits_print_volume(const FullPrintConfig &config) const
|
||||
{
|
||||
if (objects.empty())
|
||||
return true;
|
||||
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.bed_shape.values));
|
||||
BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config.max_print_height));
|
||||
return print_volume.contains(transformed_bounding_box());
|
||||
}
|
||||
|
||||
ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) :
|
||||
name(other.name),
|
||||
input_file(other.input_file),
|
||||
|
@ -539,7 +592,7 @@ void ModelObject::clear_instances()
|
|||
|
||||
// Returns the bounding box of the transformed instances.
|
||||
// This bounding box is approximate and not snug.
|
||||
BoundingBoxf3 ModelObject::bounding_box()
|
||||
const BoundingBoxf3& ModelObject::bounding_box()
|
||||
{
|
||||
if (! m_bounding_box_valid) {
|
||||
BoundingBoxf3 raw_bbox;
|
||||
|
@ -555,6 +608,54 @@ BoundingBoxf3 ModelObject::bounding_box()
|
|||
return m_bounding_box;
|
||||
}
|
||||
|
||||
BoundingBoxf3 ModelObject::tight_bounding_box(bool include_modifiers) const
|
||||
{
|
||||
BoundingBoxf3 bb;
|
||||
|
||||
for (const ModelVolume* vol : this->volumes)
|
||||
{
|
||||
if (include_modifiers || !vol->modifier)
|
||||
{
|
||||
for (const ModelInstance* inst : this->instances)
|
||||
{
|
||||
double c = cos(inst->rotation);
|
||||
double s = sin(inst->rotation);
|
||||
|
||||
for (int f = 0; f < vol->mesh.stl.stats.number_of_facets; ++f)
|
||||
{
|
||||
const stl_facet& facet = vol->mesh.stl.facet_start[f];
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
// original point
|
||||
const stl_vertex& v = facet.vertex[i];
|
||||
Pointf3 p((double)v.x, (double)v.y, (double)v.z);
|
||||
|
||||
// scale
|
||||
p.x *= inst->scaling_factor;
|
||||
p.y *= inst->scaling_factor;
|
||||
p.z *= inst->scaling_factor;
|
||||
|
||||
// rotate Z
|
||||
double x = p.x;
|
||||
double y = p.y;
|
||||
p.x = c * x - s * y;
|
||||
p.y = s * x + c * y;
|
||||
|
||||
// translate
|
||||
p.x += inst->offset.x;
|
||||
p.y += inst->offset.y;
|
||||
|
||||
bb.merge(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
// A mesh containing all transformed instances of this object.
|
||||
TriangleMesh ModelObject::mesh() const
|
||||
{
|
||||
|
@ -671,7 +772,7 @@ void ModelObject::transform(const float* matrix3x4)
|
|||
v->mesh.transform(matrix3x4);
|
||||
}
|
||||
|
||||
origin_translation = Pointf3(0.0f, 0.0f, 0.0f);
|
||||
origin_translation = Pointf3(0.0, 0.0, 0.0);
|
||||
invalidate_bounding_box();
|
||||
}
|
||||
|
||||
|
|
|
@ -102,8 +102,12 @@ public:
|
|||
|
||||
// Returns the bounding box of the transformed instances.
|
||||
// This bounding box is approximate and not snug.
|
||||
BoundingBoxf3 bounding_box();
|
||||
// This bounding box is being cached.
|
||||
const BoundingBoxf3& bounding_box();
|
||||
void invalidate_bounding_box() { m_bounding_box_valid = false; }
|
||||
// Returns a snug bounding box of the transformed instances.
|
||||
// This bounding box is not being cached.
|
||||
BoundingBoxf3 tight_bounding_box(bool include_modifiers) const;
|
||||
|
||||
// A mesh containing all transformed instances of this object.
|
||||
TriangleMesh mesh() const;
|
||||
|
@ -260,7 +264,10 @@ public:
|
|||
void delete_material(t_model_material_id material_id);
|
||||
void clear_materials();
|
||||
bool add_default_instances();
|
||||
BoundingBoxf3 bounding_box();
|
||||
// Returns approximate axis aligned bounding box of this model
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
// Returns tight axis aligned bounding box of this model
|
||||
BoundingBoxf3 transformed_bounding_box() const;
|
||||
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;
|
||||
|
@ -273,6 +280,13 @@ public:
|
|||
bool looks_like_multipart_object() const;
|
||||
void convert_multipart_object();
|
||||
|
||||
// Ensures that the min z of the model is not negative
|
||||
void adjust_min_z();
|
||||
|
||||
// Returs true if this model is contained into the print volume defined inside the given config
|
||||
bool fits_print_volume(const DynamicPrintConfig* config) const;
|
||||
bool fits_print_volume(const FullPrintConfig &config) const;
|
||||
|
||||
void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
|
||||
};
|
||||
|
||||
|
|
|
@ -120,6 +120,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||
"layer_gcode",
|
||||
"min_fan_speed",
|
||||
"max_fan_speed",
|
||||
"max_print_height",
|
||||
"min_print_speed",
|
||||
"max_print_speed",
|
||||
"max_volumetric_speed",
|
||||
|
@ -507,6 +508,15 @@ bool Print::has_skirt() const
|
|||
|
||||
std::string Print::validate() const
|
||||
{
|
||||
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.bed_shape.values));
|
||||
BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config.max_print_height));
|
||||
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
|
||||
print_volume.min.z = -1e10;
|
||||
for (PrintObject *po : this->objects) {
|
||||
if (! print_volume.contains(po->model_object()->tight_bounding_box(false)))
|
||||
return "Some objects are outside of the print volume.";
|
||||
}
|
||||
|
||||
if (this->config.complete_objects) {
|
||||
// Check horizontal clearance.
|
||||
{
|
||||
|
|
|
@ -803,6 +803,13 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 0. };
|
||||
|
||||
def = this->add("max_print_height", coFloat);
|
||||
def->label = L("Max print height");
|
||||
def->tooltip = L("Set this to the maximum height that can be reached by your extruder while printing.");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "max-print-height=f";
|
||||
def->default_value = new ConfigOptionFloat(200.0);
|
||||
|
||||
def = this->add("max_print_speed", coFloat);
|
||||
def->label = L("Max print speed");
|
||||
def->tooltip = L("When setting other speed settings to 0 Slic3r will autocalculate the optimal speed "
|
||||
|
@ -1520,12 +1527,10 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->enum_values.push_back("rectilinear");
|
||||
def->enum_values.push_back("rectilinear-grid");
|
||||
def->enum_values.push_back("honeycomb");
|
||||
def->enum_values.push_back("pillars");
|
||||
def->enum_labels.push_back("rectilinear");
|
||||
def->enum_labels.push_back("rectilinear grid");
|
||||
def->enum_labels.push_back("honeycomb");
|
||||
def->enum_labels.push_back("pillars");
|
||||
def->default_value = new ConfigOptionEnum<SupportMaterialPattern>(smpPillars);
|
||||
def->default_value = new ConfigOptionEnum<SupportMaterialPattern>(smpRectilinear);
|
||||
|
||||
def = this->add("support_material_spacing", coFloat);
|
||||
def->label = L("Pattern spacing");
|
||||
|
@ -1797,6 +1802,9 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||
values is a dirty hack and will need to be removed sometime in the future, but it
|
||||
will avoid lots of complaints for now. */
|
||||
value = "0";
|
||||
} else if (opt_key == "support_material_pattern" && value == "pillars") {
|
||||
// Slic3r PE does not support the pillars. They never worked well.
|
||||
value = "rectilinear";
|
||||
} else if (opt_key == "support_material_threshold" && value == "0") {
|
||||
// 0 used to be automatic threshold, but we introduced percent values so let's
|
||||
// transform it into the default value
|
||||
|
|
|
@ -33,7 +33,7 @@ enum InfillPattern {
|
|||
};
|
||||
|
||||
enum SupportMaterialPattern {
|
||||
smpRectilinear, smpRectilinearGrid, smpHoneycomb, smpPillars,
|
||||
smpRectilinear, smpRectilinearGrid, smpHoneycomb,
|
||||
};
|
||||
|
||||
enum SeamPosition {
|
||||
|
@ -87,7 +87,6 @@ template<> inline t_config_enum_values& ConfigOptionEnum<SupportMaterialPattern>
|
|||
keys_map["rectilinear"] = smpRectilinear;
|
||||
keys_map["rectilinear-grid"] = smpRectilinearGrid;
|
||||
keys_map["honeycomb"] = smpHoneycomb;
|
||||
keys_map["pillars"] = smpPillars;
|
||||
}
|
||||
return keys_map;
|
||||
}
|
||||
|
@ -583,6 +582,7 @@ public:
|
|||
ConfigOptionFloats max_layer_height;
|
||||
ConfigOptionInts min_fan_speed;
|
||||
ConfigOptionFloats min_layer_height;
|
||||
ConfigOptionFloat max_print_height;
|
||||
ConfigOptionFloats min_print_speed;
|
||||
ConfigOptionFloat min_skirt_length;
|
||||
ConfigOptionString notes;
|
||||
|
@ -647,6 +647,7 @@ protected:
|
|||
OPT_PTR(max_layer_height);
|
||||
OPT_PTR(min_fan_speed);
|
||||
OPT_PTR(min_layer_height);
|
||||
OPT_PTR(max_print_height);
|
||||
OPT_PTR(min_print_speed);
|
||||
OPT_PTR(min_skirt_length);
|
||||
OPT_PTR(notes);
|
||||
|
|
|
@ -2491,7 +2491,6 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
|||
infill_pattern = ipRectilinear;
|
||||
break;
|
||||
case smpHoneycomb:
|
||||
case smpPillars:
|
||||
infill_pattern = ipHoneycomb;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -987,101 +987,227 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build a map of lines by edge_a_id and a_id.
|
||||
std::vector<IntersectionLine*> by_edge_a_id;
|
||||
std::vector<IntersectionLine*> by_a_id;
|
||||
by_edge_a_id.reserve(lines.size());
|
||||
by_a_id.reserve(lines.size());
|
||||
for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++ line) {
|
||||
if (! line->skip) {
|
||||
if (line->edge_a_id != -1)
|
||||
by_edge_a_id.push_back(&(*line)); // [line->edge_a_id].push_back();
|
||||
if (line->a_id != -1)
|
||||
by_a_id.push_back(&(*line)); // [line->a_id].push_back(&(*line));
|
||||
|
||||
struct OpenPolyline {
|
||||
OpenPolyline() {};
|
||||
OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) :
|
||||
start(start), end(end), points(std::move(points)), consumed(false) {}
|
||||
void reverse() {
|
||||
std::swap(start, end);
|
||||
std::reverse(points.begin(), points.end());
|
||||
}
|
||||
IntersectionReference start;
|
||||
IntersectionReference end;
|
||||
Points points;
|
||||
bool consumed;
|
||||
};
|
||||
std::vector<OpenPolyline> open_polylines;
|
||||
{
|
||||
// Build a map of lines by edge_a_id and a_id.
|
||||
std::vector<IntersectionLine*> by_edge_a_id;
|
||||
std::vector<IntersectionLine*> by_a_id;
|
||||
by_edge_a_id.reserve(lines.size());
|
||||
by_a_id.reserve(lines.size());
|
||||
for (IntersectionLine &line : lines) {
|
||||
if (! line.skip) {
|
||||
if (line.edge_a_id != -1)
|
||||
by_edge_a_id.emplace_back(&line);
|
||||
if (line.a_id != -1)
|
||||
by_a_id.emplace_back(&line);
|
||||
}
|
||||
}
|
||||
auto by_edge_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->edge_a_id < il2->edge_a_id; };
|
||||
auto by_vertex_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->a_id < il2->a_id; };
|
||||
std::sort(by_edge_a_id.begin(), by_edge_a_id.end(), by_edge_lower);
|
||||
std::sort(by_a_id.begin(), by_a_id.end(), by_vertex_lower);
|
||||
// Chain the segments with a greedy algorithm, collect the loops and unclosed polylines.
|
||||
IntersectionLines::iterator it_line_seed = lines.begin();
|
||||
for (;;) {
|
||||
// take first spare line and start a new loop
|
||||
IntersectionLine *first_line = nullptr;
|
||||
for (; it_line_seed != lines.end(); ++ it_line_seed)
|
||||
if (! it_line_seed->skip) {
|
||||
first_line = &(*it_line_seed ++);
|
||||
break;
|
||||
}
|
||||
if (first_line == nullptr)
|
||||
break;
|
||||
first_line->skip = true;
|
||||
Points loop_pts;
|
||||
loop_pts.emplace_back(first_line->a);
|
||||
IntersectionLine *last_line = first_line;
|
||||
|
||||
/*
|
||||
printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
|
||||
first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id,
|
||||
first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y);
|
||||
*/
|
||||
|
||||
IntersectionLine key;
|
||||
for (;;) {
|
||||
// find a line starting where last one finishes
|
||||
IntersectionLine* next_line = nullptr;
|
||||
if (last_line->edge_b_id != -1) {
|
||||
key.edge_a_id = last_line->edge_b_id;
|
||||
auto it_begin = std::lower_bound(by_edge_a_id.begin(), by_edge_a_id.end(), &key, by_edge_lower);
|
||||
if (it_begin != by_edge_a_id.end()) {
|
||||
auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
|
||||
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
|
||||
if (! (*it_line)->skip) {
|
||||
next_line = *it_line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_line == nullptr && last_line->b_id != -1) {
|
||||
key.a_id = last_line->b_id;
|
||||
auto it_begin = std::lower_bound(by_a_id.begin(), by_a_id.end(), &key, by_vertex_lower);
|
||||
if (it_begin != by_a_id.end()) {
|
||||
auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
|
||||
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
|
||||
if (! (*it_line)->skip) {
|
||||
next_line = *it_line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_line == nullptr) {
|
||||
// Check whether we closed this loop.
|
||||
if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) ||
|
||||
(first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
|
||||
// The current loop is complete. Add it to the output.
|
||||
loops->emplace_back(std::move(loop_pts));
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
|
||||
#endif
|
||||
} else {
|
||||
// This is an open polyline. Add it to the list of open polylines. These open polylines will processed later.
|
||||
loop_pts.emplace_back(last_line->b);
|
||||
open_polylines.emplace_back(OpenPolyline(
|
||||
IntersectionReference(first_line->a_id, first_line->edge_a_id),
|
||||
IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*
|
||||
printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
|
||||
next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id,
|
||||
next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y);
|
||||
*/
|
||||
loop_pts.emplace_back(next_line->a);
|
||||
last_line = next_line;
|
||||
next_line->skip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto by_edge_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->edge_a_id < il2->edge_a_id; };
|
||||
auto by_vertex_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->a_id < il2->a_id; };
|
||||
std::sort(by_edge_a_id.begin(), by_edge_a_id.end(), by_edge_lower);
|
||||
std::sort(by_a_id.begin(), by_a_id.end(), by_vertex_lower);
|
||||
|
||||
IntersectionLines::iterator it_line_seed = lines.begin();
|
||||
CYCLE: while (1) {
|
||||
// take first spare line and start a new loop
|
||||
IntersectionLine *first_line = nullptr;
|
||||
for (; it_line_seed != lines.end(); ++ it_line_seed)
|
||||
if (! it_line_seed->skip) {
|
||||
first_line = &(*it_line_seed ++);
|
||||
break;
|
||||
}
|
||||
if (first_line == nullptr)
|
||||
break;
|
||||
first_line->skip = true;
|
||||
Points loop_pts;
|
||||
loop_pts.push_back(first_line->a);
|
||||
IntersectionLine *last_line = first_line;
|
||||
|
||||
/*
|
||||
printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
|
||||
first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id,
|
||||
first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y);
|
||||
*/
|
||||
|
||||
IntersectionLine key;
|
||||
for (;;) {
|
||||
// find a line starting where last one finishes
|
||||
IntersectionLine* next_line = nullptr;
|
||||
if (last_line->edge_b_id != -1) {
|
||||
key.edge_a_id = last_line->edge_b_id;
|
||||
auto it_begin = std::lower_bound(by_edge_a_id.begin(), by_edge_a_id.end(), &key, by_edge_lower);
|
||||
if (it_begin != by_edge_a_id.end()) {
|
||||
auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
|
||||
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
|
||||
if (! (*it_line)->skip) {
|
||||
next_line = *it_line;
|
||||
break;
|
||||
}
|
||||
// Now process the open polylines.
|
||||
if (! open_polylines.empty()) {
|
||||
// Store the end points of open_polylines into vectors sorted
|
||||
struct OpenPolylineEnd {
|
||||
OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {}
|
||||
OpenPolyline *polyline;
|
||||
// Is it the start or end point?
|
||||
bool start;
|
||||
const IntersectionReference& ipref() const { return start ? polyline->start : polyline->end; }
|
||||
int point_id() const { return ipref().point_id; }
|
||||
int edge_id () const { return ipref().edge_id; }
|
||||
};
|
||||
auto by_edge_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.edge_id() < ope2.edge_id(); };
|
||||
auto by_point_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.point_id() < ope2.point_id(); };
|
||||
std::vector<OpenPolylineEnd> by_edge_id;
|
||||
std::vector<OpenPolylineEnd> by_point_id;
|
||||
by_edge_id.reserve(2 * open_polylines.size());
|
||||
by_point_id.reserve(2 * open_polylines.size());
|
||||
for (OpenPolyline &opl : open_polylines) {
|
||||
if (opl.start.edge_id != -1)
|
||||
by_edge_id .emplace_back(OpenPolylineEnd(&opl, true));
|
||||
if (opl.end.edge_id != -1)
|
||||
by_edge_id .emplace_back(OpenPolylineEnd(&opl, false));
|
||||
if (opl.start.point_id != -1)
|
||||
by_point_id.emplace_back(OpenPolylineEnd(&opl, true));
|
||||
if (opl.end.point_id != -1)
|
||||
by_point_id.emplace_back(OpenPolylineEnd(&opl, false));
|
||||
}
|
||||
std::sort(by_edge_id .begin(), by_edge_id .end(), by_edge_lower);
|
||||
std::sort(by_point_id.begin(), by_point_id.end(), by_point_lower);
|
||||
|
||||
// Try to connect the loops.
|
||||
for (OpenPolyline &opl : open_polylines) {
|
||||
if (opl.consumed)
|
||||
continue;
|
||||
opl.consumed = true;
|
||||
OpenPolylineEnd end(&opl, false);
|
||||
for (;;) {
|
||||
// find a line starting where last one finishes
|
||||
OpenPolylineEnd* next_start = nullptr;
|
||||
if (end.edge_id() != -1) {
|
||||
auto it_begin = std::lower_bound(by_edge_id.begin(), by_edge_id.end(), end, by_edge_lower);
|
||||
if (it_begin != by_edge_id.end()) {
|
||||
auto it_end = std::upper_bound(it_begin, by_edge_id.end(), end, by_edge_lower);
|
||||
for (auto it_edge = it_begin; it_edge != it_end; ++ it_edge)
|
||||
if (! it_edge->polyline->consumed) {
|
||||
next_start = &(*it_edge);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_line == nullptr && last_line->b_id != -1) {
|
||||
key.a_id = last_line->b_id;
|
||||
auto it_begin = std::lower_bound(by_a_id.begin(), by_a_id.end(), &key, by_vertex_lower);
|
||||
if (it_begin != by_a_id.end()) {
|
||||
auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
|
||||
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
|
||||
if (! (*it_line)->skip) {
|
||||
next_line = *it_line;
|
||||
break;
|
||||
}
|
||||
if (next_start == nullptr && end.point_id() != -1) {
|
||||
auto it_begin = std::lower_bound(by_point_id.begin(), by_point_id.end(), end, by_point_lower);
|
||||
if (it_begin != by_point_id.end()) {
|
||||
auto it_end = std::upper_bound(it_begin, by_point_id.end(), end, by_point_lower);
|
||||
for (auto it_point = it_begin; it_point != it_end; ++ it_point)
|
||||
if (! it_point->polyline->consumed) {
|
||||
next_start = &(*it_point);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_line == nullptr) {
|
||||
// check whether we closed this loop
|
||||
if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) ||
|
||||
(first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
|
||||
// loop is complete
|
||||
loops->emplace_back(std::move(loop_pts));
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
|
||||
#endif
|
||||
goto CYCLE;
|
||||
if (next_start == nullptr) {
|
||||
// The current loop could not be closed. Unmark the segment.
|
||||
opl.consumed = false;
|
||||
break;
|
||||
}
|
||||
// Attach this polyline to the end of the initial polyline.
|
||||
if (next_start->start) {
|
||||
auto it = next_start->polyline->points.begin();
|
||||
std::copy(++ it, next_start->polyline->points.end(), back_inserter(opl.points));
|
||||
//opl.points.insert(opl.points.back(), ++ it, next_start->polyline->points.end());
|
||||
} else {
|
||||
auto it = next_start->polyline->points.rbegin();
|
||||
std::copy(++ it, next_start->polyline->points.rend(), back_inserter(opl.points));
|
||||
//opl.points.insert(opl.points.back(), ++ it, next_start->polyline->points.rend());
|
||||
}
|
||||
// we can't close this loop!
|
||||
//// push @failed_loops, [@loop];
|
||||
//#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
printf(" Unable to close this loop having %d points\n", (int)loop_pts.size());
|
||||
//#endif
|
||||
goto CYCLE;
|
||||
end = *next_start;
|
||||
end.start = !end.start;
|
||||
next_start->polyline->points.clear();
|
||||
next_start->polyline->consumed = true;
|
||||
// Check whether we closed this loop.
|
||||
const IntersectionReference &ip1 = opl.start;
|
||||
const IntersectionReference &ip2 = end.ipref();
|
||||
if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) ||
|
||||
(ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
|
||||
// The current loop is complete. Add it to the output.
|
||||
assert(opl.points.front().point_id == opl.points.back().point_id);
|
||||
assert(opl.points.front().edge_id == opl.points.back().edge_id);
|
||||
// Remove the duplicate last point.
|
||||
opl.points.pop_back();
|
||||
if (opl.points.size() >= 3) {
|
||||
// The closed polygon is patched from pieces with messed up orientation, therefore
|
||||
// the orientation of the patched up polygon is not known.
|
||||
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
|
||||
double area = 0.;
|
||||
for (size_t i = 0, j = opl.points.size() - 1; i < opl.points.size(); j = i ++)
|
||||
area += double(opl.points[j].x + opl.points[i].x) * double(opl.points[i].y - opl.points[j].y);
|
||||
if (area < 0)
|
||||
std::reverse(opl.points.begin(), opl.points.end());
|
||||
loops->emplace_back(std::move(opl.points));
|
||||
}
|
||||
opl.points.clear();
|
||||
break;
|
||||
}
|
||||
// Continue with the current loop.
|
||||
}
|
||||
/*
|
||||
printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
|
||||
next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id,
|
||||
next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y);
|
||||
*/
|
||||
loop_pts.push_back(next_line->a);
|
||||
last_line = next_line;
|
||||
next_line->skip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,11 +85,11 @@ enum FacetEdgeType {
|
|||
feHorizontal
|
||||
};
|
||||
|
||||
class IntersectionPoint : public Point
|
||||
class IntersectionReference
|
||||
{
|
||||
public:
|
||||
IntersectionPoint() : point_id(-1), edge_id(-1) {};
|
||||
// Inherits coord_t x, y
|
||||
IntersectionReference() : point_id(-1), edge_id(-1) {};
|
||||
IntersectionReference(int point_id, int edge_id) : point_id(point_id), edge_id(edge_id) {}
|
||||
// Where is this intersection point located? On mesh vertex or mesh edge?
|
||||
// Only one of the following will be set, the other will remain set to -1.
|
||||
// Index of the mesh vertex.
|
||||
|
@ -98,6 +98,15 @@ public:
|
|||
int edge_id;
|
||||
};
|
||||
|
||||
class IntersectionPoint : public Point, public IntersectionReference
|
||||
{
|
||||
public:
|
||||
IntersectionPoint() {};
|
||||
IntersectionPoint(int point_id, int edge_id, const Point &pt) : IntersectionReference(point_id, edge_id), Point(pt) {}
|
||||
IntersectionPoint(const IntersectionReference &ir, const Point &pt) : IntersectionReference(ir), Point(pt) {}
|
||||
// Inherits coord_t x, y
|
||||
};
|
||||
|
||||
class IntersectionLine : public Line
|
||||
{
|
||||
public:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue