Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsupermerill <merill@free.fr>2022-04-01 02:14:24 +0300
committersupermerill <merill@free.fr>2022-04-01 02:14:24 +0300
commit6fcfe8fc9b70e2e5b0aefd122153c27815bb7c8f (patch)
tree1c5d7037d9df72bcd4a3055e5308e35e5709ca76
parent5d375c92997c1b2b4b419539f9e67ada7e4ab128 (diff)
parent76e78c27e25758427c15c194cdd29ce5ff1f6360 (diff)
2.4 fixes. also brim speed/accel2.4.58.2
m---------resources/profiles0
-rw-r--r--resources/ui_layout/default/print.ui2
-rw-r--r--resources/ui_layout/default/version.ini2
-rw-r--r--resources/ui_layout/example/print.ui2
-rw-r--r--resources/ui_layout/example/version.ini2
-rw-r--r--src/libslic3r/AppConfig.cpp31
-rw-r--r--src/libslic3r/Config.cpp8
-rw-r--r--src/libslic3r/GCode.cpp38
-rw-r--r--src/libslic3r/GCode/SeamPlacer.cpp19
-rw-r--r--src/libslic3r/Geometry/MedialAxis.cpp135
-rw-r--r--src/libslic3r/Geometry/MedialAxis.hpp13
-rw-r--r--src/libslic3r/MedialAxis.cpp2255
-rw-r--r--src/libslic3r/MedialAxis.hpp122
-rw-r--r--src/libslic3r/PerimeterGenerator.cpp3
-rw-r--r--src/libslic3r/Preset.cpp2
-rw-r--r--src/libslic3r/PrintConfig.cpp38
-rw-r--r--src/libslic3r/PrintConfig.hpp2
-rw-r--r--src/libslic3r/SupportMaterial.cpp2
-rw-r--r--src/slic3r/GUI/ConfigManipulation.cpp4
-rw-r--r--src/slic3r/GUI/ConfigWizard.cpp22
-rw-r--r--src/slic3r/GUI/Field.cpp2
-rw-r--r--src/slic3r/GUI/FreeCADDialog.cpp50
22 files changed, 287 insertions, 2467 deletions
diff --git a/resources/profiles b/resources/profiles
-Subproject 3ee59ac218a1b80592866562447a041b19a4226
+Subproject a918766b3ffa79da91768b0f11312336382ecc2
diff --git a/resources/ui_layout/default/print.ui b/resources/ui_layout/default/print.ui
index 06cade01b..abb0cf17b 100644
--- a/resources/ui_layout/default/print.ui
+++ b/resources/ui_layout/default/print.ui
@@ -274,6 +274,7 @@ group:label_width$8:sidetext_width$7:Speed for print moves
line:Support speed
setting:width$4:support_material_speed
setting:width$4:support_material_interface_speed
+ setting:width$4:brim_speed
line:Bridge speed
setting:width$4:bridge_speed
setting:width$4:bridge_speed_internal
@@ -315,6 +316,7 @@ group:label_width$9:sidetext_width$8:Acceleration control (advanced)
line:Support acceleration
setting:width$4:support_material_acceleration
setting:width$4:support_material_interface_acceleration
+ setting:width$4:brim_acceleration
line:Bridge acceleration
setting:width$4:bridge_acceleration
setting:width$4:bridge_internal_acceleration
diff --git a/resources/ui_layout/default/version.ini b/resources/ui_layout/default/version.ini
index 0baa24148..d2c8e9a3d 100644
--- a/resources/ui_layout/default/version.ini
+++ b/resources/ui_layout/default/version.ini
@@ -1,4 +1,4 @@
name = Standard
-version = 2.4.58.1
+version = 2.4.58.2
description = Default layout for superslicer.
\ No newline at end of file
diff --git a/resources/ui_layout/example/print.ui b/resources/ui_layout/example/print.ui
index 06cade01b..abb0cf17b 100644
--- a/resources/ui_layout/example/print.ui
+++ b/resources/ui_layout/example/print.ui
@@ -274,6 +274,7 @@ group:label_width$8:sidetext_width$7:Speed for print moves
line:Support speed
setting:width$4:support_material_speed
setting:width$4:support_material_interface_speed
+ setting:width$4:brim_speed
line:Bridge speed
setting:width$4:bridge_speed
setting:width$4:bridge_speed_internal
@@ -315,6 +316,7 @@ group:label_width$9:sidetext_width$8:Acceleration control (advanced)
line:Support acceleration
setting:width$4:support_material_acceleration
setting:width$4:support_material_interface_acceleration
+ setting:width$4:brim_acceleration
line:Bridge acceleration
setting:width$4:bridge_acceleration
setting:width$4:bridge_internal_acceleration
diff --git a/resources/ui_layout/example/version.ini b/resources/ui_layout/example/version.ini
index abf0e7a4d..8439b0b0c 100644
--- a/resources/ui_layout/example/version.ini
+++ b/resources/ui_layout/example/version.ini
@@ -1,4 +1,4 @@
name = Example of another layout
-version = 2.4.58.1
+version = 2.4.58.2
description = More tags for ps/susi only settings, example of new quick settings.
\ No newline at end of file
diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp
index cbc41dca5..bec00be8f 100644
--- a/src/libslic3r/AppConfig.cpp
+++ b/src/libslic3r/AppConfig.cpp
@@ -259,8 +259,37 @@ void AppConfig::set_defaults()
if (get("drop_project_action").empty())
set("drop_project_action", "1");
- if (get("freecad_path").empty())
+ if (get("freecad_path").empty() || get("freecad_path") == ".") {
set("freecad_path", ".");
+ //try to find it
+#ifdef _WIN32
+ //windows
+ boost::filesystem::path prg_files = "C:/Program Files";
+ boost::filesystem::path freecad_path;
+ if (boost::filesystem::exists(prg_files)) {
+ for (boost::filesystem::directory_entry& prg_dir : boost::filesystem::directory_iterator(prg_files)) {
+ if (prg_dir.status().type() == boost::filesystem::file_type::directory_file
+ && boost::starts_with(prg_dir.path().filename().string(), "FreeCAD")
+ && (freecad_path.empty() || freecad_path.filename().string() < prg_dir.path().filename().string())) {
+ freecad_path = prg_dir.path();
+ }
+ }
+ }
+ if (!freecad_path.empty())
+ set("freecad_path", freecad_path.string());
+#else
+#ifdef __APPLE__
+ //apple
+ if (boost::filesystem::exists("/Applications/FreeCAD.app/Contents/Frameworks/FreeCAD"))
+ set("freecad_path", "/Applications/FreeCAD.app/Contents/Frameworks/FreeCAD");
+
+#else
+ // linux
+ if (boost::filesystem::exists("/usr/local/bin/FreeCAD"))
+ set("freecad_path", "/usr/local/bin/FreeCAD");
+#endif
+#endif
+ }
if (get("show_overwrite_dialog").empty())
set("show_overwrite_dialog", "1");
diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp
index 455bf4aff..b51de4c8f 100644
--- a/src/libslic3r/Config.cpp
+++ b/src/libslic3r/Config.cpp
@@ -1002,6 +1002,14 @@ size_t ConfigBase::load_from_gcode_string_legacy(ConfigBase& config, const char*
}
catch (UnknownOptionException & /* e */) {
// ignore
+ } catch (BadOptionValueException & e) {
+ if (substitutions.rule == ForwardCompatibilitySubstitutionRule::Disable)
+ throw e;
+ // log the error
+ const ConfigDef* def = config.def();
+ if (def == nullptr) throw e;
+ const ConfigOptionDef* optdef = def->get(std::string(key, key_end));
+ substitutions.substitutions.emplace_back(optdef, std::string(value, end), ConfigOptionUniquePtr(optdef->default_value->clone()));
}
end = start;
}
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index a79f632a2..12c227ac5 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -943,10 +943,8 @@ namespace DoExport {
excluded.insert(erMixed);
excluded.insert(erNone);
excluded.insert(erWipeTower);
- if (config->option("perimeter_speed") != nullptr && config->get_computed_value("perimeter_speed") != 0) {
+ if (config->option("perimeter_speed") != nullptr && config->get_computed_value("perimeter_speed") != 0)
excluded.insert(erPerimeter);
- excluded.insert(erSkirt);
- }
if (config->option("external_perimeter_speed") != nullptr && config->get_computed_value("external_perimeter_speed") != 0)
excluded.insert(erExternalPerimeter);
if (config->option("overhangs_speed") != nullptr && config->get_computed_value("overhangs_speed") != 0)
@@ -969,6 +967,8 @@ namespace DoExport {
excluded.insert(erSupportMaterial);
if (config->option("support_material_interface_speed") != nullptr && config->get_computed_value("support_material_interface_speed") != 0)
excluded.insert(erSupportMaterialInterface);
+ if (config->option("brim_speed") != nullptr && config->get_computed_value("brim_speed") != 0)
+ excluded.insert(erSkirt);
}
virtual void use(const ExtrusionPath& path) override {
if (excluded.find(path.role()) == excluded.end())
@@ -2982,8 +2982,7 @@ GCode::LayerResult GCode::process_layer(
path.height = layer_skirt_flow.height();
path.mm3_per_mm = mm3_per_mm;
}
- //FIXME using the support_material_speed of the 1st object printed.
- gcode += this->extrude_loop(loop, "", m_config.support_material_speed.value);
+ gcode += this->extrude_loop(loop, "");
}
m_avoid_crossing_perimeters.use_external_mp(false);
// Allow a straight travel move to the first object point if this is the first layer (but don't in next layers).
@@ -3000,7 +2999,7 @@ GCode::LayerResult GCode::process_layer(
for (const ExtrusionEntity* brim_entity : print.brim().entities()) {
//if first layer, ask for a bigger lift for travel to each brim, to be on the safe side
set_extra_lift(m_last_layer_z, layer.id(), print.config(), m_writer, extruder_id);
- gcode += this->extrude_entity(*brim_entity, "Brim", m_config.support_material_speed.value);
+ gcode += this->extrude_entity(*brim_entity, "Brim");
}
m_brim_done = true;
m_avoid_crossing_perimeters.use_external_mp(false);
@@ -3019,10 +3018,10 @@ GCode::LayerResult GCode::process_layer(
if (this->m_layer != nullptr && (this->m_layer->id() < m_config.skirt_height || print.has_infinite_skirt() )) {
if(first_layer && print.skirt_first_layer())
for (const ExtrusionEntity* ee : print_object->skirt_first_layer()->entities())
- gcode += this->extrude_entity(*ee, "", m_config.support_material_speed.value);
+ gcode += this->extrude_entity(*ee, "");
else
for (const ExtrusionEntity *ee : print_object->skirt().entities())
- gcode += this->extrude_entity(*ee, "", m_config.support_material_speed.value);
+ gcode += this->extrude_entity(*ee, "");
}
}
//extrude object-only brim
@@ -3034,7 +3033,7 @@ GCode::LayerResult GCode::process_layer(
if (this->m_layer != nullptr && this->m_layer->id() == 0) {
m_avoid_crossing_perimeters.use_external_mp(true);
for (const ExtrusionEntity *ee : print_object->brim().entities())
- gcode += this->extrude_entity(*ee, "brim", m_config.support_material_speed.value);
+ gcode += this->extrude_entity(*ee, "brim");
m_avoid_crossing_perimeters.use_external_mp(false);
m_avoid_crossing_perimeters.disable_once();
}
@@ -4197,8 +4196,8 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
std::string gcode;
if (! support_fills.entities().empty()) {
- const double support_speed = m_config.support_material_speed.value;
- const double support_interface_speed = m_config.support_material_interface_speed.get_abs_value(support_speed);
+ const double support_speed = m_config.get_computed_value("support_material_speed");
+ const double support_interface_speed = m_config.get_computed_value("support_material_interface_speed");
for (const ExtrusionEntity *ee : support_fills.entities()) {
ExtrusionRole role = ee->role();
assert(role == erSupportMaterial || role == erSupportMaterialInterface || role == erMixed);
@@ -4466,6 +4465,12 @@ double_t GCode::_compute_speed_mm_per_sec(const ExtrusionPath& path, double spee
speed = m_config.get_computed_value("travel_speed");
} else if (path.role() == erMilling) {
speed = m_config.get_computed_value("milling_speed");
+ } else if (path.role() == erSupportMaterial) {
+ speed = m_config.get_computed_value("support_material_speed");
+ } else if (path.role() == erSupportMaterialInterface) {
+ speed = m_config.get_computed_value("support_material_interface_speed");
+ } else if (path.role() == erSkirt) {
+ speed = m_config.get_computed_value("brim_speed");
} else {
throw Slic3r::InvalidArgument("Invalid speed");
}
@@ -4622,7 +4627,6 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string
}
goto topSolidInfill;
case erSupportMaterial:
- case erSkirt:
case erWipeTower:
supportMaterial:
if (m_config.support_material_acceleration.value > 0) {
@@ -4640,6 +4644,16 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string
}
}
goto supportMaterial;
+ case erSkirt:
+ //skirtBrim:
+ if (m_config.brim_acceleration.value > 0) {
+ double brim_acceleration = m_config.get_computed_value("brim_acceleration");
+ if (brim_acceleration > 0) {
+ acceleration = brim_acceleration;
+ break;
+ }
+ }
+ goto supportMaterial;
case erBridgeInfill:
bridgeInfill:
if (m_config.bridge_acceleration.value > 0) {
diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp
index fda166eb3..005c46e60 100644
--- a/src/libslic3r/GCode/SeamPlacer.cpp
+++ b/src/libslic3r/GCode/SeamPlacer.cpp
@@ -551,6 +551,7 @@ Point SeamPlacer::calculate_seam(const Layer& layer, SeamPosition seam_position,
// Look for all lambda-seam-modifiers below current z, choose the highest one
ModelVolume* v_lambda_seam = nullptr;
Vec3d lambda_pos;
+ double lambda_z;
double lambda_dist;
double lambda_radius;
//get model_instance (like from po->model_object()->instances, but we don't have the index for that array)
@@ -558,28 +559,34 @@ Point SeamPlacer::calculate_seam(const Layer& layer, SeamPosition seam_position,
for (ModelVolume* v : po->model_object()->volumes) {
if (v->is_seam_position()) {
//xy in object coordinates, z in plater coordinates
- Vec3d test_lambda_pos = model_instance->transform_vector(v->get_offset(), true);
+ // created/moved shpere have offset in their transformation, and loaded ones have their loaded transformation in the source transformation.
+ Vec3d test_lambda_pos = model_instance->transform_vector((v->get_transformation() * v->source.transform).get_offset(), false);
+ // remove shift, as we used the transform_vector(.., FALSE). that way, we have a correct z vs the layer height, and same for the x and y vs polygon.
+ test_lambda_pos.x() -= unscaled(po->instances()[print_object_instance_idx].shift.x());
+ test_lambda_pos.y() -= unscaled(po->instances()[print_object_instance_idx].shift.y());
+ double test_lambda_z = std::abs(layer.print_z - test_lambda_pos.z());
Point xy_lambda(scale_(test_lambda_pos.x()), scale_(test_lambda_pos.y()));
Point nearest = polygon.point_projection(xy_lambda);
Vec3d polygon_3dpoint{ unscaled(nearest.x()), unscaled(nearest.y()), (double)layer.print_z };
double test_lambda_dist = (polygon_3dpoint - test_lambda_pos).norm();
double sphere_radius = po->model_object()->instance_bounding_box(0, true).size().x() / 2;
- //if (test_lambda_dist > sphere_radius)
- // continue;
- //use this one if the first or nearer (in z)
- if (v_lambda_seam == nullptr || lambda_dist > test_lambda_dist) {
+
+ //use this one if the first or nearer (in z, or in xy if same z)
+ if (v_lambda_seam == nullptr
+ || ( lambda_z > test_lambda_z )
+ || ( lambda_z == test_lambda_z && lambda_dist > test_lambda_dist ) ){
v_lambda_seam = v;
lambda_pos = test_lambda_pos;
lambda_radius = sphere_radius;
lambda_dist = test_lambda_dist;
+ lambda_z = test_lambda_z;
}
}
}
if (v_lambda_seam != nullptr) {
- lambda_pos = model_instance->transform_vector(v_lambda_seam->get_offset(), true);
// Found, get the center point and apply rotation and scaling of Model instance. Continues to spAligned if not found or Weight set to Zero.
last_pos = Point::new_scale(lambda_pos.x(), lambda_pos.y());
// Weight is set by user and stored in the radius of the sphere
diff --git a/src/libslic3r/Geometry/MedialAxis.cpp b/src/libslic3r/Geometry/MedialAxis.cpp
index 42bffbdc0..1c8caaaf6 100644
--- a/src/libslic3r/Geometry/MedialAxis.cpp
+++ b/src/libslic3r/Geometry/MedialAxis.cpp
@@ -902,7 +902,7 @@ MedialAxis::process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* pol
bool
MedialAxis::validate_edge(const VD::edge_type* edge, Lines& lines, std::map<const VD::edge_type*, std::pair<coordf_t, coordf_t> >& thickness)
{
- // not relevant anymore... prusa aahs removed the (1 << 17) from clipper
+ // not relevant anymore... prusa has removed the (1 << 17) from clipper
const double CLIPPER_MAX_COORD_UNSCALED = 0x3FFFFFFFFFFFFFFFLL / (1 << 17);
// prevent overflows and detect almost-infinite edges
if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
@@ -1365,6 +1365,7 @@ MedialAxis::fusion_corners(ThickPolylines& pp)
//if (polyline.points.size() != 2) continue; // maybe we should have something to merge X-point to 2-point if it's near enough.
if (polyline.endpoints.first) polyline.reverse();
else if (!polyline.endpoints.second) continue;
+ if (polyline.width.back() > min_width) continue;
//check my length is small
coord_t length = (coord_t)polyline.length();
@@ -1909,6 +1910,7 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp)
bool changes = false;
for (size_t i = 0; i < pp.size(); ++i) {
ThickPolyline& polyline = pp[i];
+ bool polyline_changes = false;
// remove bits with too small extrusion
while (polyline.points.size() > 1 && polyline.width.front() < this->min_width && polyline.endpoints.first) {
//try to split if possible
@@ -1925,11 +1927,13 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp)
polyline.width.erase(polyline.width.begin());
}
changes = true;
+ polyline_changes = true;
break;
}
polyline.points.erase(polyline.points.begin());
polyline.width.erase(polyline.width.begin());
changes = true;
+ polyline_changes = true;
}
while (polyline.points.size() > 1 && polyline.width.back() < this->min_width && polyline.endpoints.second) {
//try to split if possible
@@ -1945,15 +1949,17 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp)
polyline.points.erase(polyline.points.end() - 1);
polyline.width.erase(polyline.width.end() - 1);
}
+ polyline_changes = true;
changes = true;
break;
}
polyline.points.erase(polyline.points.end() - 1);
polyline.width.erase(polyline.width.end() - 1);
+ polyline_changes = true;
changes = true;
}
//remove points and bits that comes from a "main line"
- if (polyline.points.size() < 2 || (changes && polyline.length() < this->max_width && polyline.points.size() == 2)) {
+ if (polyline.points.size() < 2 || (polyline_changes && polyline.points.size() == 2 && polyline.length() < std::max(this->min_length, std::max(polyline.width.front(), polyline.width.back()))) ) {
//remove self if too small
pp.erase(pp.begin() + i);
--i;
@@ -1963,9 +1969,111 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp)
}
void
-MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
+MedialAxis::concatenate_small_polylines(ThickPolylines& pp)
{
+ /*
+ new goal: ensure that if there is a too short segment, it will be connected with a sufficiently long one, to save it
+ */
+ coordf_t shortest_size = (coordf_t)this->min_length;
+ std::set<size_t> deleted;
+ std::vector<size_t> idx_per_size;
+ //TODO: cache the length
+ for (size_t i = 0; i < pp.size(); ++i) {
+ ThickPolyline& polyline = pp[i];
+ if (polyline.endpoints.first && polyline.endpoints.second) continue; // optimization
+ if(polyline.length() <= shortest_size)
+ idx_per_size.push_back(i);
+ }
+ std::sort(idx_per_size.begin(), idx_per_size.end(), [&pp](size_t a, size_t b) -> bool {return pp[a].length() > pp[b].length(); });
+ for (size_t idx_sorted = 0; idx_sorted < idx_per_size.size(); ++idx_sorted) {
+ if (deleted.find(idx_per_size[idx_sorted]) != deleted.end()) continue;
+ //all these polylines need to be saved
+ ThickPolyline& polyline = pp[idx_per_size[idx_sorted]];
+
+ ThickPolyline* best_candidate = nullptr;
+ float best_dot = -1;
+ coordf_t best_length = -1;
+ size_t best_idx = 0;
+
+ // find another polyline starting here
+ for (size_t j = 0; j < pp.size(); ++j) {
+ if (deleted.find(j) != deleted.end()) continue;
+ if (j == idx_per_size[idx_sorted]) continue;
+ ThickPolyline& other = pp[j];
+ if (other.endpoints.first && other.endpoints.second) continue;
+ coordf_t other_length = other.length();
+ if (other_length + polyline.length() <= shortest_size) continue; // need to be long enough to save it
+ bool me_reverse = false;
+ bool other_reverse = false;
+ if (polyline.last_point().coincides_with_epsilon(other.last_point())) {
+ other_reverse = true;
+ } else if (polyline.first_point().coincides_with_epsilon(other.last_point())) {
+ me_reverse = true;
+ other_reverse = true;
+ } else if (polyline.first_point().coincides_with_epsilon(other.first_point())) {
+ me_reverse = true;
+ } else if (!polyline.last_point().coincides_with_epsilon(other.first_point())) {
+ continue;
+ }
+
+ //find the straitest
+ Vec2d v_poly(me_reverse ? polyline.lines().front().vector().x() : polyline.lines().back().vector().x(),
+ me_reverse ? polyline.lines().front().vector().y() : polyline.lines().back().vector().y());
+ v_poly *= (1 / std::sqrt(v_poly.x() * v_poly.x() + v_poly.y() * v_poly.y()));
+ Vec2d v_other(other_reverse ? other.lines().back().vector().x() : other.lines().front().vector().x(),
+ other_reverse ? other.lines().back().vector().y() : other.lines().front().vector().y());
+ v_other *= (1 / std::sqrt(v_other.x() * v_other.x() + v_other.y() * v_other.y()));
+ float other_dot = std::abs(float(v_poly.x() * v_other.x() + v_poly.y() * v_other.y()));
+ // use the straitest one
+ // but if almost equal, use the shortest one
+ if (std::abs(other_dot - best_dot) < 0.01) {
+ if (best_length < 0 || best_length > other_length) {
+ best_candidate = &other;
+ best_idx = j;
+ best_dot = other_dot;
+ best_length = other_length;
+ }
+ }else if (other_dot > best_dot) {
+ best_candidate = &other;
+ best_idx = j;
+ best_dot = other_dot;
+ best_length = other_length;
+ }
+ }
+ if (best_candidate != nullptr && best_candidate->points.size() > 1) {
+ if (polyline.last_point().coincides_with_epsilon(best_candidate->last_point())) {
+ best_candidate->reverse();
+ } else if (polyline.first_point().coincides_with_epsilon(best_candidate->last_point())) {
+ polyline.reverse();
+ best_candidate->reverse();
+ } else if (polyline.first_point().coincides_with_epsilon(best_candidate->first_point())) {
+ polyline.reverse();
+ }
+ //intersections may create over-extrusion because the included circle can be a bit larger. We have to make it short again if needed.
+ if (polyline.points.size() > 1 && best_candidate->points.size() > 1
+ && polyline.width.back() > polyline.width[polyline.width.size() - 2]
+ && polyline.width.back() > best_candidate->width[1]) {
+ polyline.width.back() = std::min(polyline.width[polyline.width.size() - 2], best_candidate->width[1]);
+ }
+ //be far enough
+ int far_idx = 1;
+ while (far_idx < best_candidate->points.size() && polyline.last_point().coincides_with_epsilon(best_candidate->points[far_idx]))
+ far_idx++;
+ polyline.points.insert(polyline.points.end(), best_candidate->points.begin() + far_idx, best_candidate->points.end());
+ polyline.width.insert(polyline.width.end(), best_candidate->width.begin() + far_idx, best_candidate->width.end());
+ polyline.endpoints.second = best_candidate->endpoints.second;
+ assert(polyline.width.size() == polyline.points.size());
+ deleted.insert(best_idx);
+ }
+ }
+ //delete items to delete (iterate in reverse order to not invalidate the idxs
+ for (auto rit = deleted.rbegin(); rit != deleted.rend(); rit++)
+ pp.erase(pp.begin() + (*rit));
+}
+void
+MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
+{
// concatenate, but even where multiple thickpolyline join, to create nice long strait polylines
/* If we removed any short polylines we now try to connect consecutive polylines
in order to allow loop detection. Note that this algorithm is greedier than
@@ -2101,7 +2209,7 @@ MedialAxis::remove_too_thin_points(ThickPolylines& pp)
}
void
-MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_size)
+MedialAxis::remove_too_short_polylines(ThickPolylines& pp)
{
// reduce the flow at the intersection ( + ) points
//FIXME: TODO: note that crossings are unnafected right now. they may need a different codepath directly in their method
@@ -2167,7 +2275,7 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz
while (changes) {
changes = false;
- coordf_t shortest_size = (coordf_t)min_size;
+ coordf_t shortest_size = (coordf_t)this->min_length;
size_t shortest_idx = -1;
for (size_t i = 0; i < pp.size(); ++i) {
ThickPolyline& polyline = pp[i];
@@ -2198,8 +2306,6 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz
changes = true;
while (changes) {
changes = false;
-
- coordf_t shortest_size = (coordf_t)min_size;
size_t shortest_idx = -1;
for (size_t polyidx = 0; polyidx < pp.size(); ++polyidx) {
ThickPolyline& tp = pp[polyidx];
@@ -2699,10 +2805,21 @@ MedialAxis::build(ThickPolylines& polylines_out)
// svg.Close();
//}
//TODO: reduce the flow at the intersection ( + ) points on crossing?
+ concatenate_small_polylines(pp);
+ //{
+ // std::stringstream stri;
+ // stri << "medial_axis_6_concat_small_" << id << ".svg";
+ // SVG svg(stri.str());
+ // svg.draw(*bounds, "grey");
+ // svg.draw(this->expolygon, "green");
+ // svg.draw(pp, "red");
+ // svg.Close();
+ //}
+
concatenate_polylines_with_crossing(pp);
//{
// std::stringstream stri;
- // stri << "medial_axis_6_concat_" << id << ".svg";
+ // stri << "medial_axis_7_concat_" << id << ".svg";
// SVG svg(stri.str());
// svg.draw(*bounds, "grey");
// svg.draw(this->expolygon, "green");
@@ -2710,7 +2827,7 @@ MedialAxis::build(ThickPolylines& polylines_out)
// svg.Close();
//}
- remove_too_short_polylines(pp, max_w * 2);
+ remove_too_short_polylines(pp);
//{
// std::stringstream stri;
// stri << "medial_axis_8_tooshort_" << id << ".svg";
diff --git a/src/libslic3r/Geometry/MedialAxis.hpp b/src/libslic3r/Geometry/MedialAxis.hpp
index 4ea48de79..b8cd4518a 100644
--- a/src/libslic3r/Geometry/MedialAxis.hpp
+++ b/src/libslic3r/Geometry/MedialAxis.hpp
@@ -55,7 +55,7 @@ public:
/// _height: height of the extrusion, used to compute the difference between width and spacing.
MedialAxis(const ExPolygon& _expolygon, const coord_t _max_width, const coord_t _min_width, const coord_t _height)
: surface(_expolygon), max_width(_max_width), min_width(_min_width), height(_height),
- bounds(&_expolygon), nozzle_diameter(_min_width), taper_size(0), stop_at_min_width(true), resolution(12500) {/*id= staticid;staticid++;*/
+ bounds(&_expolygon), nozzle_diameter(_min_width), taper_size(0), stop_at_min_width(true), resolution(12500), min_length(_max_width) {/*id= staticid;staticid++;*/
};
/// create the polylines_out collection of variable-width polyline to extrude.
@@ -69,8 +69,9 @@ public:
MedialAxis& use_min_real_width(const coord_t nozzle_diameter) { this->nozzle_diameter = nozzle_diameter; return *this; }
/// optional parameter: create a taper of this length at each end (inside a bound or not). Default : 0 (no taper)
MedialAxis& use_tapers(const coord_t taper_size) { this->taper_size = taper_size; return *this; }
- /// optional parameter: if true, the entension inside the bounds can be cut if the width is too small. Default : true
+ /// optional parameter: if true, the extension inside the bounds can be cut if the width is too small. Default : true
MedialAxis& set_stop_at_min_width(const bool stop_at_min_width) { this->stop_at_min_width = stop_at_min_width; return *this; }
+ MedialAxis& set_min_length(const coord_t min_length) { this->min_length = min_length; return *this; }
private:
@@ -83,6 +84,8 @@ private:
const coord_t max_width;
/// minimum width of the extrusion, every spot where a circle diameter is lower than that will be ignored (unless it's the tip of the extrusion)
const coord_t min_width;
+ /// minimum length of continuous segments (may cross a crossing)
+ coord_t min_length;
/// resolution for simplifuing and stuff like that
const coord_t resolution;
/// height of the extrusion, used to compute the diufference between width and spacing.
@@ -124,12 +127,14 @@ private:
void extends_line(ThickPolyline& polyline, const ExPolygons& anchors, const coord_t join_width);
/// remove too thin bits at start & end of polylines
void remove_too_thin_extrusion(ThickPolylines& pp);
+ /// when we have a too small polyline, try to see if we can't concatenate it at a crossing to keep it.
+ void concatenate_small_polylines(ThickPolylines& pp);
/// instead of keeping polyline split at each corssing, we try to create long strait polylines that can cross each other.
void concatenate_polylines_with_crossing(ThickPolylines& pp);
/// remove bits around points that are too thin (can be inside the polyline)
void remove_too_thin_points(ThickPolylines& pp);
- /// delete polylines that are too short
- void remove_too_short_polylines(ThickPolylines& pp, const coord_t min_size);
+ /// delete polylines that are too short (below the this->min_length)
+ void remove_too_short_polylines(ThickPolylines& pp);
/// be sure we didn't try to push more plastic than the volume defined by surface * height can receive. If overextruded, reduce all widths by the correct %.
void ensure_not_overextrude(ThickPolylines& pp);
/// if nozzle_diameter > min_width, grow bits that are < width(nozzle_diameter) to width(nozzle_diameter) (don't activate that for gapfill)
diff --git a/src/libslic3r/MedialAxis.cpp b/src/libslic3r/MedialAxis.cpp
deleted file mode 100644
index 1e31c714f..000000000
--- a/src/libslic3r/MedialAxis.cpp
+++ /dev/null
@@ -1,2255 +0,0 @@
-#include "MedialAxis.hpp"
-#include "BoundingBox.hpp"
-#include "ExPolygon.hpp"
-#include "Geometry.hpp"
-#include "Polygon.hpp"
-#include "Line.hpp"
-#include "ClipperUtils.hpp"
-#include "SVG.hpp"
-#include "polypartition.h"
-#include "poly2tri/poly2tri.h"
-
-#include <boost/log/trivial.hpp>
-
-#include <algorithm>
-#include <cassert>
-#include <list>
-
-namespace Slic3r {
- int count_error = 0;
-
- //int Slic3r::MedialAxis::staticid = 0;
-
-void
-MedialAxis::build(Polylines &polylines)
-{
- //TODO: special case for triangles
- // take the longest edge
- // take the opposite vertex and get the otho dist
- // move the longest edge by X% that dist (depends on angle? from 1/2 to 1/4? or always 1/3?) use move dist as width
- // clip it and then enlarge it into anchor
- // note: ensure that if anchor is over only one edge, it's not the one choosen.
-
- //TODO: special case for quasi-rectangle
- // take longest (not-anchor if any) edge
- // get mid-dist for each adjascent edge
- // use these point to get the line, with the mid-dist as widths.
- // enlarge it into anchor
-
- ThickPolylines tp;
- this->build(tp);
- polylines.insert(polylines.end(), tp.begin(), tp.end());
-}
-
-void
-MedialAxis::polyline_from_voronoi(const Lines& voronoi_edges, ThickPolylines* polylines)
-{
- std::map<const VD::edge_type*, std::pair<coordf_t, coordf_t> > thickness;
- Lines lines = voronoi_edges;
- VD vd;
- construct_voronoi(lines.begin(), lines.end(), &vd);
-
- typedef const VD::edge_type edge_t;
-
- // DEBUG: dump all Voronoi edges
- //{
- // std::stringstream stri;
- // stri << "medial_axis_04_voronoi_" << this->id << ".svg";
- // SVG svg(stri.str());
- // for (VD::const_edge_iterator edge = vd.edges().begin(); edge != vd.edges().end(); ++edge) {
- // if (edge->is_infinite()) continue;
- // const edge_t* edgeptr = &*edge;
- // ThickPolyline polyline;
- // polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() ));
- // polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() ));
- // polyline.width.push_back(thickness[edgeptr].first);
- // polyline.width.push_back(thickness[edgeptr].second);
- // //polylines->push_back(polyline);
- // svg.draw(polyline, "red");
- // }
- // svg.Close();
- // return;
- //}
-
-
-
- // collect valid edges (i.e. prune those not belonging to MAT)
- // note: this keeps twins, so it inserts twice the number of the valid edges
- std::set<const VD::edge_type*> valid_edges;
- {
- std::set<const edge_t*> seen_edges;
- for (VD::const_edge_iterator edge = vd.edges().begin(); edge != vd.edges().end(); ++edge) {
- // if we only process segments representing closed loops, none if the
- // infinite edges (if any) would be part of our MAT anyway
- if (edge->is_secondary() || edge->is_infinite()) continue;
-
- // don't re-validate twins
- if (seen_edges.find(&*edge) != seen_edges.end()) continue; // TODO: is this needed?
- seen_edges.insert(&*edge);
- seen_edges.insert(edge->twin());
-
- if (!this->validate_edge(&*edge, lines, thickness)) continue;
- valid_edges.insert(&*edge);
- valid_edges.insert(edge->twin());
- }
- }
- std::set<const VD::edge_type*> edges = valid_edges;
-
- // iterate through the valid edges to build polylines
- while (!edges.empty()) {
- const edge_t* edge = *edges.begin();
- if (thickness[edge].first > this->max_width*1.001) {
- //std::cerr << "Error, edge.first has a thickness of " << unscaled(this->thickness[edge].first) << " > " << unscaled(this->max_width) << "\n";
- //(void)this->edges.erase(edge);
- //(void)this->edges.erase(edge->twin());
- //continue;
- }
- if (thickness[edge].second > this->max_width*1.001) {
- //std::cerr << "Error, edge.second has a thickness of " << unscaled(this->thickness[edge].second) << " > " << unscaled(this->max_width) << "\n";
- //(void)this->edges.erase(edge);
- //(void)this->edges.erase(edge->twin());
- //continue;
- }
-
- // start a polyline
- ThickPolyline polyline;
- polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() ));
- polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() ));
- polyline.width.push_back(thickness[edge].first);
- polyline.width.push_back(thickness[edge].second);
-
- // remove this edge and its twin from the available edges
- (void)edges.erase(edge);
- (void)edges.erase(edge->twin());
-
- // get next points
- this->process_edge_neighbors(edge, &polyline, edges, valid_edges, thickness);
-
- // get previous points
- {
- ThickPolyline rpolyline;
- this->process_edge_neighbors(edge->twin(), &rpolyline, edges, valid_edges, thickness);
- polyline.points.insert(polyline.points.begin(), rpolyline.points.rbegin(), rpolyline.points.rend());
- polyline.width.insert(polyline.width.begin(), rpolyline.width.rbegin(), rpolyline.width.rend());
- polyline.endpoints.first = rpolyline.endpoints.second;
- }
-
- assert(polyline.width.size() == polyline.points.size());
-
- // if loop, set endpoints to false
- // prevent loop endpoints from being extended
- if (polyline.first_point().coincides_with(polyline.last_point())) {
- polyline.endpoints.first = false;
- polyline.endpoints.second = false;
- }
-
- // append polyline to result
- polylines->push_back(polyline);
- }
-
- #ifdef SLIC3R_DEBUG
- {
- static int iRun = 0;
- dump_voronoi_to_svg(this->lines, this->vd, polylines, debug_out_path("MedialAxis-%d.svg", iRun ++).c_str());
- printf("Thick lines: ");
- for (ThickPolylines::const_iterator it = polylines->begin(); it != polylines->end(); ++ it) {
- ThickLines lines = it->thicklines();
- for (ThickLines::const_iterator it2 = lines.begin(); it2 != lines.end(); ++ it2) {
- printf("%f,%f ", it2->a_width, it2->b_width);
- }
- }
- printf("\n");
- }
- #endif /* SLIC3R_DEBUG */
-}
-
-void
-MedialAxis::process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* polyline, std::set<const VD::edge_type*> &edges, std::set<const VD::edge_type*> &valid_edges, std::map<const VD::edge_type*, std::pair<coordf_t, coordf_t> > &thickness)
-{
- while (true) {
- // Since rot_next() works on the edge starting point but we want
- // to find neighbors on the ending point, we just swap edge with
- // its twin.
- const VD::edge_type* twin = edge->twin();
-
- // count neighbors for this edge
- std::vector<const VD::edge_type*> neighbors;
- for (const VD::edge_type* neighbor = twin->rot_next(); neighbor != twin;
- neighbor = neighbor->rot_next()) {
- if (valid_edges.count(neighbor) > 0) neighbors.push_back(neighbor);
- }
-
- // if we have a single neighbor then we can continue recursively
- if (neighbors.size() == 1) {
- const VD::edge_type* neighbor = neighbors.front();
-
- // break if this is a closed loop
- if (edges.count(neighbor) == 0) return;
-
- Point new_point(neighbor->vertex1()->x(), neighbor->vertex1()->y());
- polyline->points.push_back(new_point);
- polyline->width.push_back(thickness[neighbor].second);
-
- (void)edges.erase(neighbor);
- (void)edges.erase(neighbor->twin());
- edge = neighbor;
- } else if (neighbors.size() == 0) {
- polyline->endpoints.second = true;
- return;
- } else {
- // T-shaped or star-shaped joint
- return;
- }
- }
-}
-
-bool
-MedialAxis::validate_edge(const VD::edge_type* edge, Lines &lines, std::map<const VD::edge_type*, std::pair<coordf_t, coordf_t> > &thickness)
-{
- // prevent overflows and detect almost-infinite edges
- if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
- std::abs(edge->vertex0()->y()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
- std::abs(edge->vertex1()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
- std::abs(edge->vertex1()->y()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
- std::isnan(edge->vertex0()->x()) ||
- std::isnan(edge->vertex0()->y()) ||
- std::isnan(edge->vertex1()->x()) ||
- std::isnan(edge->vertex1()->y()) )
- return false;
-
- // construct the line representing this edge of the Voronoi diagram
- const Line line(
- Point( edge->vertex0()->x(), edge->vertex0()->y() ),
- Point( edge->vertex1()->x(), edge->vertex1()->y() )
- );
-
- // discard edge if it lies outside the supplied shape
- // this could maybe be optimized (checking inclusion of the endpoints
- // might give false positives as they might belong to the contour itself)
- if (line.a.coincides_with_epsilon(line.b)) {
- // in this case, contains(line) returns a false positive
- if (!this->expolygon.contains(line.a)) return false;
- } else {
- //test if (!expolygon.contains(line))
- //this if isn't perfect (the middle of the line may still be out of the polygon)
- //but this edge-case shouldn't occur anyway, by the way the voronoi is built.
- if (!expolygon.contains(line.a) || !expolygon.contains(line.b)) { //this if reduced diff_pl from 25% to 18% cpu usage
- //this line can count for 25% of slicing time, if not enclosed in if
- Polylines external_bits = diff_pl(Polylines{ Polyline{ line.a, line.b } }, expolygon);
- if (!external_bits.empty()) {
- //check if the bits that are not inside are under epsilon length
- coordf_t max_length = 0;
- for (Polyline& poly : external_bits) {
- max_length = std::max(max_length, poly.length());
- }
- if (max_length > SCALED_EPSILON)
- return false;
- }
- }
- }
-
- // retrieve the original line segments which generated the edge we're checking
- const VD::cell_type* cell_l = edge->cell();
- const VD::cell_type* cell_r = edge->twin()->cell();
- const Line &segment_l = this->retrieve_segment(cell_l, lines);
- const Line &segment_r = this->retrieve_segment(cell_r, lines);
-
-
- //SVG svg("edge.svg");
- //svg.draw(this->expolygon.expolygon);
- //svg.draw(line);
- //svg.draw(segment_l, "red");
- //svg.draw(segment_r, "blue");
- //svg.Close();
- //
-
- /* Calculate thickness of the cross-section at both the endpoints of this edge.
- Our Voronoi edge is part of a CCW sequence going around its Voronoi cell
- located on the left side. (segment_l).
- This edge's twin goes around segment_r. Thus, segment_r is
- oriented in the same direction as our main edge, and segment_l is oriented
- in the same direction as our twin edge.
- We used to only consider the (half-)distances to segment_r, and that works
- whenever segment_l and segment_r are almost specular and facing. However,
- at curves they are staggered and they only face for a very little length
- (our very short edge represents such visibility).
- Both w0 and w1 can be calculated either towards cell_l or cell_r with equal
- results by Voronoi definition.
- When cell_l or cell_r don't refer to the segment but only to an endpoint, we
- calculate the distance to that endpoint instead. */
-
- coordf_t w0 = cell_r->contains_segment()
- ? line.a.distance_to(segment_r)*2
- : line.a.distance_to(this->retrieve_endpoint(cell_r, lines))*2;
-
- coordf_t w1 = cell_l->contains_segment()
- ? line.b.distance_to(segment_l)*2
- : line.b.distance_to(this->retrieve_endpoint(cell_l, lines))*2;
-
- //don't remove the line that goes to the intersection of the contour
- // we use them to create nicer thin wall lines
- //if (cell_l->contains_segment() && cell_r->contains_segment()) {
- // // calculate the relative angle between the two boundary segments
- // double angle = fabs(segment_r.orientation() - segment_l.orientation());
- // if (angle > PI) angle = 2*PI - angle;
- // assert(angle >= 0 && angle <= PI);
- //
- // // fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction)
- // // we're interested only in segments close to the second case (facing segments)
- // // so we allow some tolerance.
- // // this filter ensures that we're dealing with a narrow/oriented area (longer than thick)
- // // we don't run it on edges not generated by two segments (thus generated by one segment
- // // and the endpoint of another segment), since their orientation would not be meaningful
- // if (PI - angle > PI/8) {
- // // angle is not narrow enough
- //
- // // only apply this filter to segments that are not too short otherwise their
- // // angle could possibly be not meaningful
- // if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON || line.length() >= this->min_width)
- // return false;
- // }
- //} else {
- // if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON)
- // return false;
- //}
-
- // don't do that before we try to fusion them
- //if (w0 < this->min_width && w1 < this->min_width)
- // return false;
- //
-
- //shouldn't occur if perimeter_generator is well made. *1.05 for a little wiggle room
- if (w0 > this->max_width*1.05 && w1 > this->max_width*1.05)
- return false;
-
- thickness[edge] = std::make_pair(w0, w1);
- thickness[edge->twin()] = std::make_pair(w1, w0);
-
- return true;
-}
-
-const Line&
-MedialAxis::retrieve_segment(const VD::cell_type* cell, Lines& lines) const
-{
- return lines[cell->source_index()];
-}
-
-const Point&
-MedialAxis::retrieve_endpoint(const VD::cell_type* cell, Lines &lines) const
-{
- const Line& line = this->retrieve_segment(cell, lines);
- if (cell->source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) {
- return line.a;
- } else {
- return line.b;
- }
-}
-
-
-/// remove point that are at SCALED_EPSILON * 2 distance.
-void
-remove_point_too_near(ThickPolyline* to_reduce)
-{
- const coord_t smallest = (coord_t)SCALED_EPSILON * 2;
- size_t id = 1;
- while (id < to_reduce->points.size() - 1) {
- coord_t newdist = (coord_t)std::min(to_reduce->points[id].distance_to(to_reduce->points[id - 1])
- , to_reduce->points[id].distance_to(to_reduce->points[id + 1]));
- if (newdist < smallest) {
- to_reduce->points.erase(to_reduce->points.begin() + id);
- to_reduce->width.erase(to_reduce->width.begin() + id);
- newdist = (coord_t)to_reduce->points[id].distance_to(to_reduce->points[id - 1]);
- //if you removed a point, it check if the next one isn't too near from the previous one.
- // if not, it bypass it.
- if (newdist > smallest) {
- ++id;
- }
- }
- //go to next one
- else ++id;
- }
-}
-
-/// add points from pattern to to_modify at the same % of the length
-/// so not add if an other point is present at the correct position
-void
-add_point_same_percent(ThickPolyline* pattern, ThickPolyline* to_modify)
-{
- const coordf_t to_modify_length = to_modify->length();
- const double percent_epsilon = SCALED_EPSILON / to_modify_length;
- const coordf_t pattern_length = pattern->length();
-
- double percent_length = 0;
- for (size_t idx_point = 1; idx_point < pattern->points.size() - 1; ++idx_point) {
- percent_length += pattern->points[idx_point-1].distance_to(pattern->points[idx_point]) / pattern_length;
- //find position
- size_t idx_other = 1;
- double percent_length_other_before = 0;
- double percent_length_other = 0;
- while (idx_other < to_modify->points.size()) {
- percent_length_other_before = percent_length_other;
- percent_length_other += to_modify->points[idx_other-1].distance_to(to_modify->points[idx_other])
- / to_modify_length;
- if (percent_length_other > percent_length - percent_epsilon) {
- //if higher (we have gone over it)
- break;
- }
- ++idx_other;
- }
- if (percent_length_other > percent_length + percent_epsilon) {
- //insert a new point before the position
- double percent_dist = (percent_length - percent_length_other_before) / (percent_length_other - percent_length_other_before);
- coordf_t new_width = to_modify->width[idx_other - 1] * (1 - percent_dist);
- new_width += to_modify->width[idx_other] * (percent_dist);
- to_modify->width.insert(to_modify->width.begin() + idx_other, new_width);
- to_modify->points.insert(
- to_modify->points.begin() + idx_other,
- to_modify->points[idx_other - 1].interpolate(percent_dist, to_modify->points[idx_other]));
- }
- }
-}
-
-/// find the nearest angle in the contour (or 2 nearest if it's difficult to choose)
-/// return 1 for an angle of 90° and 0 for an angle of 0° or 180°
-/// find the nearest angle in the contour (or 2 nearest if it's difficult to choose)
-/// return 1 for an angle of 90° and 0 for an angle of 0° or 180°
-double
-get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t min_dist_between_point) {
- coordf_t nearest_dist = point.distance_to(contour.contour.points.front());
- Point point_nearest = contour.contour.points.front();
- size_t id_nearest = 0;
- coordf_t near_dist = nearest_dist;
- Point point_near = point_nearest;
- size_t id_near = 0;
- for (size_t id_point = 1; id_point < contour.contour.points.size(); ++id_point) {
- if (nearest_dist > point.distance_to(contour.contour.points[id_point])) {
- //update point_near
- id_near = id_nearest;
- point_near = point_nearest;
- near_dist = nearest_dist;
- //update nearest
- nearest_dist = point.distance_to(contour.contour.points[id_point]);
- point_nearest = contour.contour.points[id_point];
- id_nearest = id_point;
- }
- }
- double angle = 0;
- size_t id_before = id_nearest == 0 ? contour.contour.points.size() - 1 : id_nearest - 1;
- Point point_before = id_nearest == 0 ? contour.contour.points.back() : contour.contour.points[id_nearest - 1];
- //Search one point far enough to be relevant
- while (point_nearest.distance_to(point_before) < min_dist_between_point) {
- point_before = id_before == 0 ? contour.contour.points.back() : contour.contour.points[id_before - 1];
- id_before = id_before == 0 ? contour.contour.points.size() - 1 : id_before - 1;
- //don't loop
- if (id_before == id_nearest) {
- id_before = id_nearest == 0 ? contour.contour.points.size() - 1 : id_nearest - 1;
- point_before = id_nearest == 0 ? contour.contour.points.back() : contour.contour.points[id_nearest - 1];
- break;
- }
- }
- size_t id_after = id_nearest == contour.contour.points.size() - 1 ? 0 : id_nearest + 1;
- Point point_after = id_nearest == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_nearest + 1];
- //Search one point far enough to be relevant
- while (point_nearest.distance_to(point_after) < min_dist_between_point) {
- point_after = id_after == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_after + 1];
- id_after = id_after == contour.contour.points.size() - 1 ? 0 : id_after + 1;
- //don't loop
- if (id_after == id_nearest) {
- id_after = id_nearest == contour.contour.points.size() - 1 ? 0 : id_nearest + 1;
- point_after = id_nearest == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_nearest + 1];
- break;
- }
- }
- //compute angle
- angle = point_nearest.ccw_angle(point_before, point_after);
- if (angle >= PI) angle = 2 * PI - angle; // smaller angle
- //compute the diff from 90°
- angle = abs(angle - PI / 2);
- if (point_near.coincides_with_epsilon(point_nearest) && std::max(nearest_dist, near_dist) + SCALED_EPSILON < point_nearest.distance_to(point_near)) {
- //not only nearest
- Point point_before = id_near == 0 ? contour.contour.points.back() : contour.contour.points[id_near - 1];
- Point point_after = id_near == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_near + 1];
- double angle2 = std::min(point_nearest.ccw_angle(point_before, point_after), point_nearest.ccw_angle(point_after, point_before));
- angle2 = abs(angle - PI / 2);
- angle = (angle + angle2) / 2;
- }
-
- return 1 - (angle / (PI / 2));
-}
-
-double
-dot(Line l1, Line l2)
-{
- Vec2d v_1(l1.b.x() - l1.a.x(), l1.b.y() - l1.a.y());
- v_1.normalize();
- Vec2d v_2(l2.b.x() - l2.a.x(), l2.b.y() - l2.a.y());
- v_2.normalize();
- return v_1.x()*v_2.x() + v_1.y()*v_2.y();
-}
-
-void
-MedialAxis::fusion_curve(ThickPolylines &pp)
-{
- //fusion Y with only 1 '0' value => the "0" branch "pull" the cross-point
- bool changes = false;
- for (size_t i = 0; i < pp.size(); ++i) {
- ThickPolyline& polyline = pp[i];
- // only consider 2-point polyline with endpoint
- //if (polyline.points.size() != 2) continue; // too restrictive.
- if (polyline.endpoints.first) polyline.reverse();
- else if (!polyline.endpoints.second) continue;
- if (polyline.width.back() > EPSILON) continue;
-
- //check my length is small
- coord_t length = (coord_t)polyline.length();
- if (length > this->max_width) continue;
-
- size_t closest_point_idx = this->expolygon.contour.closest_point_index(polyline.points.back());
-
- //check the 0-width point is on the contour.
- if (closest_point_idx == (size_t)-1) continue;
-
- size_t prev_idx = closest_point_idx == 0 ? this->expolygon.contour.points.size() - 1 : closest_point_idx - 1;
- size_t next_idx = closest_point_idx == this->expolygon.contour.points.size() - 1 ? 0 : closest_point_idx + 1;
- double mindot = 1;
- mindot = std::min(mindot, abs(dot(Line(polyline.points[polyline.points.size() - 1], polyline.points[polyline.points.size() - 2]),
- (Line(this->expolygon.contour.points[closest_point_idx], this->expolygon.contour.points[prev_idx])))));
- mindot = std::min(mindot, abs(dot(Line(polyline.points[polyline.points.size() - 1], polyline.points[polyline.points.size() - 2]),
- (Line(this->expolygon.contour.points[closest_point_idx], this->expolygon.contour.points[next_idx])))));
-
- //compute angle
- double coeff_contour_angle = this->expolygon.contour.points[closest_point_idx].ccw_angle(this->expolygon.contour.points[prev_idx], this->expolygon.contour.points[next_idx]);
- if (coeff_contour_angle >= PI) coeff_contour_angle = 2 * PI - coeff_contour_angle; // smaller angle
- //compute the diff from 90°
- coeff_contour_angle = abs(coeff_contour_angle - PI / 2);
-
-
- // look if other end is a cross point with almost 90° angle
- double sum_dot = 0;
- double min_dot = 0;
- // look if other end is a cross point with multiple other branch
- std::vector<size_t> crosspoint;
- for (size_t j = 0; j < pp.size(); ++j) {
- if (j == i) continue;
- ThickPolyline& other = pp[j];
- if (polyline.first_point().coincides_with_epsilon(other.last_point())) {
- other.reverse();
- crosspoint.push_back(j);
- double dot_temp = dot(Line(polyline.points[0], polyline.points[1]), (Line(other.points[0], other.points[1])));
- min_dot = std::min(min_dot, abs(dot_temp));
- sum_dot += dot_temp;
- } else if (polyline.first_point().coincides_with_epsilon(other.first_point())) {
- crosspoint.push_back(j);
- double dot_temp = dot(Line(polyline.points[0], polyline.points[1]), (Line(other.points[0], other.points[1])));
- min_dot = std::min(min_dot, abs(dot_temp));
- sum_dot += dot_temp;
- }
- }
- sum_dot = abs(sum_dot);
-
- //only consider very shallow angle for contour
- if (mindot > 0.15 &&
- (1 - (coeff_contour_angle / (PI / 2))) > 0.2) continue;
-
- //check if it's a line that we can pull
- if (crosspoint.size() != 2) continue;
- if (sum_dot > 0.2) continue;
- if (min_dot > 0.5) continue;
- //don't remove useful bits. TODO: use the mindot to know by how much to multiply (1 when 90°, 1.42 when 45+, 1 when 0°)
- if (polyline.length() > polyline.width.front()*1.42) continue;
-
- //don't pull, it distords the line if there are too many points.
- //// pull it a bit, depends on my size, the dot?, and the coeff at my 0-end (~14% for a square, almost 0 for a gentle curve)
- //coord_t length_pull = polyline.length();
- //length_pull *= 0.144 * get_coeff_from_angle_countour(polyline.points.back(), this->expolygon, std::min(min_width, polyline.length() / 2));
-
- ////compute dir
- //Vectorf pull_direction(polyline.points[1].x() - polyline.points[0].x(), polyline.points[1].y() - polyline.points[0].y());
- //pull_direction = normalize(pull_direction);
- //pull_direction.x() *= length_pull;
- //pull_direction.y() *= length_pull;
-
- ////pull the points
- //Point &p1 = pp[crosspoint[0]].points[0];
- //p1.x() = p1.x() + (coord_t)pull_direction.x();
- //p1.y() = p1.y() + (coord_t)pull_direction.y();
-
- //Point &p2 = pp[crosspoint[1]].points[0];
- //p2.x() = p2.x() + (coord_t)pull_direction.x();
- //p2.y() = p2.y() + (coord_t)pull_direction.y();
-
- //delete the now unused polyline
- pp.erase(pp.begin() + i);
- --i;
- changes = true;
- }
- if (changes) {
- concatThickPolylines(pp);
- ///reorder, in case of change
- std::sort(pp.begin(), pp.end(), [](const ThickPolyline & a, const ThickPolyline & b) { return a.length() < b.length(); });
- //have to redo it to remove multi-branch bits.
- fusion_curve(pp);
- }
-}
-
-void
-MedialAxis::remove_bits(ThickPolylines &pp)
-{
-
- //remove small bits that stick out of the path
- bool changes = false;
- for (size_t i = 0; i < pp.size(); ++i) {
- ThickPolyline& polyline = pp[i];
- // only consider polyline with 0-end
- if (polyline.endpoints.first) polyline.reverse();
- else if (!polyline.endpoints.second) continue;
- if (polyline.width.back() > 0) continue;
-
- //check my length is small
- coordf_t length = polyline.length();
- if (length > coordf_t(this->max_width) * 1.5) {
- continue;
- }
-
- // look if other end is a cross point with multiple other branch
- std::vector<size_t> crosspoint;
- for (size_t j = 0; j < pp.size(); ++j) {
- if (j == i) continue;
- ThickPolyline& other = pp[j];
- if (polyline.first_point().coincides_with_epsilon(other.last_point())) {
- other.reverse();
- crosspoint.push_back(j);
- } else if (polyline.first_point().coincides_with_epsilon(other.first_point())) {
- crosspoint.push_back(j);
- }
- }
- if (crosspoint.size() < 2) continue;
-
- //check if is smaller or the other ones are not endpoits
- int nb_better_than_me = 0;
- for (int i = 0; i < crosspoint.size(); i++) {
- if (!pp[crosspoint[0]].endpoints.second || length <= pp[crosspoint[0]].length())
- nb_better_than_me++;
- }
- if (nb_better_than_me < 2) continue;
-
- //check if the length of the polyline is small vs width of the other lines
- coord_t local_max_width = 0;
- for (int i = 0; i < crosspoint.size(); i++) {
- local_max_width = std::max(local_max_width, pp[crosspoint[i]].width[0]);
- }
- if (length > coordf_t(local_max_width + min_width))
- continue;
-
- //delete the now unused polyline
- pp.erase(pp.begin() + i);
- --i;
- changes = true;
- }
- if (changes) {
- concatThickPolylines(pp);
- ///reorder, in case of change
- std::sort(pp.begin(), pp.end(), [](const ThickPolyline & a, const ThickPolyline & b) { return a.length() < b.length(); });
- }
-
- //TODO: check if there is a U-turn (almost 180° direction change) : remove it.
-}
-
-void
-MedialAxis::fusion_corners(ThickPolylines &pp)
-{
-
- //fusion Y with only 1 '0' value => the "0" branch "pull" the cross-point
- bool changes = false;
- for (size_t i = 0; i < pp.size(); ++i) {
- ThickPolyline& polyline = pp[i];
- // only consider polyline with 0-end
- //if (polyline.points.size() != 2) continue; // maybe we should have something to merge X-point to 2-point if it's near enough.
- if (polyline.endpoints.first) polyline.reverse();
- else if (!polyline.endpoints.second) continue;
-
- //check my length is small
- coord_t length = (coord_t)polyline.length();
- if (length > this->max_width) continue;
-
- // look if other end is a cross point with multiple other branch
- std::vector<size_t> crosspoint;
- for (size_t j = 0; j < pp.size(); ++j) {
- if (j == i) continue;
- ThickPolyline& other = pp[j];
- if (polyline.first_point().coincides_with_epsilon(other.last_point())) {
- other.reverse();
- crosspoint.push_back(j);
- } else if (polyline.first_point().coincides_with_epsilon(other.first_point())) {
- crosspoint.push_back(j);
- }
- }
- //check if it's a line that we can pull
- if (crosspoint.size() != 2) continue;
-
- // check if i am at the external side of a curve
- double angle1 = polyline.points[0].ccw_angle(polyline.points[1], pp[crosspoint[0]].points[1]);
- if (angle1 >= PI) angle1 = 2 * PI - angle1; // smaller angle
- double angle2 = polyline.points[0].ccw_angle(polyline.points[1], pp[crosspoint[1]].points[1]);
- if (angle2 >= PI) angle2 = 2 * PI - angle2; // smaller angle
- if (angle1 + angle2 < PI) continue;
-
- //check if is smaller or the other ones are not endpoits
- if (pp[crosspoint[0]].endpoints.second && length > pp[crosspoint[0]].length()) continue;
- if (pp[crosspoint[1]].endpoints.second && length > pp[crosspoint[1]].length()) continue;
-
- if (polyline.width.back() > 0) {
- //FIXME: also pull (a bit less) points that are near to this one.
- // if true, pull it a bit, depends on my size, the dot?, and the coeff at my 0-end (~14% for a square, almost 0 for a gentle curve)
- coord_t length_pull = (coord_t)polyline.length();
- length_pull *= (coord_t)(0.144 * get_coeff_from_angle_countour(
- polyline.points.back(),
- this->expolygon,
- std::min(min_width, (coord_t)(polyline.length() / 2))));
-
- //compute dir
- Vec2d pull_direction(polyline.points[1].x() - polyline.points[0].x(), polyline.points[1].y() - polyline.points[0].y());
- pull_direction.normalize();
- pull_direction.x() *= length_pull;
- pull_direction.y() *= length_pull;
-
- //pull the points
- Point& p1 = pp[crosspoint[0]].points[0];
- p1.x() = p1.x() + (coord_t)pull_direction.x();
- p1.y() = p1.y() + (coord_t)pull_direction.y();
-
- Point& p2 = pp[crosspoint[1]].points[0];
- p2.x() = p2.x() + (coord_t)pull_direction.x();
- p2.y() = p2.y() + (coord_t)pull_direction.y();
- }
-
- //delete the now unused polyline
- pp.erase(pp.begin() + i);
- --i;
- changes = true;
- }
- if (changes) {
- concatThickPolylines(pp);
- ///reorder, in case of change
- std::sort(pp.begin(), pp.end(), [](const ThickPolyline & a, const ThickPolyline & b) { return a.length() < b.length(); });
- }
-}
-
-void
-MedialAxis::extends_line_both_side(ThickPolylines& pp) {
- const ExPolygons anchors = offset2_ex(to_polygons(diff_ex(*this->bounds, this->expolygon)), double(-SCALED_RESOLUTION), double(SCALED_RESOLUTION));
- for (size_t i = 0; i < pp.size(); ++i) {
- ThickPolyline& polyline = pp[i];
- this->extends_line(polyline, anchors, this->min_width);
- if (!polyline.points.empty()) {
- polyline.reverse();
- this->extends_line(polyline, anchors, this->min_width);
- }
- if (polyline.points.empty()) {
- pp.erase(pp.begin() + i);
- --i;
- }
- }
-}
-
-void
-MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, const coord_t join_width)
-{
- // extend initial and final segments of each polyline if they're actual endpoints
- // We assign new endpoints to temporary variables because in case of a single-line
- // polyline, after we extend the start point it will be caught by the intersection()
- // call, so we keep the inner point until we perform the second intersection() as well
- if (polyline.endpoints.second && !bounds->has_boundary_point(polyline.points.back())) {
- size_t first_idx = polyline.points.size() - 2;
- Line line(*(polyline.points.begin() + first_idx), polyline.points.back());
- while (line.length() < SCALED_RESOLUTION && first_idx>0) {
- first_idx--;
- line.a = *(polyline.points.begin() + first_idx);
- }
- // prevent the line from touching on the other side, otherwise intersection() might return that solution
- if (polyline.points.size() == 2 && this->expolygon.contains(line.midpoint())) line.a = line.midpoint();
-
- line.extend_end((double)this->max_width);
- Point new_back;
- if (this->expolygon.contour.has_boundary_point(polyline.points.back())) {
- new_back = polyline.points.back();
- } else {
- bool finded = this->expolygon.contour.first_intersection(line, &new_back);
- //verify also for holes.
- Point new_back_temp;
- for (Polygon hole : this->expolygon.holes) {
- if (hole.first_intersection(line, &new_back_temp)) {
- if (!finded || line.a.distance_to(new_back_temp) < line.a.distance_to(new_back)) {
- finded = true;
- new_back = new_back_temp;
- }
- }
- }
- // safety check if no intersection
- if (!finded) {
- if (!this->expolygon.contains(line.b)) {
- //it's outside!!!
- //if (!this->expolygon.contains(line.a)) {
- // std::cout << "Error, a line is formed that start outside a polygon, end outside of it and don't cross it!\n";
- //} else {
- // std::cout << "Error, a line is formed that start in a polygon, end outside of it and don't cross it!\n";
- //}
-
- //{
- // std::stringstream stri;
- // stri << "Error_" << (count_error++) << ".svg";
- // SVG svg(stri.str());
- // svg.draw(anchors);
- // svg.draw(this->expolygon);
- // svg.draw(line);
- // svg.draw(polyline);
- // svg.Close();
- //}
- //it's not possible to print that
- polyline.points.clear();
- polyline.width.clear();
- return;
- }
- new_back = line.b;
- }
- polyline.points.push_back(new_back);
- polyline.width.push_back(polyline.width.back());
- }
- Point new_bound;
- bool finded = bounds->contour.first_intersection(line, &new_bound);
- //verify also for holes.
- Point new_bound_temp;
- for (Polygon hole : bounds->holes) {
- if (hole.first_intersection(line, &new_bound_temp)) {
- if (!finded || line.a.distance_to(new_bound_temp) < line.a.distance_to(new_bound)) {
- finded = true;
- new_bound = new_bound_temp;
- }
- }
- }
- // safety check if no intersection
- if (!finded) {
- if (line.b.coincides_with_epsilon(polyline.points.back()))
- return;
- //check if we don't over-shoot inside us
- bool is_in_anchor = false;
- for (const ExPolygon& a : anchors) {
- if (a.contains(line.b)) {
- is_in_anchor = true;
- break;
- }
- }
- if (!is_in_anchor) return;
- new_bound = line.b;
- }
- /* if (new_bound.coincides_with_epsilon(new_back)) {
- return;
- }*/
- // find anchor
- Point best_anchor;
- coordf_t shortest_dist = (coordf_t)this->max_width;
- for (const ExPolygon& a : anchors) {
- Point p_maybe_inside = a.contour.centroid();
- coordf_t test_dist = new_bound.distance_to(p_maybe_inside) + new_back.distance_to(p_maybe_inside);
- //if (test_dist < max_width / 2 && (test_dist < shortest_dist || shortest_dist < 0)) {
- double angle_test = new_back.ccw_angle(p_maybe_inside, line.a);
- if (angle_test > PI) angle_test = 2 * PI - angle_test;
- if (test_dist < (coordf_t)this->max_width && test_dist<shortest_dist && abs(angle_test) > PI / 2) {
- shortest_dist = test_dist;
- best_anchor = p_maybe_inside;
- }
- }
- if (best_anchor.x() != 0 && best_anchor.y() != 0) {
- Point p_obj = best_anchor + new_bound;
- p_obj.x() /= 2;
- p_obj.y() /= 2;
- Line l2 = Line(new_back, p_obj);
- l2.extend_end((coordf_t)this->max_width);
- (void)bounds->contour.first_intersection(l2, &new_bound);
- }
- if (new_bound.coincides_with_epsilon(new_back))
- return;
- polyline.points.push_back(new_bound);
- //polyline.width.push_back(join_width);
- //it thickens the line a bit too early, imo
- polyline.width.push_back(polyline.width.back());
- }
-}
-
-void
-MedialAxis::main_fusion(ThickPolylines& pp)
-{
- //int idf = 0;
-
- bool changes = true;
- std::map<Point, double> coeff_angle_cache;
- while (changes) {
- concatThickPolylines(pp);
- //reoder pp by length (ascending) It's really important to do that to avoid building the line from the width insteand of the length
- std::sort(pp.begin(), pp.end(), [](const ThickPolyline & a, const ThickPolyline & b) {
- bool ahas0 = a.width.front() == 0 || a.width.back() == 0;
- bool bhas0 = b.width.front() == 0 || b.width.back() == 0;
- if (ahas0 && !bhas0) return true;
- if (!ahas0 && bhas0) return false;
- return a.length() < b.length();
- });
- changes = false;
- for (size_t i = 0; i < pp.size(); ++i) {
- ThickPolyline& polyline = pp[i];
-
- //simple check to see if i can be fusionned
- if (!polyline.endpoints.first && !polyline.endpoints.second) continue;
-
-
- ThickPolyline* best_candidate = nullptr;
- float best_dot = -1;
- size_t best_idx = 0;
- double dot_poly_branch = 0;
- double dot_candidate_branch = 0;
-
- bool find_main_branch = false;
- size_t biggest_main_branch_id = 0;
- coord_t biggest_main_branch_length = 0;
-
- // find another polyline starting here
- for (size_t j = i + 1; j < pp.size(); ++j) {
- ThickPolyline& other = pp[j];
- if (polyline.last_point().coincides_with_epsilon(other.last_point())) {
- polyline.reverse();
- other.reverse();
- } else if (polyline.first_point().coincides_with_epsilon(other.last_point())) {
- other.reverse();
- } else if (polyline.first_point().coincides_with_epsilon(other.first_point())) {
- } else if (polyline.last_point().coincides_with_epsilon(other.first_point())) {
- polyline.reverse();
- } else {
- continue;
- }
- //std::cout << " try : " << i << ":" << j << " : " <<
- // (polyline.points.size() < 2 && other.points.size() < 2) <<
- // (!polyline.endpoints.second || !other.endpoints.second) <<
- // ((polyline.points.back().distance_to(other.points.back())
- // + (polyline.width.back() + other.width.back()) / 4)
- // > max_width*1.05) <<
- // (abs(polyline.length() - other.length()) > max_width) << "\n";
-
- //// mergeable tests
- if (polyline.points.size() < 2 && other.points.size() < 2) continue;
- if (!polyline.endpoints.second || !other.endpoints.second) continue;
- // test if the new width will not be too big if a fusion occur
- //note that this isn't the real calcul. It's just to avoid merging lines too far apart.
- if (
- ((polyline.points.back().distance_to(other.points.back())
- + (polyline.width.back() + other.width.back()) / 4)
- > this->max_width *1.05))
- continue;
- // test if the lines are not too different in length.
- if (abs(polyline.length() - other.length()) > (coordf_t)this->max_width) continue;
-
-
- //test if we don't merge with something too different and without any relevance.
- double coeffSizePolyI = 1;
- if (polyline.width.back() == 0) {
- coeffSizePolyI = 0.1 + 0.9*get_coeff_from_angle_countour(polyline.points.back(), this->expolygon, std::min(min_width, (coord_t)(polyline.length() / 2)));
- }
- double coeffSizeOtherJ = 1;
- if (other.width.back() == 0) {
- coeffSizeOtherJ = 0.1 + 0.9*get_coeff_from_angle_countour(other.points.back(), this->expolygon, std::min(min_width, (coord_t)(polyline.length() / 2)));
- }
- //std::cout << " try2 : " << i << ":" << j << " : "
- // << (abs(polyline.length()*coeffSizePolyI - other.length()*coeffSizeOtherJ) > max_width / 2)
- // << (abs(polyline.length()*coeffSizePolyI - other.length()*coeffSizeOtherJ) > max_width)
- // << "\n";
- if (abs(polyline.length()*coeffSizePolyI - other.length()*coeffSizeOtherJ) > (coordf_t)(this->max_width / 2)) continue;
-
-
- //compute angle to see if it's better than previous ones (straighter = better).
- //we need to add how strait we are from our main.
- float test_dot = (float)(dot(polyline.lines().front(), other.lines().front()));
-
- // Get the branch/line in wich we may merge, if possible
- // with that, we can decide what is important, and how we can merge that.
- // angle_poly - angle_candi =90° => one is useless
- // both angle are equal => both are useful with same strength
- // ex: Y => | both are useful to crete a nice line
- // ex2: TTTTT => ----- these 90° useless lines should be discarded
- find_main_branch = false;
- biggest_main_branch_id = 0;
- biggest_main_branch_length = 0;
- for (size_t k = 0; k < pp.size(); ++k) {
- //std::cout << "try to find main : " << k << " ? " << i << " " << j << " ";
- if (k == i || k == j) continue;
- ThickPolyline& main = pp[k];
- if (polyline.first_point().coincides_with_epsilon(main.last_point())) {
- main.reverse();
- if (!main.endpoints.second)
- find_main_branch = true;
- else if (biggest_main_branch_length < main.length()) {
- biggest_main_branch_id = k;
- biggest_main_branch_length = (coord_t)main.length();
- }
- } else if (polyline.first_point().coincides_with_epsilon(main.first_point())) {
- if (!main.endpoints.second)
- find_main_branch = true;
- else if (biggest_main_branch_length < main.length()) {
- biggest_main_branch_id = k;
- biggest_main_branch_length = (coord_t)main.length();
- }
- }
- if (find_main_branch) {
- //use this variable to store the good index and break to compute it
- biggest_main_branch_id = k;
- break;
- }
- }
- double dot_poly_branch_test = 0.707;
- double dot_candidate_branch_test = 0.707;
- if (!find_main_branch && biggest_main_branch_length == 0) {
- // nothing -> it's impossible!
- dot_poly_branch_test = 0.707;
- dot_candidate_branch_test = 0.707;
- //std::cout << "no main branch... impossible!!\n";
- } else if (!find_main_branch && (
- (pp[biggest_main_branch_id].length() < polyline.length() && (polyline.width.back() != 0 || pp[biggest_main_branch_id].width.back() ==0))
- || (pp[biggest_main_branch_id].length() < other.length() && (other.width.back() != 0 || pp[biggest_main_branch_id].width.back() == 0)))) {
- //the main branch should have no endpoint or be bigger!
- //here, it have an endpoint, and is not the biggest -> bad!
- //std::cout << "he main branch should have no endpoint or be bigger! here, it have an endpoint, and is not the biggest -> bad!\n";
- continue;
- } else {
- //compute the dot (biggest_main_branch_id)
- dot_poly_branch_test = -dot(Line(polyline.points[0], polyline.points[1]), Line(pp[biggest_main_branch_id].points[0], pp[biggest_main_branch_id].points[1]));
- dot_candidate_branch_test = -dot(Line(other.points[0], other.points[1]), Line(pp[biggest_main_branch_id].points[0], pp[biggest_main_branch_id].points[1]));
- if (dot_poly_branch_test < 0) dot_poly_branch_test = 0;
- if (dot_candidate_branch_test < 0) dot_candidate_branch_test = 0;
- if (pp[biggest_main_branch_id].width.back()>0)
- test_dot += 2 * (float)dot_poly_branch;
- //std::cout << "compute dot "<< dot_poly_branch_test<<" & "<< dot_candidate_branch_test <<"\n";
- }
- //test if it's useful to merge or not
- //ie, don't merge 'T' but ok for 'Y', merge only lines of not disproportionate different length (ratio max: 4) (or they are both with 0-width end)
- if (dot_poly_branch_test < 0.1 || dot_candidate_branch_test < 0.1 ||
- (
- ((polyline.length()>other.length() ? polyline.length() / other.length() : other.length() / polyline.length()) > 4)
- && !(polyline.width.back() == 0 && other.width.back()==0)
- )) {
- //std::cout << "not useful to merge\n";
- continue;
- }
- if (test_dot > best_dot) {
- best_candidate = &other;
- best_idx = j;
- best_dot = test_dot;
- dot_poly_branch = dot_poly_branch_test;
- dot_candidate_branch = dot_candidate_branch_test;
- //{
- // std::cout << "going to merge: b1=" << i << ", b2=" << best_idx << ", main=" << biggest_main_branch_id << "\n";
- // std::cout << "b1=" << polyline.points.front().x() << " : " << polyline.points.front().y() << " => " << polyline.points.back().x() << " : " << polyline.points.back().y() << "\n";
- // std::cout << "b2=" << other.points.front().x() << " : " << other.points.front().y() << " => " << other.points.back().x() << " : " << other.points.back().y() << "\n";
- // std::cout << "main=" << pp[biggest_main_branch_id].points.front().x() << " : " << pp[biggest_main_branch_id].points.front().y() << " => " << pp[biggest_main_branch_id].points.back().x() << " : " << pp[biggest_main_branch_id].points.back().y() << "\n";
- //}
- }
- }
- if (best_candidate != nullptr) {
- //idf++;
- //std::cout << " == fusion " << id <<" : "<< idf << " == with "<< i <<" & "<<best_idx<<"\n";
- // delete very near points
- remove_point_too_near(&polyline);
- remove_point_too_near(best_candidate);
-
- // add point at the same pos than the other line to have a nicer fusion
- add_point_same_percent(&polyline, best_candidate);
- add_point_same_percent(best_candidate, &polyline);
-
- //get the angle of the nearest points of the contour to see : _| (good) \_ (average) __(bad)
- //sqrt because the result are nicer this way: don't over-penalize /_ angles
- //TODO: try if we can achieve a better result if we use a different algo if the angle is <90°
- const double coeff_angle_poly = (coeff_angle_cache.find(polyline.points.back()) != coeff_angle_cache.end())
- ? coeff_angle_cache[polyline.points.back()]
- : (get_coeff_from_angle_countour(polyline.points.back(), this->expolygon, std::min(min_width, (coord_t)(polyline.length() / 2))));
- const double coeff_angle_candi = (coeff_angle_cache.find(best_candidate->points.back()) != coeff_angle_cache.end())
- ? coeff_angle_cache[best_candidate->points.back()]
- : (get_coeff_from_angle_countour(best_candidate->points.back(), this->expolygon, std::min(min_width, (coord_t)(best_candidate->length() / 2))));
-
- //this will encourage to follow the curve, a little, because it's shorter near the center
- //without that, it tends to go to the outter rim.
- //std::cout << " std::max(polyline.length(), best_candidate->length())=" << std::max(polyline.length(), best_candidate->length())
- // << ", polyline.length()=" << polyline.length()
- // << ", best_candidate->length()=" << best_candidate->length()
- // << ", polyline.length() / max=" << (polyline.length() / std::max(polyline.length(), best_candidate->length()))
- // << ", best_candidate->length() / max=" << (best_candidate->length() / std::max(polyline.length(), best_candidate->length()))
- // << "\n";
- double weight_poly = 2 - (polyline.length() / std::max(polyline.length(), best_candidate->length()));
- double weight_candi = 2 - (best_candidate->length() / std::max(polyline.length(), best_candidate->length()));
- weight_poly *= coeff_angle_poly;
- weight_candi *= coeff_angle_candi;
- const double coeff_poly = (dot_poly_branch * weight_poly) / (dot_poly_branch * weight_poly + dot_candidate_branch * weight_candi);
- const double coeff_candi = 1.0 - coeff_poly;
- //std::cout << "coeff_angle_poly=" << coeff_angle_poly
- // << ", coeff_angle_candi=" << coeff_angle_candi
- // << ", weight_poly=" << (2 - (polyline.length() / std::max(polyline.length(), best_candidate->length())))
- // << ", weight_candi=" << (2 - (best_candidate->length() / std::max(polyline.length(), best_candidate->length())))
- // << ", sumpoly=" << weight_poly
- // << ", sumcandi=" << weight_candi
- // << ", dot_poly_branch=" << dot_poly_branch
- // << ", dot_candidate_branch=" << dot_candidate_branch
- // << ", coeff_poly=" << coeff_poly
- // << ", coeff_candi=" << coeff_candi
- // << "\n";
- //iterate the points
- // as voronoi should create symetric thing, we can iterate synchonously
- size_t idx_point = 1;
- while (idx_point < std::min(polyline.points.size(), best_candidate->points.size())) {
- //fusion
- polyline.points[idx_point].x() = (coord_t)( polyline.points[idx_point].x() * coeff_poly + best_candidate->points[idx_point].x() * coeff_candi);
- polyline.points[idx_point].y() = (coord_t)(polyline.points[idx_point].y() * coeff_poly + best_candidate->points[idx_point].y() * coeff_candi);
-
- // The width decrease with distance from the centerline.
- // This formula is what works the best, even if it's not perfect (created empirically). 0->3% error on a gap fill on some tests.
- //If someone find an other formula based on the properties of the voronoi algorithm used here, and it works better, please use it.
- //or maybe just use the distance to nearest edge in bounds...
- double value_from_current_width = 0.5*polyline.width[idx_point] * dot_poly_branch / std::max(dot_poly_branch, dot_candidate_branch);
- value_from_current_width += 0.5*best_candidate->width[idx_point] * dot_candidate_branch / std::max(dot_poly_branch, dot_candidate_branch);
- double value_from_dist = 2 * polyline.points[idx_point].distance_to(best_candidate->points[idx_point]);
- value_from_dist *= sqrt(std::min(dot_poly_branch, dot_candidate_branch) / std::max(dot_poly_branch, dot_candidate_branch));
- polyline.width[idx_point] = value_from_current_width + value_from_dist;
- //std::cout << "width:" << polyline.width[idx_point] << " = " << value_from_current_width << " + " << value_from_dist
- // << " (<" << max_width << " && " << (bounds.contour.closest_point(polyline.points[idx_point])->distance_to(polyline.points[idx_point]) * 2.1)<<")\n";
- //failsafes
- if (polyline.width[idx_point] > this->max_width)
- polyline.width[idx_point] = this->max_width;
- //failsafe: try to not go out of the radius of the section, take the width of the merging point for that. (and with some offset)
- coord_t main_branch_width = pp[biggest_main_branch_id].width.front();
- coordf_t main_branch_dist = pp[biggest_main_branch_id].points.front().distance_to(polyline.points[idx_point]);
- coord_t max_width_from_main = (coord_t)std::sqrt(main_branch_width*main_branch_width + main_branch_dist*main_branch_dist);
- if (find_main_branch && polyline.width[idx_point] > max_width_from_main)
- polyline.width[idx_point] = max_width_from_main;
- if (find_main_branch && polyline.width[idx_point] > pp[biggest_main_branch_id].width.front() * 1.1)
- polyline.width[idx_point] = coord_t(pp[biggest_main_branch_id].width.front() * 1.1);
- //std::cout << "main fusion, max dist : " << max_width_from_main << "\n";
-
- ++idx_point;
- }
- if (idx_point < best_candidate->points.size()) {
- if (idx_point + 1 < best_candidate->points.size()) {
- //create a new polyline
- pp.emplace_back();
- best_candidate = &pp[best_idx]; // have to refresh the pointer, as the emplace_back() may have moved the array
- pp.back().endpoints.first = true;
- pp.back().endpoints.second = best_candidate->endpoints.second;
- for (size_t idx_point_new_line = idx_point; idx_point_new_line < best_candidate->points.size(); ++idx_point_new_line) {
- pp.back().points.push_back(best_candidate->points[idx_point_new_line]);
- pp.back().width.push_back(best_candidate->width[idx_point_new_line]);
- }
- } else {
- //Add last point
- polyline.points.push_back(best_candidate->points[idx_point]);
- polyline.width.push_back(best_candidate->width[idx_point]);
- //select if an end occur
- polyline.endpoints.second &= best_candidate->endpoints.second;
- }
-
- } else {
- //select if an end occur
- polyline.endpoints.second &= best_candidate->endpoints.second;
- }
-
- //remove points that are the same or too close each other, ie simplify
- for (size_t idx_point = 1; idx_point < polyline.points.size(); ++idx_point) {
- if (polyline.points[idx_point - 1].distance_to(polyline.points[idx_point]) < SCALED_EPSILON) {
- if (idx_point < polyline.points.size() - 1) {
- polyline.points.erase(polyline.points.begin() + idx_point);
- polyline.width.erase(polyline.width.begin() + idx_point);
- } else {
- polyline.points.erase(polyline.points.begin() + idx_point - 1);
- polyline.width.erase(polyline.width.begin() + idx_point - 1);
- }
- --idx_point;
- }
- }
- //remove points that are outside of the geometry
- for (size_t idx_point = 0; idx_point < polyline.points.size(); ++idx_point) {
- if (!bounds->contains_b(polyline.points[idx_point])) {
- polyline.points.erase(polyline.points.begin() + idx_point);
- polyline.width.erase(polyline.width.begin() + idx_point);
- --idx_point;
- }
- }
-
- if (polyline.points.size() < 2) {
- //remove self
- pp.erase(pp.begin() + i);
- --i;
- --best_idx;
- } else {
- //update cache
- coeff_angle_cache[polyline.points.back()] = coeff_angle_poly * coeff_poly + coeff_angle_candi * coeff_candi;
- }
-
- pp.erase(pp.begin() + best_idx);
- //{
- // std::stringstream stri;
- // stri << "medial_axis_2.0_aft_fus_" << id << "_" << idf << ".svg";
- // SVG svg(stri.str());
- // svg.draw(bounds);
- // svg.draw(this->expolygon);
- // svg.draw(pp);
- // svg.Close();
- //}
- changes = true;
- break;
- }
- }
- }
-}
-
-void
-MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp)
-{
- // remove too thin extrusion at start & end of polylines
- bool changes = false;
- for (size_t i = 0; i < pp.size(); ++i) {
- ThickPolyline& polyline = pp[i];
- // remove bits with too small extrusion
- while (polyline.points.size() > 1 && polyline.width.front() < this->min_width && polyline.endpoints.first) {
- //try to split if possible
- if (polyline.width[1] > min_width) {
- double percent_can_keep = (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]);
- if (polyline.points.front().distance_to(polyline.points[1]) * (1 - percent_can_keep) > SCALED_RESOLUTION) {
- //Can split => move the first point and assign a new weight.
- //the update of endpoints wil be performed in concatThickPolylines
- polyline.points.front() = polyline.points.front().interpolate(percent_can_keep, polyline.points[1]);
- polyline.width.front() = min_width;
- } else {
- /// almost 0-length, Remove
- polyline.points.erase(polyline.points.begin());
- polyline.width.erase(polyline.width.begin());
- }
- changes = true;
- break;
- }
- polyline.points.erase(polyline.points.begin());
- polyline.width.erase(polyline.width.begin());
- changes = true;
- }
- while (polyline.points.size() > 1 && polyline.width.back() < this->min_width && polyline.endpoints.second) {
- //try to split if possible
- if (polyline.width[polyline.points.size() - 2] > min_width) {
- double percent_can_keep = (min_width - polyline.width.back()) / (polyline.width[polyline.points.size() - 2] - polyline.width.back());
- if (polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * (1 - percent_can_keep) > SCALED_RESOLUTION) {
- //Can split => move the first point and assign a new weight.
- //the update of endpoints wil be performed in concatThickPolylines
- polyline.points.back() = polyline.points.back().interpolate(percent_can_keep, polyline.points[polyline.points.size() - 2]);
- polyline.width.back() = min_width;
- } else {
- /// almost 0-length, Remove
- polyline.points.erase(polyline.points.end() - 1);
- polyline.width.erase(polyline.width.end() - 1);
- }
- changes = true;
- break;
- }
- polyline.points.erase(polyline.points.end() - 1);
- polyline.width.erase(polyline.width.end() - 1);
- changes = true;
- }
- //remove points and bits that comes from a "main line"
- if (polyline.points.size() < 2 || (changes && polyline.length() < this->max_width && polyline.points.size() ==2)) {
- //remove self if too small
- pp.erase(pp.begin() + i);
- --i;
- }
- }
- if (changes) concatThickPolylines(pp);
-}
-
-void
-MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
-{
-
- // concatenate, but even where multiple thickpolyline join, to create nice long strait polylines
- /* If we removed any short polylines we now try to connect consecutive polylines
- in order to allow loop detection. Note that this algorithm is greedier than
- MedialAxis::process_edge_neighbors() as it will connect random pairs of
- polylines even when more than two start from the same point. This has no
- drawbacks since we optimize later using nearest-neighbor which would do the
- same, but should we use a more sophisticated optimization algorithm we should
- not connect polylines when more than two meet.
- Optimisation of the old algorithm : now we select the most "strait line" choice
- when we merge with an other line at a point with more than two meet.
- */
- for (size_t i = 0; i < pp.size(); ++i) {
- ThickPolyline& polyline = pp[i];
- if (polyline.endpoints.first && polyline.endpoints.second) continue; // optimization
-
- ThickPolyline* best_candidate = nullptr;
- float best_dot = -1;
- size_t best_idx = 0;
-
- // find another polyline starting here
- for (size_t j = 0; j < pp.size(); ++j) {
- if (j == i) continue;
- ThickPolyline& other = pp[j];
- if (other.endpoints.first && other.endpoints.second) continue;
- bool me_reverse = false;
- bool other_reverse = false;
- if (polyline.last_point().coincides_with_epsilon(other.last_point())) {
- other_reverse = true;
- } else if (polyline.first_point().coincides_with_epsilon(other.last_point())) {
- me_reverse = true;
- other_reverse = true;
- } else if (polyline.first_point().coincides_with_epsilon(other.first_point())) {
- me_reverse = true;
- } else if (!polyline.last_point().coincides_with_epsilon(other.first_point())) {
- continue;
- }
-
- Vec2d v_poly(me_reverse ? polyline.lines().front().vector().x() : polyline.lines().back().vector().x(),
- me_reverse ? polyline.lines().front().vector().y() : polyline.lines().back().vector().y());
- v_poly *= (1 / std::sqrt(v_poly.x()*v_poly.x() + v_poly.y()*v_poly.y()));
- Vec2d v_other(other_reverse ? other.lines().back().vector().x() : other.lines().front().vector().x(),
- other_reverse ? other.lines().back().vector().y() : other.lines().front().vector().y());
- v_other *= (1 / std::sqrt(v_other.x()*v_other.x() + v_other.y()*v_other.y()));
- float other_dot = std::abs(float( v_poly.x()*v_other.x() + v_poly.y()*v_other.y() ));
- if (other_dot > best_dot) {
- best_candidate = &other;
- best_idx = j;
- best_dot = other_dot;
- }
- }
- if (best_candidate != nullptr && best_candidate->points.size() > 1) {
- if (polyline.last_point().coincides_with_epsilon(best_candidate->last_point())) {
- best_candidate->reverse();
- } else if (polyline.first_point().coincides_with_epsilon(best_candidate->last_point())) {
- polyline.reverse();
- best_candidate->reverse();
- } else if (polyline.first_point().coincides_with_epsilon(best_candidate->first_point())) {
- polyline.reverse();
- }
- //intersections may create over-extrusion because the included circle can be a bit larger. We have to make it short again if needed.
- if (polyline.points.size() > 1 && best_candidate->points.size() > 1
- && polyline.width.back() > polyline.width[polyline.width.size() - 2]
- && polyline.width.back() > best_candidate->width[1]) {
- polyline.width.back() = std::min(polyline.width[polyline.width.size() - 2], best_candidate->width[1]);
- }
- //be far enough
- int far_idx = 1;
- while (far_idx < best_candidate->points.size() && polyline.last_point().coincides_with_epsilon(best_candidate->points[far_idx]))
- far_idx++;
- polyline.points.insert(polyline.points.end(), best_candidate->points.begin() + far_idx, best_candidate->points.end());
- polyline.width.insert(polyline.width.end(), best_candidate->width.begin() + far_idx, best_candidate->width.end());
- polyline.endpoints.second = best_candidate->endpoints.second;
- assert(polyline.width.size() == polyline.points.size());
- if (best_idx < i) i--;
- pp.erase(pp.begin() + best_idx);
- }
- }
-}
-
-void
-MedialAxis::remove_too_thin_points(ThickPolylines& pp)
-{
- //remove too thin polylines points (inside a polyline : split it)
- for (size_t i = 0; i < pp.size(); ++i) {
- ThickPolyline* polyline = &pp[i];
-
- // remove bits with too small extrusion
- size_t idx_point = 0;
- while (idx_point<polyline->points.size()) {
- if (polyline->width[idx_point] < min_width) {
- if (idx_point == 0) {
- //too thin at start
- polyline->points.erase(polyline->points.begin());
- polyline->width.erase(polyline->width.begin());
- idx_point = 0;
- } else if (idx_point == 1) {
- //too thin at start
- polyline->points.erase(polyline->points.begin());
- polyline->width.erase(polyline->width.begin());
- polyline->points.erase(polyline->points.begin());
- polyline->width.erase(polyline->width.begin());
- idx_point = 0;
- } else if (idx_point == polyline->points.size() - 2) {
- //too thin at (near) end
- polyline->points.erase(polyline->points.end() - 1);
- polyline->width.erase(polyline->width.end() - 1);
- polyline->points.erase(polyline->points.end() - 1);
- polyline->width.erase(polyline->width.end() - 1);
- } else if (idx_point == polyline->points.size() - 1) {
- //too thin at end
- polyline->points.erase(polyline->points.end() - 1);
- polyline->width.erase(polyline->width.end() - 1);
- } else {
- //too thin in middle : split
- pp.emplace_back();
- polyline = &pp[i]; // have to refresh the pointer, as the emplace_back() may have moved the array
- ThickPolyline &newone = pp.back();
- newone.points.insert(newone.points.begin(), polyline->points.begin() + idx_point + 1, polyline->points.end());
- newone.width.insert(newone.width.begin(), polyline->width.begin() + idx_point + 1, polyline->width.end());
- polyline->points.erase(polyline->points.begin() + idx_point, polyline->points.end());
- polyline->width.erase(polyline->width.begin() + idx_point, polyline->width.end());
- }
- } else idx_point++;
-
- if (polyline->points.size() < 2) {
- //remove self if too small
- pp.erase(pp.begin() + i);
- --i;
- break;
- }
- }
- }
-}
-
-void
-MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_size)
-{
- // reduce the flow at the intersection ( + ) points
- //FIXME: TODO: note that crossings are unnafected right now. they may need a different codepath directly in their method
- //TODO: unit tests for that.
- //TODO: never triggered. ther's only the sections passed by crossing fusion that aren't edge-case and it's not treated by this. => comment for now
- //for each not-endpoint point
- //std::vector<bool> endpoint_not_used(pp.size() * 2, true);
- //for (size_t idx_endpoint = 0; idx_endpoint < endpoint_not_used.size(); idx_endpoint++) {
- // ThickPolyline& polyline = pp[idx_endpoint / 2];
- // //update endpoint_not_used if not seen before
- // if (idx_endpoint % 2 == 0 && endpoint_not_used[idx_endpoint]) {
- // //update
- // endpoint_not_used[(idx_endpoint / 2)] = !polyline.endpoints.first;
- // endpoint_not_used[(idx_endpoint / 2) + 1] = endpoint_not_used[(idx_endpoint / 2) + 1] && !polyline.endpoints.second;
- // }
- // if (endpoint_not_used[idx_endpoint]) {
- // int nb_endpoints;
- // Point pt = idx_endpoint % 2 == 0 ? polyline.first_point() : polyline.last_point();
- // if (idx_endpoint % 2 == 0 && pt.coincides_with_epsilon(polyline.last_point())) {
- // nb_endpoints++;
- // endpoint_not_used[(idx_endpoint / 2) + 1] = false;
- // }
- // //good, now find other points
- // for (size_t idx_other_pp = (idx_endpoint / 2) + 1; idx_other_pp < pp.size(); idx_other_pp++) {
- // ThickPolyline& other = pp[idx_other_pp];
- // if (pt.coincides_with_epsilon(other.first_point())) {
- // nb_endpoints++;
- // endpoint_not_used[idx_other_pp * 2] = false;
- // }
- // if (pt.coincides_with_epsilon(other.last_point())) {
- // nb_endpoints++;
- // endpoint_not_used[idx_other_pp * 2 + 1] = false;
- // }
- // }
- // if (nb_endpoints < 3)
- // continue;
- // // reduce width accordingly
- // float reduction = 2.f / nb_endpoints;
- // std::cout << "reduce " << reduction << " points!\n";
- // if (idx_endpoint % 2 == 0 ) {
- // polyline.width.front() *= reduction;
- // if(pt.coincides_with_epsilon(polyline.last_point()))
- // polyline.width.back() *= reduction;
- // } else {
- // polyline.width.back() *= reduction;
- // }
- // //good, now find other points
- // for (size_t idx_other_pp = (idx_endpoint / 2) + 1; idx_other_pp < pp.size(); idx_other_pp++) {
- // ThickPolyline& other = pp[idx_other_pp];
- // if (pt.coincides_with_epsilon(other.first_point())) {
- // other.width.front() *= reduction;
- // }
- // if (pt.coincides_with_epsilon(other.last_point())) {
- // other.width.back() *= reduction;
- // }
- // }
- // //TODO: restore good width at width dist, or reduce other points up to width dist
- // }
- //}
-
- //remove too short polyline
- bool changes = true;
- while (changes) {
- changes = false;
-
- coordf_t shortest_size = (coordf_t) min_size;
- size_t shortest_idx = -1;
- for (size_t i = 0; i < pp.size(); ++i) {
- ThickPolyline& polyline = pp[i];
- // Remove the shortest polylines : polyline that are shorter than wider
- // (we can't do this check before endpoints extension and clipping because we don't
- // know how long will the endpoints be extended since it depends on polygon thickness
- // which is variable - extension will be <= max_width/2 on each side)
- if ((polyline.endpoints.first || polyline.endpoints.second)) {
- coordf_t local_max_width = this->max_width / 2;
- for (coordf_t w : polyline.width)
- local_max_width = std::max(local_max_width, w);
- if(polyline.length() < local_max_width) {
- if (shortest_size > polyline.length()) {
- shortest_size = polyline.length();
- shortest_idx = i;
- }
- }
- }
- }
- if (shortest_idx < pp.size()) {
- pp.erase(pp.begin() + shortest_idx);
- changes = true;
- }
- if (changes) concatThickPolylines(pp);
- }
-
- //remove points too near each other
- changes = true;
- while (changes) {
- changes = false;
-
- coordf_t shortest_size = (coordf_t)min_size;
- size_t shortest_idx = -1;
- for (size_t polyidx = 0; polyidx < pp.size(); ++polyidx) {
- ThickPolyline& tp = pp[polyidx];
- for (size_t pt_idx = 1; pt_idx < tp.points.size() - 1; pt_idx++) {
- if (tp.points[pt_idx - 1].coincides_with_epsilon(tp.points[pt_idx])) {
- tp.points.erase(tp.points.begin() + pt_idx);
- tp.width.erase(tp.width.begin() + pt_idx);
- pt_idx--;
- changes = true;
- }
- }
- //check last segment
- if (tp.points.size() > 2 && tp.points[tp.points.size() - 2].coincides_with_epsilon(tp.points.back())) {
- tp.points.erase(tp.points.end() - 2);
- tp.width.erase(tp.width.end() - 2);
- changes = true;
- }
- //delete null-length polylines
- if (tp.length() < SCALED_EPSILON && tp.first_point().coincides_with_epsilon(tp.last_point())) {
- pp.erase(pp.begin() + polyidx);
- --polyidx;
- changes = true;
- }
- }
- if (changes) concatThickPolylines(pp);
- }
-
-}
-
-void
-MedialAxis::check_width(ThickPolylines& pp, coord_t local_max_width, std::string msg)
-{
- //remove empty polyline
- int nb = 0;
- for (size_t i = 0; i < pp.size(); ++i) {
- for (size_t j = 0; j < pp[i].width.size(); ++j) {
- if (pp[i].width[j] > coord_t(local_max_width * 1.01)) {
- BOOST_LOG_TRIVIAL(error) << "Error " << msg << " width " << unscaled(pp[i].width[j]) << "(" << i << ":" << j << ") > " << unscaled(local_max_width) << "\n";
- nb++;
- }
- }
- }
- if (nb > 0) BOOST_LOG_TRIVIAL(error) << "== nbBig = " << nb << " ==\n";
-}
-
-void
-MedialAxis::ensure_not_overextrude(ThickPolylines& pp)
-{
- //ensure the volume extruded is correct for what we have been asked
- // => don't over-extrude
- double surface = 0;
- double volume = 0;
- for (ThickPolyline& polyline : pp) {
- for (ThickLine &l : polyline.thicklines()) {
- surface += l.length() * (l.a_width + l.b_width) / 2;
- coord_t width_mean = (l.a_width + l.b_width) / 2;
- volume += height * (width_mean - height * (1. - 0.25 * PI)) * l.length();
- }
- }
-
- // compute bounds volume
- double boundsVolume = 0;
- boundsVolume += height*bounds->area();
- // add external "perimeter gap"
- double perimeterRoundGap = bounds->contour.length() * height * (1 - 0.25*PI) * 0.5;
- // add holes "perimeter gaps"
- double holesGaps = 0;
- for (const Polygon &hole : bounds->holes) {
- holesGaps += hole.length() * height * (1 - 0.25*PI) * 0.5;
- }
- boundsVolume += perimeterRoundGap + holesGaps;
-
- if (boundsVolume < volume) {
- //reduce width
- double reduce_by = boundsVolume / volume;
- for (ThickPolyline& polyline : pp) {
- for (coord_t &width : polyline.width) {
- width = coord_t( double(width) * reduce_by);
- }
- }
- }
-}
-
-void
-MedialAxis::simplify_polygon_frontier()
-{
- //it will remove every point in the surface contour that aren't on the bounds contour
- this->expolygon = this->surface;
- this->expolygon.contour.remove_collinear(SCALED_EPSILON);
- for (Polygon &hole : this->expolygon.holes)
- hole.remove_collinear(SCALED_EPSILON);
- if (&this->surface != this->bounds) {
- bool need_intersect = false;
- for (size_t i = 0; i < this->expolygon.contour.points.size(); i++) {
- Point &p_check = this->expolygon.contour.points[i];
- //if (!find) {
- if (!bounds->has_boundary_point(p_check)) {
- //check if we put it at a bound point instead of delete it
- size_t prev_i = i == 0 ? this->expolygon.contour.points.size() - 1 : (i - 1);
- size_t next_i = i == this->expolygon.contour.points.size() - 1 ? 0 : (i + 1);
- const Point* closest = bounds->contour.closest_point(p_check);
- if (closest != nullptr && closest->distance_to(p_check) + SCALED_EPSILON
- < std::min(p_check.distance_to(this->expolygon.contour.points[prev_i]), p_check.distance_to(this->expolygon.contour.points[next_i])) / 2) {
- p_check.x() = closest->x();
- p_check.y() = closest->y();
- need_intersect = true;
- } else {
- this->expolygon.contour.points.erase(this->expolygon.contour.points.begin() + i);
- i--;
- }
- }
- }
- if (need_intersect) {
- ExPolygons simplified_polygons = intersection_ex(this->expolygon, *bounds);
- if (simplified_polygons.size() == 1) {
- this->expolygon = simplified_polygons[0];
- } else {
- //can't simplify that much, reuse the given one
- this->expolygon = this->surface;
- this->expolygon.contour.remove_collinear(SCALED_EPSILON);
- for (Polygon &hole : this->expolygon.holes)
- hole.remove_collinear(SCALED_EPSILON);
- }
- }
- }
-
- if (!this->expolygon.contour.points.empty())
- this->expolygon.remove_point_too_near((coord_t)SCALED_RESOLUTION);
-}
-
-/// Grow the extrusion to at least nozzle_diameter*1.05 (lowest safe extrusion width)
-/// Do not grow points inside the anchor.
-void
-MedialAxis::grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchors)
-{
- //compute the min width
- coord_t min_width = this->nozzle_diameter;
- if (this->height > 0) min_width = Flow::new_from_spacing(
- float(unscaled(this->nozzle_diameter)),
- float(unscaled(this->nozzle_diameter)),
- float(unscaled(this->height)),
- 1, false).scaled_width();
- //ensure the width is not lower than min_width.
- for (ThickPolyline& polyline : pp) {
- for (int i = 0; i < polyline.points.size(); ++i) {
- bool is_anchored = false;
- for (const ExPolygon &poly : anchors) {
- if (poly.contains(polyline.points[i])) {
- is_anchored = true;
- break;
- }
- }
- if (!is_anchored && polyline.width[i] < min_width)
- polyline.width[i] = min_width;
- }
- }
-}
-
-void
-MedialAxis::taper_ends(ThickPolylines& pp)
-{
- // minimum size of the taper: be sure to extrude at least the "round edges" of the extrusion (0-spacing extrusion).
- const coord_t min_size = (coord_t) std::max(this->nozzle_diameter * 0.1, this->height * (1. - 0.25 * PI));
- const coordf_t length = (coordf_t) std::min(this->taper_size, (this->nozzle_diameter - min_size) / 2);
- if (length <= SCALED_RESOLUTION) return;
- //ensure the width is not lower than min_size.
- for (ThickPolyline& polyline : pp) {
- if (polyline.length() < length * 2.2) continue;
- if (polyline.endpoints.first) {
- polyline.width[0] = min_size;
- coord_t current_dist = min_size;
- coord_t last_dist = min_size;
- for (size_t i = 1; i<polyline.width.size(); ++i) {
- current_dist += (coord_t) polyline.points[i - 1].distance_to(polyline.points[i]);
- if (current_dist > length) {
- //create a new point if not near enough
- if (current_dist > length + SCALED_RESOLUTION) {
- coordf_t percent_dist = (length - last_dist) / (current_dist - last_dist);
- polyline.points.insert(polyline.points.begin() + i, polyline.points[i - 1].interpolate(percent_dist, polyline.points[i]));
- polyline.width.insert(polyline.width.begin() + i, polyline.width[i]);
- }
- break;
- }
- polyline.width[i] = std::max((coordf_t)min_size, min_size + (polyline.width[i] - min_size) * current_dist / length);
- last_dist = current_dist;
- }
- }
- if (polyline.endpoints.second) {
- polyline.width[polyline.width.size() - 1] = min_size;
- coord_t current_dist = min_size;
- coord_t last_dist = min_size;
- for (size_t i = polyline.width.size()-1; i > 0; --i) {
- current_dist += (coord_t)polyline.points[i].distance_to(polyline.points[i - 1]);
- if (current_dist > length) {
- //create new point if not near enough
- if (current_dist > length + SCALED_RESOLUTION) {
- coordf_t percent_dist = (length - last_dist) / (current_dist - last_dist);
- polyline.points.insert(polyline.points.begin() + i, polyline.points[i].interpolate(percent_dist, polyline.points[i - 1]));
- polyline.width.insert(polyline.width.begin() + i, polyline.width[i - 1]);
- }
- break;
- }
- polyline.width[i - 1] = std::max((coordf_t)min_size, min_size + (polyline.width[i - 1] - min_size) * current_dist / length);
- last_dist = current_dist;
- }
- }
- }
-}
-
-double
-check_circular(ExPolygon& expolygon, coord_t max_variation) {
- if (expolygon.holes.size() > 0) return 0;
-
- //test if convex
- if (expolygon.contour.concave_points().empty() && expolygon.contour.points.size() > 3) {
- // Computing circle center
- Point center = expolygon.contour.centroid();
- coordf_t radius_min = std::numeric_limits<float>::max(), radius_max = 0;
- for (int i = 0; i < expolygon.contour.points.size(); ++i) {
- coordf_t dist = expolygon.contour.points[i].distance_to(center);
- radius_min = std::min(radius_min, dist);
- radius_max = std::max(radius_max, dist);
- }
- // check with max_variation to be sure it's round enough
- if (radius_max - radius_min < max_variation) {
- return radius_max;
- }
- }
- return 0;
-}
-
-void
-MedialAxis::build(ThickPolylines &polylines_out)
-{
- //static int id = 0;
- //id++;
- //std::cout << id << "\n";
- //{
- // std::stringstream stri;
- // stri << "medial_axis_0_enter_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(this->surface);
- // svg.Close();
- //}
- simplify_polygon_frontier();
- //{
- // std::stringstream stri;
- // stri << "medial_axis_0.5_simplified_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.Close();
- //}
- //safety check
- if (this->expolygon.area() < this->min_width * this->min_width) this->expolygon = this->surface;
- if (this->expolygon.area() < this->min_width * this->min_width) return;
-
- //check for circular shape
- coordf_t radius = check_circular(this->expolygon, this->min_width/4);
- if (radius > 0 && this->expolygon.contour.points.size() > 4) {
- ExPolygons miniPeri = offset_ex(this->expolygon.contour, -radius / 2);
- if (miniPeri.size() == 1 && miniPeri[0].holes.size() == 0) {
- ThickPolyline thickPoly;
- thickPoly.points = miniPeri[0].contour.points;
- thickPoly.points.push_back(thickPoly.points.front());
- thickPoly.endpoints.first = false;
- thickPoly.endpoints.second = false;
- for (int i = 0; i < thickPoly.points.size(); i++) {
- thickPoly.width.push_back(radius);
- }
- polylines_out.insert(polylines_out.end(), thickPoly);
- return;
- }
- }
-
- //std::cout << "simplify_polygon_frontier\n";
- // compute the Voronoi diagram and extract medial axis polylines
- ThickPolylines pp;
- this->polyline_from_voronoi(this->expolygon.lines(), &pp);
- //FIXME this is a stop-gap for voronoi bug, see superslicer/issues/995
- {
- double ori_area = 0;
- for (ThickPolyline& tp : pp) {
- for (int i = 1; i < tp.points.size(); i++) {
- ori_area += (tp.width[i - 1] + tp.width[i]) * tp.points[i - 1].distance_to(tp.points[i]) / 2;
- }
- }
- double area = this->expolygon.area();
- double ratio_area = ori_area / area;
- if (ratio_area < 1) ratio_area = 1 / ratio_area;
- //check if the returned voronoi is really off
- if (ratio_area > 1.1) {
- //add a little offset and retry
- ExPolygons fixer = offset_ex(this->expolygon, SCALED_EPSILON);
- if (fixer.size() == 1) {
- ExPolygon fixPoly = fixer[0];
- ThickPolylines pp_stopgap;
- this->polyline_from_voronoi(fixPoly.lines(), &pp_stopgap);
- double fix_area = 0;
- for (ThickPolyline& tp : pp_stopgap) {
- for (int i = 1; i < tp.points.size(); i++) {
- fix_area += (tp.width[i - 1] + tp.width[i]) * tp.points[i - 1].distance_to(tp.points[i]) / 2;
- }
- }
- double fix_ratio_area = fix_area / area;
- if (fix_ratio_area < 1) fix_ratio_area = 1 / fix_ratio_area;
- //if it's less off, then use it.
- if (fix_ratio_area < ratio_area) {
- pp = pp_stopgap;
- }
- }
- }
- }
- //{
- // std::stringstream stri;
- // stri << "medial_axis_0.9_voronoi_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
-
- //sanity check, as the voronoi can return (abeit very rarely) randomly high values.
- for (size_t tp_idx = 0; tp_idx < pp.size(); tp_idx++) {
- ThickPolyline& tp = pp[tp_idx];
- for (size_t i = 0; i < tp.width.size(); i++) {
- if (tp.width[i] > this->max_width) {
- tp.width[i] = this->max_width;
- }
- }
- // voronoi bugfix: when we have a wheel, it creates a polyline at the center, completly out of the polygon. #651
- // note: can't reproduce in the new verison. This may have been fixed by another way.
- //if (tp.endpoints.first && tp.endpoints.second && !this->expolygon.contains(tp.first_point()) && !this->expolygon.contains(tp.last_point()) && pp.size() > 1) {
- // //delete this out-of-bounds polyline
- // pp.erase(pp.begin() + tp_idx);
- // --tp_idx;
- //}
- //voronoi problem: can put two consecutive points at the same position. Delete one.
- for (size_t i = 1; i < tp.points.size()-1; i++) {
- if (tp.points[i-1].distance_to_square(tp.points[i]) < SCALED_EPSILON) {
- tp.points.erase(tp.points.begin() + i);
- tp.width.erase(tp.width.begin() + i);
- i--;
- }
- }
- //delete the inner one
- if (tp.points.size()>2 && tp.points[tp.points.size() - 2].distance_to_square(tp.points.back()) < SCALED_EPSILON) {
- tp.points.erase(tp.points.end() - 2);
- tp.width.erase(tp.width.end() - 2);
- }
- //delete null-length polylines
- if (tp.length() < SCALED_EPSILON && tp.first_point().coincides_with_epsilon(tp.last_point())) {
- pp.erase(pp.begin() + tp_idx);
- --tp_idx;
- }
- }
- //std::cout << "polyline_from_voronoi\n";
- //{
- // std::stringstream stri;
- // stri << "medial_axis_1_voronoi_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
-
- //check_width(pp, this->max_width, "polyline_from_voronoi");
-
- concatThickPolylines(pp);
-
- //std::cout << "concatThickPolylines\n";
- //{
- // std::stringstream stri;
- // stri << "medial_axis_1_voronoi_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
-
- /* Find the maximum width returned; we're going to use this for validating and
- filtering the output segments. */
- coord_t max_w = 0;
- for (ThickPolylines::const_iterator it = pp.begin(); it != pp.end(); ++it)
- max_w = std::max(max_w, (coord_t)*std::max_element(it->width.begin(), it->width.end()));
-
- //for (auto &p : pp) {
- // std::cout << "Start polyline : ";
- // for (auto &w : p.width) {
- // std::cout << ", " << w;
- // }
- // std::cout << "\n";
- //}
-
- // "remove" the little paths that are at the outside of a curve.
- fusion_curve(pp);
- //{
- // std::stringstream stri;
- // stri << "medial_axis_2_curve_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
-
-
- // Aligned fusion: Fusion the bits at the end of lines by "increasing thickness"
- // For that, we have to find other lines,
- // and with a next point no more distant than the max width.
- // Then, we can merge the bit from the first point to the second by following the mean.
- //
- main_fusion(pp);
- //{
- // std::stringstream stri;
- // stri << "medial_axis_3_fusion_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
-
- //fusion right-angle corners.
- fusion_corners(pp);
-
- // Loop through all returned polylines in order to extend their endpoints to the
- // expolygon boundaries (if done here, it may be cut later if not thick enough)
- if (stop_at_min_width) {
- //{
- // std::stringstream stri;
- // stri << "medial_axis_3_3_extends_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
- extends_line_both_side(pp);
- }
-
- /*for (auto &p : pp) {
- std::cout << "Fusion polyline : ";
- for (auto &w : p.width) {
- std::cout << ", " << w;
- }
- std::cout << "\n";
- }*/
- //reduce extrusion when it's too thin to be printable
- //{
- // std::stringstream stri;
- // stri << "medial_axis_3_6_remove_thin_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
-
- remove_too_thin_extrusion(pp);
- //{
- // std::stringstream stri;
- // stri << "medial_axis_4_thinok_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
-
- remove_too_thin_points(pp);
- //{
- // std::stringstream stri;
- // stri << "medial_axis_5.0_thuinner_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
-
- // Loop through all returned polylines in order to extend their endpoints to the
- // expolygon boundaries
- if (!stop_at_min_width) {
- extends_line_both_side(pp);
- }
- //{
- // std::stringstream stri;
- // stri << "medial_axis_5_expand_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
- //TODO: reduce the flow at the intersection ( + ) points on crossing?
- concatenate_polylines_with_crossing(pp);
- //{
- // std::stringstream stri;
- // stri << "medial_axis_6_concat_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
-
- remove_too_short_polylines(pp, max_w * 2);
- //{
- // std::stringstream stri;
- // stri << "medial_axis_8_tooshort_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
-
- ensure_not_overextrude(pp);
- //{
- // std::stringstream stri;
- // stri << "medial_axis_9.1_end_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
- if (nozzle_diameter != min_width) {
- grow_to_nozzle_diameter(pp, diff_ex(*this->bounds, this->expolygon));
- }
- if(this->taper_size != 0){
- taper_ends(pp);
- }
- //{
- // std::stringstream stri;
- // stri << "medial_axis_9.9_endnwithtaper_" << id << ".svg";
- // SVG svg(stri.str());
- // svg.draw(*bounds, "grey");
- // svg.draw(this->expolygon, "green");
- // svg.draw(pp, "red");
- // svg.Close();
- //}
-
- remove_bits(pp);
-
- //sort_polylines(pp);
-
- //for (auto &p : pp) {
- // std::cout << " polyline : ";
- // for (auto &w : p.width) {
- // std::cout << ", " << w;
- // }
- // std::cout << "\n";
- //}
-
- polylines_out.insert(polylines_out.end(), pp.begin(), pp.end());
-
-}
-
-ExtrusionEntitiesPtr
-thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow, coord_t resolution_internal)
-{
- assert(resolution_internal > SCALED_EPSILON);
-
- // this value determines granularity of adaptive width, as G-code does not allow
- // variable extrusion within a single move; this value shall only affect the amount
- // of segments, and any pruning shall be performed before we apply this tolerance
- const coord_t tolerance = flow.scaled_width() / 10;//scale_(0.05);
-
- ExtrusionEntitiesPtr coll;
- for (const ThickPolyline &p : polylines) {
- ExtrusionPaths paths;
- ExtrusionPath path(role);
- ThickLines lines = p.thicklines();
-
- coordf_t saved_line_len = 0;
- for (int i = 0; i < (int)lines.size(); ++i) {
- ThickLine& line = lines[i];
-
- const coordf_t line_len = line.length();
- const coordf_t prev_line_len = saved_line_len;
- saved_line_len = line_len;
-
- assert(line.a_width >= 0 && !std::isnan(line.a_width));
- assert(line.b_width >= 0 && !std::isnan(line.b_width));
- coord_t thickness_delta = std::abs(line.a_width - line.b_width);
-
- // split lines ?
- if (resolution_internal < line_len) {
- if (thickness_delta > tolerance && ceil(float(thickness_delta) / float(tolerance)) > 2) {
- const uint16_t segments = 1 + (uint16_t)std::min((uint32_t)16000, (uint32_t)ceil(float(thickness_delta) / float(tolerance)));
- Points pp;
- std::vector<coordf_t> width;
- {
- for (size_t j = 0; j < segments; ++j) {
- pp.push_back(line.a.interpolate(((double)j) / segments, line.b));
- double percent_width = ((double)j) / (segments - 1);
- width.push_back(line.a_width * (1 - percent_width) + line.b_width * percent_width);
- }
- pp.push_back(line.b);
-
- assert(pp.size() == segments + 1);
- assert(width.size() == segments);
- }
-
- // delete this line and insert new ones
- lines.erase(lines.begin() + i);
- for (size_t j = 0; j < segments; ++j) {
- ThickLine new_line(pp[j], pp[j + 1]);
- new_line.a_width = width[j];
- new_line.b_width = width[j];
- lines.insert(lines.begin() + i + j, new_line);
- }
-
- // go back to the start of this loop iteration
- --i;
- continue;
- } else if (thickness_delta > 0) {
- //create a middle point
- ThickLine new_line(line.a.interpolate(0.5, line.b), line.b);
- new_line.a_width = line.b_width;
- new_line.b_width = line.b_width;
- line.b = new_line.a;
- line.b_width = line.a_width;
- lines.insert(lines.begin() + i + 1, new_line);
-
- // go back to the start of this loop iteration
- --i;
- continue;
- }
- } else if (i > 0 && resolution_internal > line_len + prev_line_len) {
- //merge lines?
- //if it's a loop, merge only if the distance is high enough
- if (p.first_point() == p.last_point() && p.length() < (line_len + prev_line_len) * 6)
- continue;
- ThickLine& prev_line = lines[i - 1];
- coordf_t width = prev_line_len * (prev_line.a_width + prev_line.b_width) / 2;
- width += line_len * (line.a_width + line.b_width) / 2;
- prev_line.b = line.b;
- const coordf_t new_length = prev_line.length();
- if (new_length < SCALED_EPSILON) {
- // too short, remove it and restart
- if (i > 1) {
- line.a = lines[i - 2].b;
- }
- lines.erase(lines.begin() + i-1);
- i-=2;
- continue;
- }
- width /= new_length;
- prev_line.a_width = width;
- prev_line.b_width = width;
- saved_line_len = new_length;
- //erase 'line'
- lines.erase(lines.begin() + i);
- --i;
- continue;
- } else if (thickness_delta > 0) {
- //set width as a middle-ground
- line.a_width = (line.a_width + line.b_width) / 2;
- line.b_width = line.a_width;
- }
- }
- for (int i = 0; i < (int)lines.size(); ++i) {
- ThickLine& line = lines[i];
-
- //gapfill : we want to be able to fill the voids (touching the perimeters), so the spacing is what we want.
- //thinwall: we want the extrusion to not go out of the polygon, so the width is what we want.
- // but we can't extrude with a negative spacing, so we have to gradually fall back to spacing if the width is too small.
-
- // default: extrude a thin wall that doesn't go outside of the specified width.
- coordf_t wanted_width = unscaled(line.a_width);
- if (role == erGapFill) {
- // Convert from spacing to extrusion width based on the extrusion model
- // of a square extrusion ended with semi circles.
- wanted_width = unscaled(line.a_width) + flow.height * (1. - 0.25 * PI);
- } else if (unscale<coordf_t>(line.a_width) < 2 * flow.height * (1. - 0.25 * PI)) {
- //width (too) small, be sure to not extrude with negative spacing.
- //we began to fall back to spacing gradually even before the spacing go into the negative
- // to make extrusion1 < extrusion2 if width1 < width2 even if width2 is too small.
- wanted_width = unscaled(line.a_width)*0.35 + 1.3 * flow.height * (1. - 0.25 * PI);
- }
-
- if (path.polyline.points.empty()) {
- flow.width = wanted_width;
- path.polyline.append(line.a);
- path.polyline.append(line.b);
- assert(flow.mm3_per_mm() == flow.mm3_per_mm());
- assert(flow.width == flow.width);
- assert(flow.height == flow.height);
- path.mm3_per_mm = flow.mm3_per_mm();
- path.width = flow.width;
- path.height = flow.height;
- } else {
- coord_t thickness_delta = scale_t(fabs(flow.width - wanted_width));
- if (thickness_delta <= tolerance / 2) {
- // the width difference between this line and the current flow width is
- // within the accepted tolerance
- path.polyline.append(line.b);
- } else {
- // we need to initialize a new line
- paths.emplace_back(std::move(path));
- path = ExtrusionPath(role);
- flow.width = wanted_width;
- path.polyline.append(line.a);
- path.polyline.append(line.b);
- assert(flow.mm3_per_mm() == flow.mm3_per_mm());
- assert(flow.width == flow.width);
- assert(flow.height == flow.height);
- path.mm3_per_mm = flow.mm3_per_mm();
- path.width = flow.width;
- path.height = flow.height;
- }
- }
- assert(path.polyline.points.size() > 2 || path.first_point() != path.last_point());
- }
- if (path.polyline.is_valid())
- paths.emplace_back(std::move(path));
-
- // Append paths to collection.
- if (!paths.empty()) {
- if (paths.front().first_point().coincides_with_epsilon(paths.back().last_point())) {
- coll.push_back(new ExtrusionLoop(std::move(paths)));
- } else {
- if (role == erThinWall){
- //thin walls : avoid to cut them, please.
- //also, keep the start, as the start should be already in a frontier where possible.
- ExtrusionEntityCollection *unsortable_coll = new ExtrusionEntityCollection(std::move(paths));
- unsortable_coll->set_can_sort_reverse(false, false);
- coll.push_back(unsortable_coll);
- } else {
- if (paths.size() == 1) {
- coll.push_back(paths.front().clone_move());
- } else {
- ExtrusionEntityCollection *unsortable_coll = new ExtrusionEntityCollection(std::move(paths));
- //gap fill : can reverse, but refrain from cutting them as it creates a mess.
- // I say that, but currently (false, true) does bad things.
- unsortable_coll->set_can_sort_reverse(false, true);
- coll.push_back(unsortable_coll);
- }
- }
- }
- }
- }
- return coll;
-}
-
-} // namespace Slic3r
diff --git a/src/libslic3r/MedialAxis.hpp b/src/libslic3r/MedialAxis.hpp
deleted file mode 100644
index e8614c5ba..000000000
--- a/src/libslic3r/MedialAxis.hpp
+++ /dev/null
@@ -1,122 +0,0 @@
-#ifndef slic3r_MedialAxis_hpp_
-#define slic3r_MedialAxis_hpp_
-
-#include "libslic3r.h"
-#include "ExPolygon.hpp"
-#include "Polyline.hpp"
-#include "Geometry.hpp"
-#include "ExtrusionEntityCollection.hpp"
-#include "Flow.hpp"
-#include <vector>
-
-#define BOOST_VORONOI_USE_GMP 1
-#include "boost/polygon/voronoi.hpp"
-using boost::polygon::voronoi_builder;
-using boost::polygon::voronoi_diagram;
-
-namespace Slic3r {
-
-/// This class is used to create single-line extrusion pattern with variable width to cover a ExPolygon.
-/// The ends can enter a boundary area if neded, and can have a taper at each end.
-/// The constructor initialize the mandatory variable.
-/// you must use the setter to add the opptional settings before calling build().
-class MedialAxis {
- public:
- //static int staticid;
- //int id;
- /// _expolygon: the polygon to fill
- /// _max_width : maximum width of the extrusion. _expolygon shouldn't have a spot where a circle diameter is higher than that (or almost).
- /// _min_width : minimum width of the extrusion, every spot where a circle diameter is lower than that will be ignored (unless it's the tip of the extrusion)
- /// _height: height of the extrusion, used to compute the difference between width and spacing.
- MedialAxis(const ExPolygon &_expolygon, const coord_t _max_width, const coord_t _min_width, const coord_t _height)
- : surface(_expolygon), max_width(_max_width), min_width(_min_width), height(_height),
- bounds(&_expolygon), nozzle_diameter(_min_width), taper_size(0), stop_at_min_width(true){/*id= staticid;staticid++;*/};
-
- /// create the polylines_out collection of variable-width polyline to extrude.
- void build(ThickPolylines &polylines_out);
- /// You shouldn't use this method as it doesn't give you the variable width. Can be useful for debugging.
- void build(Polylines &polylines);
-
- /// optional parameter: anchor area in which the extrusion should extends into. Default : expolygon (no bound)
- MedialAxis& use_bounds(const ExPolygon & _bounds) { this->bounds = &_bounds; return *this; }
- /// optional parameter: the real minimum width : it will grow the width of every extrusion that has a width lower than that. Default : min_width (same min)
- MedialAxis& use_min_real_width(const coord_t nozzle_diameter) { this->nozzle_diameter = nozzle_diameter; return *this; }
- /// optional parameter: create a taper of this length at each end (inside a bound or not). Default : 0 (no taper)
- MedialAxis& use_tapers(const coord_t taper_size) { this->taper_size = taper_size; return *this; }
- /// optional parameter: if true, the entension inside the bounds can be cut if the width is too small. Default : true
- MedialAxis& set_stop_at_min_width(const bool stop_at_min_width) { this->stop_at_min_width = stop_at_min_width; return *this; }
-
- private:
-
- /// input polygon to fill
- const ExPolygon& surface;
- /// the copied expolygon from surface, it's modified in build() to simplify it. It's then used to create the voronoi diagram.
- ExPolygon expolygon;
- const ExPolygon* bounds;
- /// maximum width of the extrusion. _expolygon shouldn't have a spot where a circle diameter is higher than that (or almost).
- const coord_t max_width;
- /// minimum width of the extrusion, every spot where a circle diameter is lower than that will be ignored (unless it's the tip of the extrusion)
- const coord_t min_width;
- /// height of the extrusion, used to compute the diufference between width and spacing.
- const coord_t height;
- /// Used to compute the real minimum width we can extrude. if != min_width, it activate grow_to_nozzle_diameter().
- coord_t nozzle_diameter;
- /// if != , it activates taper_ends(). Can use nozzle_diameter.
- coord_t taper_size;
- //if true, remove_too_* can shorten the bits created by extends_line.
- bool stop_at_min_width;
-
- //voronoi stuff
- class VD : public voronoi_diagram<double> {
- public:
- typedef double coord_type;
- typedef boost::polygon::point_data<coordinate_type> point_type;
- typedef boost::polygon::segment_data<coordinate_type> segment_type;
- typedef boost::polygon::rectangle_data<coordinate_type> rect_type;
- };
- void process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* polyline, std::set<const VD::edge_type*> &edges, std::set<const VD::edge_type*> &valid_edges, std::map<const VD::edge_type*, std::pair<coordf_t, coordf_t> > &thickness);
- bool validate_edge(const VD::edge_type* edge, Lines &lines, std::map<const VD::edge_type*, std::pair<coordf_t, coordf_t> > &thickness);
- const Line& retrieve_segment(const VD::cell_type* cell, Lines& lines) const;
- const Point& retrieve_endpoint(const VD::cell_type* cell, Lines& lines) const;
- void polyline_from_voronoi(const Lines& voronoi_edges, ThickPolylines* polylines_out);
-
- // functions called by build:
-
- /// create a simplied version of surface, store it in expolygon
- void simplify_polygon_frontier();
- /// fusion little polylines created (by voronoi) on the external side of a curve inside the main polyline.
- void fusion_curve(ThickPolylines &pp);
- /// fusion polylines created by voronoi, where needed.
- void main_fusion(ThickPolylines& pp);
- /// like fusion_curve but for sharp angles like a square corner.
- void fusion_corners(ThickPolylines &pp);
- /// extends the polylines inside bounds, use extends_line on both end
- void extends_line_both_side(ThickPolylines& pp);
- /// extends the polylines inside bounds (anchors)
- void extends_line(ThickPolyline& polyline, const ExPolygons& anchors, const coord_t join_width);
- /// remove too thin bits at start & end of polylines
- void remove_too_thin_extrusion(ThickPolylines& pp);
- /// instead of keeping polyline split at each corssing, we try to create long strait polylines that can cross each other.
- void concatenate_polylines_with_crossing(ThickPolylines& pp);
- /// remove bits around points that are too thin (can be inside the polyline)
- void remove_too_thin_points(ThickPolylines& pp);
- /// delete polylines that are too short
- void remove_too_short_polylines(ThickPolylines& pp, const coord_t min_size);
- /// be sure we didn't try to push more plastic than the volume defined by surface * height can receive. If overextruded, reduce all widths by the correct %.
- void ensure_not_overextrude(ThickPolylines& pp);
- /// if nozzle_diameter > min_width, grow bits that are < width(nozzle_diameter) to width(nozzle_diameter) (don't activate that for gapfill)
- void grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchors);
- /// taper the ends of polylines (don't activate that for gapfill)
- void taper_ends(ThickPolylines& pp);
- //cleaning method
- void check_width(ThickPolylines& pp, coord_t max_width, std::string msg);
- //removing small extrusion that won't be useful and will harm print. A bit like fusion_corners but more lenient and with just del.
- void remove_bits(ThickPolylines& pp);
-};
-
- /// create a ExtrusionEntitiesPtr from ThickPolylines, discretizing the variable width into little sections (of 4*SCALED_RESOLUTION length) where needed. Please delete all ptr if not used.
- ExtrusionEntitiesPtr thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow, coord_t resolution_internal);
-}
-
-
-#endif
diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp
index c8e518a55..61cfa0891 100644
--- a/src/libslic3r/PerimeterGenerator.cpp
+++ b/src/libslic3r/PerimeterGenerator.cpp
@@ -514,7 +514,7 @@ void PerimeterGenerator::process()
}
// allow this perimeter to overlap itself?
- float thin_perimeter = perimeter_idx == 0 ? this->config->thin_perimeters.get_abs_value(1) : this->config->thin_perimeters_all.get_abs_value(1);
+ float thin_perimeter = perimeter_idx == 0 ? this->config->thin_perimeters.get_abs_value(1) : (this->config->thin_perimeters.get_abs_value(1)==0 ? 0 : this->config->thin_perimeters_all.get_abs_value(1));
if (thin_perimeter < 0.02) // can create artifacts
thin_perimeter = 0;
@@ -617,6 +617,7 @@ void PerimeterGenerator::process()
ma.use_bounds(bound)
.use_min_real_width(scale_t(this->ext_perimeter_flow.nozzle_diameter()))
.use_tapers(thin_walls_overlap)
+ .set_min_length(ext_perimeter_width + ext_perimeter_spacing)
.build(thin_walls_thickpolys);
}
break;
diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index c2f380dd9..5bfa74985 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -510,6 +510,7 @@ static std::vector<std::string> s_Preset_print_options {
"default_speed",
"bridge_speed",
"bridge_speed_internal",
+ "brim_speed",
"external_perimeter_speed",
"first_layer_speed",
"first_layer_min_speed",
@@ -540,6 +541,7 @@ static std::vector<std::string> s_Preset_print_options {
// acceleration
"bridge_acceleration",
"bridge_internal_acceleration",
+ "brim_acceleration",
"default_acceleration",
"external_perimeter_acceleration",
"first_layer_acceleration",
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 652b683a9..bde2beb52 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -862,7 +862,21 @@ void PrintConfigDef::init_fff_params()
def->min = 0;
def->max = 180;
def->mode = comAdvancedE | comSuSi;
- def->set_default_value(new ConfigOptionFloat(125));
+ def->set_default_value(new ConfigOptionFloat(125));
+
+ def = this->add("brim_acceleration", coFloatOrPercent);
+ def->label = L("Brim & Skirt");
+ def->full_label = L("Brim & Skirt acceleration");
+ def->category = OptionCategory::speed;
+ def->tooltip = L("This is the acceleration your printer will use for brim and skirt. "
+ "\nCan be a % of the support acceleration"
+ "\nSet zero to use support acceleration.");
+ def->sidetext = L("mm/s² or %");
+ def->ratio_over = "support_material_acceleration";
+ def->min = 0;
+ def->max_literal = { -200, false };
+ def->mode = comAdvancedE | comSuSi;
+ def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
def = this->add("brim_ears_detection_length", coFloat);
def->label = L("Detection radius");
@@ -900,6 +914,19 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloat(0));
def->aliases = { "brim_offset" }; // from superslicer 2.3
+ def = this->add("brim_speed", coFloatOrPercent);
+ def->label = L("Brim & Skirt");
+ def->full_label = L("Brim & Skirt speed");
+ def->category = OptionCategory::speed;
+ def->tooltip = L("This separate setting will affect the speed of brim and skirt. "
+ "\nIf expressed as percentage (for example: 80%) it will be calculated over the Support speed setting."
+ "\nSet zero to use autospeed for this feature.");
+ def->sidetext = L("mm/s or %");
+ def->ratio_over = "support_material_speed";
+ def->min = 0;
+ def->mode = comExpert | comSuSi;
+ def->set_default_value(new ConfigOptionFloatOrPercent(50, true));
+
#if 0
def = this->add("brim_type", coEnum);
def->label = L("Brim type");
@@ -6969,11 +6996,13 @@ std::unordered_set<std::string> prusa_export_to_remove_keys = {
"bridge_speed_internal",
"bridge_type",
"bridged_infill_margin",
+"brim_acceleration",
"brim_ears_detection_length",
"brim_ears_max_angle",
"brim_ears_pattern",
"brim_ears",
"brim_inside_holes",
+"brim_speed",
"brim_width_interior",
"chamber_temperature",
"complete_objects_one_brim",
@@ -7161,7 +7190,12 @@ std::map<std::string, std::string> PrintConfigDef::to_prusa(t_config_option_key&
}
} else if ("elephant_foot_min_width" == opt_key) {
opt_key = "elefant_foot_min_width";
- } else if ("first_layer_acceleration" == opt_key || "infill_acceleration" == opt_key || "bridge_acceleration" == opt_key || "default_acceleration" == opt_key || "perimeter_acceleration" == opt_key
+ } else if("first_layer_acceleration" == opt_key) {
+ if (value.find("%") != std::string::npos) {
+ // can't support %, so we uese the default accel a baseline for half-assed conversion
+ value = std::to_string(all_conf.get_abs_value(opt_key, all_conf.get_computed_value("default_acceleration")));
+ }
+ } else if ("infill_acceleration" == opt_key || "bridge_acceleration" == opt_key || "default_acceleration" == opt_key || "perimeter_acceleration" == opt_key
|| "overhangs_speed" == opt_key || "ironing_speed" == opt_key || "perimeter_speed" == opt_key || "infill_speed" == opt_key || "bridge_speed" == opt_key || "support_material_speed" == opt_key
|| "max_print_speed" == opt_key
) {
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index b507e813e..d500818eb 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -774,6 +774,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloatOrPercent, bridged_infill_margin))
((ConfigOptionFloatOrPercent, bridge_speed))
((ConfigOptionFloatOrPercent, bridge_speed_internal))
+ ((ConfigOptionFloatOrPercent, brim_speed))
((ConfigOptionFloat, curve_smoothing_precision))
((ConfigOptionFloat, curve_smoothing_cutoff_dist))
((ConfigOptionFloat, curve_smoothing_angle_convex))
@@ -1083,6 +1084,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionFloatOrPercent, bridge_internal_acceleration))
((ConfigOptionInts, bridge_fan_speed))
((ConfigOptionInts, bridge_internal_fan_speed))
+ ((ConfigOptionFloatOrPercent, brim_acceleration))
((ConfigOptionInts, chamber_temperature))
((ConfigOptionBool, complete_objects))
((ConfigOptionBool, complete_objects_one_skirt))
diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index c90886003..7445caa74 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -4282,7 +4282,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Filler and its parameters
filler, float(supp_density),
// Extrusion parameters
- erSupportMaterialInterface, interface_flow, filler_spacing,
+ interface_as_base ? erSupportMaterial : erSupportMaterialInterface, interface_flow, filler_spacing,
m_object->print()->default_region_config());
}
diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp
index 0a72cc4dd..90182f00c 100644
--- a/src/slic3r/GUI/ConfigManipulation.cpp
+++ b/src/slic3r/GUI/ConfigManipulation.cpp
@@ -453,7 +453,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field("perimeter_extrusion_spacing", have_perimeters || have_brim);
toggle_field("skirt_extrusion_width", have_skirt);
toggle_field("support_material_extruder", have_support_material || have_skirt);
- toggle_field("support_material_speed", have_support_material || have_brim || have_skirt);
+ toggle_field("support_material_speed", have_support_material);
+ toggle_field("brim_speed", have_brim || have_skirt);
toggle_field("raft_contact_distance", have_raft && !have_support_soluble);
for (auto el : { "raft_expansion", "first_layer_acceleration_over_raft", "first_layer_speed_over_raft" })
@@ -515,6 +516,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field("ironing_acceleration", have_default_acceleration && has_ironing);
toggle_field("support_material_acceleration", have_default_acceleration && (have_support_material || have_brim || have_skirt));
toggle_field("support_material_interface_acceleration", have_default_acceleration && have_support_material && have_support_interface);
+ toggle_field("brim_acceleration", have_default_acceleration && have_support_material && (have_brim || have_skirt));
for (auto el : { "bridge_acceleration", "bridge_internal_acceleration", "overhangs_acceleration", "gap_fill_acceleration", "travel_acceleration", "travel_deceleration_use_target", "first_layer_acceleration" })
toggle_field(el, have_default_acceleration);
diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp
index dd2a47ce7..3d96c1027 100644
--- a/src/slic3r/GUI/ConfigWizard.cpp
+++ b/src/slic3r/GUI/ConfigWizard.cpp
@@ -134,18 +134,24 @@ BundleMap BundleMap::load()
// and then additionally from resources/profiles.
bool is_in_resources = false;
for (auto dir : { &vendor_dir, &rsrc_vendor_dir }) {
- for (const auto &dir_entry : boost::filesystem::directory_iterator(*dir)) {
- if (Slic3r::is_ini_file(dir_entry)) {
- std::string id = dir_entry.path().stem().string(); // stem() = filename() without the trailing ".ini" part
+ try {
+ for (const auto& dir_entry : boost::filesystem::directory_iterator(*dir)) {
+ if (Slic3r::is_ini_file(dir_entry)) {
+ std::string id = dir_entry.path().stem().string(); // stem() = filename() without the trailing ".ini" part
- // Don't load this bundle if we've already loaded it.
- if (res.find(id) != res.end()) { continue; }
+ // Don't load this bundle if we've already loaded it.
+ if (res.find(id) != res.end()) { continue; }
- Bundle bundle;
- if (bundle.load(dir_entry.path(), is_in_resources))
- res.emplace(std::move(id), std::move(bundle));
+ Bundle bundle;
+ if (bundle.load(dir_entry.path(), is_in_resources))
+ res.emplace(std::move(id), std::move(bundle));
+ }
}
}
+ catch (std::exception e) {
+ MessageDialog msg(nullptr, format_wxstr(_L("Can't open directory '%1%'. Config bundles from here can't be loaded."), vendor_dir.string()), _L("Error"), wxOK);
+ msg.ShowModal();
+ }
is_in_resources = true;
}
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index ac9311f4a..7361acdb4 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -1030,14 +1030,12 @@ void CheckBox::msw_rescale()
wxCheckBox* chk = dynamic_cast<wxCheckBox*>(window);
if (chk != nullptr) {
- std::cout << "chk->GetFont().GetPixelSize().y = " << chk->GetFont().GetPixelSize().y << "\n";
chk->SetMinSize(wxSize(-1, int(1.5f * chk->GetFont().GetPixelSize().y + 0.5f)));
}
#ifdef __WXGTK2__
else
{ //a bit useless as it's a windows-only func. To have a correct thing, you have to del the previous window and create a new one anyway.
wxToggleButton* tgl = dynamic_cast<wxToggleButton*>(window);
- std::cout << "tgl->GetFont().GetPixelSize().y = " << tgl->GetFont().GetPixelSize().y << "\n";
if (tgl) tgl->SetMinSize(wxSize(def_width_thinner() * m_em_unit / 2, def_width_thinner() * m_em_unit / 2));
}
#endif
diff --git a/src/slic3r/GUI/FreeCADDialog.cpp b/src/slic3r/GUI/FreeCADDialog.cpp
index a08a74111..44d516acf 100644
--- a/src/slic3r/GUI/FreeCADDialog.cpp
+++ b/src/slic3r/GUI/FreeCADDialog.cpp
@@ -452,7 +452,7 @@ void FreeCADDialog::on_autocomp_complete(wxStyledTextEvent& event) {
} else if (((command->type & PyCommandType::pctMODIFIER) != 0) && !has_already_parenthese) {
int nb_add_pos = 0;
//check if there's not a forgotten '.' before
- std::cout << "char before the word : " << stc->GetCharAt(currentPos - command->name.length() - 1) << "\n";
+ BOOST_LOG_TRIVIAL(warning) << "char before the word : " << stc->GetCharAt(currentPos - command->name.length() - 1) << "\n";
if (stc->GetCharAt(currentPos - command->name.length() - 1) == ')') {
stc->InsertText(currentPos - command->name.length(), ".");
nb_add_pos++;
@@ -481,19 +481,9 @@ void FreeCADDialog::on_word_change_for_autocomplete(wxStyledTextEvent& event) {
if ((event.GetModificationType() & (wxSTC_MOD_INSERTTEXT | wxSTC_PERFORMED_USER)) != (wxSTC_MOD_INSERTTEXT | wxSTC_PERFORMED_USER)) {
return; // not our event
}
- //std::cout << "word_change "<<event.GetModificationType()<<" typed: " << (int)stc->GetCharAt(current_pos) << " with len_entered " << len_entered
- //<< ", event_string='"<< event_string << "' (last='"<< (event_string.empty()?-1:event_string.Last().GetValue()) <<"') ; Str is '" << str << "' with length " << str.length()
- // << "' chars are (c-1)='" << stc->GetCharAt(current_pos - 1) << "' : (c)='" << stc->GetCharAt(current_pos)
- //<< "', text length=" << stc->GetTextLength() << ", currentPos=" << current_pos << " , " << int('\n') << "\n";
- //std::cout << "test: " << (!(stc->GetCharAt(current_pos - 1) <= '\n')) << ", 2=" << (len_entered >= 0) << ", 3=" << (!str.empty())
- // << ", 4=" << (std::regex_match(str.ToStdString(), word_regex))
- // <<", Mod5="<<((event.GetModificationType() & wxSTC_STARTACTION) != 0)
- // <<", 6="<< (current_pos <= 1 || str != ".")<<", 6b="<< (str == ".")
- // << "\n";
if ((event.GetModificationType() & wxSTC_STARTACTION) != 0 && (str.empty() || str.Last() != '.'))
return;
- //if (!event_string.empty() && !str.empty() && int(str[str.length() - 1]) != event_string.Last().GetValue()) std::cout << "removecall?\n";
if (len_entered >= 0 && !str.empty() && std::regex_match(str.ToStdString(), word_regex)) {
//check for possible words
//todo: check for '.' to filter for modifiers
@@ -509,33 +499,24 @@ void FreeCADDialog::on_word_change_for_autocomplete(wxStyledTextEvent& event) {
if (nb_words >= 1)
stc->AutoCompShow(len_entered, possible);
} else if (!str.empty() && str.Last() == '.') {
- //wxString possible;
- //for(const wxString &str : modif_words)
- // possible += possible.empty() ? str : (" " + str);
wxString possible;
for (const PyCommand &cmd : commands) {
if (((cmd.type & PyCommandType::pctMODIFIER) != 0) && ((cmd.type & PyCommandType::pctDO_NOT_SHOW) == 0)) {
possible += possible.empty() ? cmd.name : (" " + cmd.name);
}
}
- //std::cout << "autocomplete: modifier: '"<< possible.ToStdString() <<"'\n";
if (possible.length() >= 1)
stc->AutoCompShow(0, possible);
}
}
void FreeCADDialog::on_char_add(wxStyledTextEvent& event) {
- //if (event.GetUpdated() != wxSTC_UPDATE_CONTENT) return;
wxStyledTextCtrl* stc = (wxStyledTextCtrl*)event.GetEventObject();
// Find the word start
int current_pos = stc->GetCurrentPos();
int word_start_pos = stc->WordStartPosition(current_pos, true);
//int len_entered = current_pos - word_start_pos;
const wxString str = stc->GetTextRange(word_start_pos, current_pos + 1);
- //if(current_pos>1)
- // std::cout << "char typed: " << (char)stc->GetCharAt(current_pos)<<" with length "<< len_entered
- // << ", str is "<< str << "chars are '"<< stc->GetCharAt(current_pos - 1) << "' : '" << stc->GetCharAt(current_pos)
- // <<"', text length="<< stc->GetTextLength()<<", currentPos="<< current_pos<<" , "<<int('\n')<<"\n";
if(current_pos > 2 && stc->GetCharAt(current_pos-1) == '\n'){
//TODO: check that we are really in a freepyscad section.
int lastpos = current_pos - 2;
@@ -615,7 +596,6 @@ void FreeCADDialog::comment(bool is_switch) {
}
void FreeCADDialog::on_char_type(wxKeyEvent &event) {
- //std::cout << "on_char_type " << event.GetUnicodeKey() <<", " << event.GetModifiers() << "\n";
if (event.GetUnicodeKey() == 'Q' && event.GetModifiers() == wxMOD_CONTROL) {
comment(true);
} else if (event.GetUnicodeKey() == 'K' && event.GetModifiers() == wxMOD_CONTROL) {
@@ -629,14 +609,12 @@ void FreeCADDialog::on_char_type(wxKeyEvent &event) {
// space, back, del are ok but no ascii char
void FreeCADDialog::on_key_type(wxKeyEvent& event)
{
- //std::cout << "on_key_type " << event.GetUnicodeKey() << " ? "<< int('Q') <<", "<< event.GetKeyCode() << ", " << event.GetModifiers() << "\n";
if (event.GetKeyCode() == WXK_SPACE && event.GetModifiers() == wxMOD_CONTROL)
{
//get word, if any
int current_pos = m_text->GetCurrentPos();
int word_start_pos = m_text->WordStartPosition(current_pos, true);
const wxString str = m_text->GetTextRange(word_start_pos, current_pos);
- //std::cout << "ctrl-space! " << event.GetEventType() << " '" << str.ToStdString() << "' " << int(m_text->GetCharAt(current_pos - 1)) << "\n";
if (current_pos > 0 && m_text->GetCharAt(current_pos - 1) == '.') {
//only modifiers
wxString possible;
@@ -656,7 +634,6 @@ void FreeCADDialog::on_key_type(wxKeyEvent& event)
nb_words++; possible += possible.empty() ? cmd.name : (" " + cmd.name);
}
}
- //std::cout << "space autocomplete: find " << nb_words << " forstring '" << str << "'\n";
// Display the autocompletion list
if (nb_words >= 1)
m_text->AutoCompShow(str.length(), possible);
@@ -714,10 +691,8 @@ void FreeCADDialog::createSTC()
m_text = new wxStyledTextCtrl(this, wxID_ANY,
wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO);
- //m_text->SetMarginWidth(MARGIN_LINE_NUMBERS, 50);
m_text->StyleSetForeground(wxSTC_STYLE_LINENUMBER, wxColour(75, 75, 75));
m_text->StyleSetBackground(wxSTC_STYLE_LINENUMBER, wxColour(220, 220, 220));
- //m_text->SetMarginType(MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER);
m_text->SetTabWidth(4);
m_text->SetIndent(4);
@@ -818,22 +793,20 @@ void FreeCADDialog::test_update_script_file(std::string &json) {
boost::locale::generator gen;
std::locale loc = gen.generate(""); // or "C", "en_US.UTF-8" etc.
std::locale::global(loc);
- std::cout.imbue(loc);
- std::cout << "root.commit.committer.date=" << str_date << "\n";
+ BOOST_LOG_TRIVIAL(debug) << "root.commit.committer.date=" << str_date;
std::time_t commit_time = parse_iso_time(str_date);
- std::cout << "github time_t = "<<commit_time<<"\n";
+ BOOST_LOG_TRIVIAL(debug) << "github time_t = " << commit_time;
std::time_t last_modif = boost::filesystem::last_write_time(pyscad_path / "freepyscad.py");
- std::cout << "pyscad_path time_t = " << commit_time << "\n";
+ BOOST_LOG_TRIVIAL(debug) << "pyscad_path time_t = " << commit_time;
if (commit_time > last_modif) {
- std::cout << "have to update!!\n";
get_file_from_web("https://raw.githubusercontent.com/supermerill/FreePySCAD/master/__init__.py", pyscad_path / "__init__.py");
get_file_from_web("https://raw.githubusercontent.com/supermerill/FreePySCAD/master/Init.py", pyscad_path / "Init.py");
get_file_from_web("https://raw.githubusercontent.com/supermerill/FreePySCAD/master/freepyscad.py", pyscad_path / "freepyscad.py");
}
}
catch (std::exception ex) {
- std::cerr << "Error, cannot parse https://api.github.com/repos/supermerill/FreePySCAD/commits/master: " << ex.what() << "\n";
+ BOOST_LOG_TRIVIAL(error) << "Error, cannot parse https://api.github.com/repos/supermerill/FreePySCAD/commits/master: " << ex.what();
}
}
@@ -872,7 +845,7 @@ bool FreeCADDialog::init_start_python() {
get_file_from_web("https://raw.githubusercontent.com/supermerill/FreePySCAD/master/__init__.py", scripts_path / "FreePySCAD" / "__init__.py");
get_file_from_web("https://raw.githubusercontent.com/supermerill/FreePySCAD/master/Init.py", scripts_path / "FreePySCAD" / "Init.py");
get_file_from_web("https://raw.githubusercontent.com/supermerill/FreePySCAD/master/freepyscad.py", scripts_path / "FreePySCAD" / "freepyscad.py");
- }else if (!update_done){
+ } else if (!update_done) {
update_done = true;
//try to check last version on website
//it's async so maybe you won't update it in time, but it's not the end of the world.
@@ -881,7 +854,7 @@ bool FreeCADDialog::init_start_python() {
get_string_from_web_async("https://api.github.com/repos/supermerill/FreePySCAD/commits/master", this, &FreeCADDialog::test_update_script_file);
}
- exec_var->process.reset(new boost::process::child(pythonpath.string() + " -u -i", boost::process::std_in < exec_var->pyin,
+ exec_var->process.reset(new boost::process::child(pythonpath.string() + " -u -i", boost::process::std_in < exec_var->pyin,
boost::process::std_out > exec_var->data_out, boost::process::std_err > exec_var->data_err, exec_var->ios));
exec_var->pyin << "import sys" << std::endl;
#ifndef __WINDOWS__
@@ -892,9 +865,7 @@ bool FreeCADDialog::init_start_python() {
exec_var->pyin << "import Part" << std::endl;
exec_var->pyin << "import Draft" << std::endl;
exec_var->pyin << "sys.path.append('" << scripts_path.generic_string() << "')" << std::endl;
- //std::cout << "sys.path.append('" << pyscad_path.generic_string() << "')" << std::endl;
exec_var->pyin << "from FreePySCAD.freepyscad import *" << std::endl;
- //std::cout << "from FreePySCAD.freepyscad import *" << std::endl;
exec_var->pyin << "App.newDocument(\"document\")" << std::endl;
#ifdef __WINDOWS__
exec_var->pyin << "set_font_dir(\"C:/Windows/Fonts/\")" << std::endl;
@@ -997,7 +968,6 @@ void FreeCADDialog::create_geometry(wxCommandEvent& event_args) {
}
- //std::cout<< "scene().redraw(" << boost::replace_all_copy(boost::replace_all_copy(m_text->GetText(), "\r", ""), "\n", "") << ")" << std::endl;
//exec_var->pyin << "scene().redraw("<< boost::replace_all_copy(boost::replace_all_copy(m_text->GetText(), "\r", ""), "\n", "") <<")" << std::endl;
exec_var->pyin << ("exec(open('" + temp_file.generic_string() + "').read())\n");
//filter to avoid importing "intermediate" object like ones from importStl
@@ -1008,11 +978,9 @@ void FreeCADDialog::create_geometry(wxCommandEvent& event_args) {
end_python();
std::string pyout_str_hello;
- std::cout << "==cout==\n";
- std::cout << exec_var->data_out.get();
- std::cout << "==cerr==\n";
+ BOOST_LOG_TRIVIAL(trace) << "==cout==\n" << exec_var->data_out.get()<<"\n";
std::string errStr = exec_var->data_err.get();
- std::cout << errStr << "\n";
+ BOOST_LOG_TRIVIAL(trace) << "==cerr==\n" << errStr <<"\n";
std::string cleaned = boost::replace_all_copy(boost::replace_all_copy(errStr, ">>> ", ""),"\r","");
boost::replace_all(cleaned, "QWaitCondition: Destroyed while threads are still waiting\n", "");
boost::replace_all(cleaned, "Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n", "");