Merge branch 'master' of https://github.com/prusa3d/Slic3r into gcode_preview

This commit is contained in:
Enrico Turri 2018-03-20 08:33:53 +01:00
commit 99f7bd90db
63 changed files with 6132 additions and 12448 deletions

View file

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

View file

@ -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 &para
// 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 &para
// 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2491,7 +2491,6 @@ void PrintObjectSupportMaterial::generate_toolpaths(
infill_pattern = ipRectilinear;
break;
case smpHoneycomb:
case smpPillars:
infill_pattern = ipHoneycomb;
break;
}

View file

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

View file

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