Merge branch 'master' into fs_emboss
This commit is contained in:
commit
25bb81b9f6
98 changed files with 540 additions and 7578 deletions
|
@ -920,29 +920,35 @@ template<class G> auto within(const G &g) { return Within<G>{g}; }
|
|||
|
||||
namespace detail {
|
||||
|
||||
// Returns true in case traversal should continue,
|
||||
// returns false if traversal should stop (for example if the first hit was found).
|
||||
template<int Dims, typename T, typename Pred, typename Fn>
|
||||
void traverse_recurse(const Tree<Dims, T> &tree,
|
||||
bool traverse_recurse(const Tree<Dims, T> &tree,
|
||||
size_t idx,
|
||||
Pred && pred,
|
||||
Fn && callback)
|
||||
{
|
||||
assert(tree.node(idx).is_valid());
|
||||
|
||||
if (!pred(tree.node(idx))) return;
|
||||
if (!pred(tree.node(idx)))
|
||||
// Continue traversal.
|
||||
return true;
|
||||
|
||||
if (tree.node(idx).is_leaf()) {
|
||||
callback(tree.node(idx));
|
||||
// Callback returns true to continue traversal, false to stop traversal.
|
||||
return callback(tree.node(idx));
|
||||
} else {
|
||||
|
||||
// call this with left and right node idx:
|
||||
auto trv = [&](size_t idx) {
|
||||
traverse_recurse(tree, idx, std::forward<Pred>(pred),
|
||||
std::forward<Fn>(callback));
|
||||
auto trv = [&](size_t idx) -> bool {
|
||||
return traverse_recurse(tree, idx, std::forward<Pred>(pred),
|
||||
std::forward<Fn>(callback));
|
||||
};
|
||||
|
||||
// Left / right child node index.
|
||||
trv(Tree<Dims, T>::left_child_idx(idx));
|
||||
trv(Tree<Dims, T>::right_child_idx(idx));
|
||||
// Returns true if both children allow the traversal to continue.
|
||||
return trv(Tree<Dims, T>::left_child_idx(idx)) &&
|
||||
trv(Tree<Dims, T>::right_child_idx(idx));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -952,6 +958,7 @@ void traverse_recurse(const Tree<Dims, T> &tree,
|
|||
// traverse(tree, intersecting(QueryBox), [](size_t face_idx) {
|
||||
// /* ... */
|
||||
// });
|
||||
// Callback shall return true to continue traversal, false if it wants to stop traversal, for example if it found the answer.
|
||||
template<int Dims, typename T, typename Predicate, typename Fn>
|
||||
void traverse(const Tree<Dims, T> &tree, Predicate &&pred, Fn &&callback)
|
||||
{
|
||||
|
|
|
@ -95,10 +95,8 @@ public:
|
|||
bool all_paths_inside_vertices_and_normals_interleaved(const std::vector<float>& paths, const Eigen::AlignedBox<float, 3>& bbox, bool ignore_bottom = true) const;
|
||||
|
||||
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_scene() const { return m_top_bottom_convex_hull_decomposition_scene; }
|
||||
const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_bed() const { return m_top_bottom_convex_hull_decomposition_bed; }
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
||||
private:
|
||||
// Source definition of the print bed geometry (PrintConfig::bed_shape)
|
||||
|
|
|
@ -130,6 +130,8 @@ set(SLIC3R_SOURCES
|
|||
GCode/PressureEqualizer.hpp
|
||||
GCode/PrintExtents.cpp
|
||||
GCode/PrintExtents.hpp
|
||||
GCode/RetractWhenCrossingPerimeters.cpp
|
||||
GCode/RetractWhenCrossingPerimeters.hpp
|
||||
GCode/SpiralVase.cpp
|
||||
GCode/SpiralVase.hpp
|
||||
GCode/SeamPlacer.cpp
|
||||
|
|
|
@ -140,6 +140,21 @@ namespace ClipperUtils {
|
|||
out.reserve(src.size());
|
||||
for (const Polygon &p : src)
|
||||
out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox));
|
||||
out.erase(
|
||||
std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }),
|
||||
out.end());
|
||||
return out;
|
||||
}
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(src.num_contours());
|
||||
out.emplace_back(clip_clipper_polygon_with_subject_bbox(src.contour, bbox));
|
||||
for (const Polygon &p : src.holes)
|
||||
out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox));
|
||||
out.erase(
|
||||
std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }),
|
||||
out.end());
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
@ -794,6 +809,8 @@ Polylines _clipper_pl_closed(ClipperLib::ClipType clipType, PathProvider1 &&subj
|
|||
return retval;
|
||||
}
|
||||
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip)
|
||||
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::PolygonsProvider(clip)); }
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip)
|
||||
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip)
|
||||
|
|
|
@ -323,6 +323,7 @@ namespace ClipperUtils {
|
|||
void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out);
|
||||
[[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox);
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox);
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox);
|
||||
}
|
||||
|
||||
// offset Polygons
|
||||
|
@ -427,6 +428,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPoly
|
|||
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygons &clip);
|
||||
|
|
|
@ -3090,7 +3090,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
|
|||
}
|
||||
|
||||
if (m_config.only_retract_when_crossing_perimeters && m_layer != nullptr &&
|
||||
m_config.fill_density.value > 0 && m_layer->any_internal_region_slice_contains(travel))
|
||||
m_config.fill_density.value > 0 && m_retract_when_crossing_perimeters.travel_inside_internal_regions(*m_layer, travel))
|
||||
// Skip retraction if travel is contained in an internal slice *and*
|
||||
// internal infill is enabled (so that stringing is entirely not visible).
|
||||
//FIXME any_internal_region_slice_contains() is potentionally very slow, it shall test for the bounding boxes first.
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "GCode/AvoidCrossingPerimeters.hpp"
|
||||
#include "GCode/CoolingBuffer.hpp"
|
||||
#include "GCode/FindReplace.hpp"
|
||||
#include "GCode/RetractWhenCrossingPerimeters.hpp"
|
||||
#include "GCode/SpiralVase.hpp"
|
||||
#include "GCode/ToolOrdering.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
|
@ -349,6 +350,7 @@ private:
|
|||
Wipe m_wipe;
|
||||
AvoidCrossingPerimeters m_avoid_crossing_perimeters;
|
||||
JPSPathFinder m_avoid_curled_filaments;
|
||||
RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters;
|
||||
bool m_enable_loop_clipping;
|
||||
// If enabled, the G-code generator will put following comments at the ends
|
||||
// of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _BRIDGE_FAN_START, _BRIDGE_FAN_END
|
||||
|
|
|
@ -35,16 +35,12 @@ static const float DEFAULT_TRAVEL_ACCELERATION = 1250.0f;
|
|||
static const size_t MIN_EXTRUDERS_COUNT = 5;
|
||||
static const float DEFAULT_FILAMENT_DIAMETER = 1.75f;
|
||||
static const float DEFAULT_FILAMENT_DENSITY = 1.245f;
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
static const float DEFAULT_FILAMENT_COST = 0.0f;
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
static const Slic3r::Vec3f DEFAULT_EXTRUDER_OFFSET = Slic3r::Vec3f::Zero();
|
||||
// taken from PrusaResearch.ini - [printer:Original Prusa i3 MK2.5 MMU2]
|
||||
static const std::vector<std::string> DEFAULT_EXTRUDER_COLORS = { "#FF8000", "#DB5182", "#3EC0FF", "#FF4F4F", "#FBEB7D" };
|
||||
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
static const std::string INTERNAL_G2G3_TAG = "!#!#! internal only - from G2/G3 expansion !#!#!";
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -357,314 +353,6 @@ void GCodeProcessor::TimeProcessor::reset()
|
|||
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
|
||||
}
|
||||
|
||||
#if !ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends)
|
||||
{
|
||||
FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
|
||||
if (in.f == nullptr)
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
|
||||
|
||||
// temporary file to contain modified gcode
|
||||
std::string out_path = filename + ".postprocess";
|
||||
FilePtr out{ boost::nowide::fopen(out_path.c_str(), "wb") };
|
||||
if (out.f == nullptr) {
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
|
||||
}
|
||||
|
||||
auto time_in_minutes = [](float time_in_seconds) {
|
||||
assert(time_in_seconds >= 0.f);
|
||||
return int((time_in_seconds + 0.5f) / 60.0f);
|
||||
};
|
||||
|
||||
auto time_in_last_minute = [](float time_in_seconds) {
|
||||
assert(time_in_seconds <= 60.0f);
|
||||
return time_in_seconds / 60.0f;
|
||||
};
|
||||
|
||||
auto format_line_M73_main = [](const std::string& mask, int percent, int time) {
|
||||
char line_M73[64];
|
||||
sprintf(line_M73, mask.c_str(),
|
||||
std::to_string(percent).c_str(),
|
||||
std::to_string(time).c_str());
|
||||
return std::string(line_M73);
|
||||
};
|
||||
|
||||
auto format_line_M73_stop_int = [](const std::string& mask, int time) {
|
||||
char line_M73[64];
|
||||
sprintf(line_M73, mask.c_str(), std::to_string(time).c_str());
|
||||
return std::string(line_M73);
|
||||
};
|
||||
|
||||
auto format_time_float = [](float time) {
|
||||
return Slic3r::float_to_string_decimal_point(time, 2);
|
||||
};
|
||||
|
||||
auto format_line_M73_stop_float = [format_time_float](const std::string& mask, float time) {
|
||||
char line_M73[64];
|
||||
sprintf(line_M73, mask.c_str(), format_time_float(time).c_str());
|
||||
return std::string(line_M73);
|
||||
};
|
||||
|
||||
std::string gcode_line;
|
||||
size_t g1_lines_counter = 0;
|
||||
// keeps track of last exported pair <percent, remaining time>
|
||||
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_main;
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
last_exported_main[i] = { 0, time_in_minutes(machines[i].time) };
|
||||
}
|
||||
|
||||
// keeps track of last exported remaining time to next printer stop
|
||||
std::array<int, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_stop;
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
last_exported_stop[i] = time_in_minutes(machines[i].time);
|
||||
}
|
||||
|
||||
// buffer line to export only when greater than 64K to reduce writing calls
|
||||
std::string export_line;
|
||||
|
||||
// replace placeholder lines with the proper final value
|
||||
// gcode_line is in/out parameter, to reduce expensive memory allocation
|
||||
auto process_placeholders = [&](std::string& gcode_line) {
|
||||
unsigned int extra_lines_count = 0;
|
||||
|
||||
// remove trailing '\n'
|
||||
auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1);
|
||||
|
||||
std::string ret;
|
||||
if (line.length() > 1) {
|
||||
line = line.substr(1);
|
||||
if (export_remaining_time_enabled &&
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder) || line == reserved_tag(ETags::Last_Line_M73_Placeholder))) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = machines[i];
|
||||
if (machine.enabled) {
|
||||
// export pair <percent, remaining time>
|
||||
ret += format_line_M73_main(machine.line_m73_main_mask.c_str(),
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0);
|
||||
++extra_lines_count;
|
||||
|
||||
// export remaining time to next printer stop
|
||||
if (line == reserved_tag(ETags::First_Line_M73_Placeholder) && !machine.stop_times.empty()) {
|
||||
int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time);
|
||||
ret += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
++extra_lines_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = machines[i];
|
||||
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
|
||||
if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) {
|
||||
char buf[128];
|
||||
sprintf(buf, "; estimated printing time (%s mode) = %s\n",
|
||||
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
get_time_dhms(machine.time).c_str());
|
||||
ret += buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! ret.empty())
|
||||
// Not moving the move operator on purpose, so that the gcode_line allocation will grow and it will not be reallocated after handful of lines are processed.
|
||||
gcode_line = ret;
|
||||
return std::tuple(!ret.empty(), (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
|
||||
};
|
||||
|
||||
// check for temporary lines
|
||||
auto is_temporary_decoration = [](const std::string_view gcode_line) {
|
||||
// remove trailing '\n'
|
||||
assert(! gcode_line.empty());
|
||||
assert(gcode_line.back() == '\n');
|
||||
|
||||
// return true for decorations which are used in processing the gcode but that should not be exported into the final gcode
|
||||
// i.e.:
|
||||
// bool ret = gcode_line.substr(0, gcode_line.length() - 1) == ";" + Layer_Change_Tag;
|
||||
// ...
|
||||
// return ret;
|
||||
return false;
|
||||
};
|
||||
|
||||
// Iterators for the normal and silent cached time estimate entry recently processed, used by process_line_G1.
|
||||
auto g1_times_cache_it = Slic3r::reserve_vector<std::vector<TimeMachine::G1LinesCacheItem>::const_iterator>(machines.size());
|
||||
for (const auto& machine : machines)
|
||||
g1_times_cache_it.emplace_back(machine.g1_times_cache.begin());
|
||||
|
||||
// add lines M73 to exported gcode
|
||||
auto process_line_G1 = [
|
||||
// Lambdas, mostly for string formatting, all with an empty capture block.
|
||||
time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute,
|
||||
&self = std::as_const(*this),
|
||||
// Caches, to be modified
|
||||
&g1_times_cache_it, &last_exported_main, &last_exported_stop,
|
||||
// String output
|
||||
&export_line]
|
||||
(const size_t g1_lines_counter) {
|
||||
unsigned int exported_lines_count = 0;
|
||||
if (self.export_remaining_time_enabled) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = self.machines[i];
|
||||
if (machine.enabled) {
|
||||
// export pair <percent, remaining time>
|
||||
// Skip all machine.g1_times_cache below g1_lines_counter.
|
||||
auto& it = g1_times_cache_it[i];
|
||||
while (it != machine.g1_times_cache.end() && it->id < g1_lines_counter)
|
||||
++it;
|
||||
if (it != machine.g1_times_cache.end() && it->id == g1_lines_counter) {
|
||||
std::pair<int, int> to_export_main = { int(100.0f * it->elapsed_time / machine.time),
|
||||
time_in_minutes(machine.time - it->elapsed_time) };
|
||||
if (last_exported_main[i] != to_export_main) {
|
||||
export_line += format_line_M73_main(machine.line_m73_main_mask.c_str(),
|
||||
to_export_main.first, to_export_main.second);
|
||||
last_exported_main[i] = to_export_main;
|
||||
++exported_lines_count;
|
||||
}
|
||||
// export remaining time to next printer stop
|
||||
auto it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), it->elapsed_time,
|
||||
[](float value, const TimeMachine::StopTime& t) { return value < t.elapsed_time; });
|
||||
if (it_stop != machine.stop_times.end()) {
|
||||
int to_export_stop = time_in_minutes(it_stop->elapsed_time - it->elapsed_time);
|
||||
if (last_exported_stop[i] != to_export_stop) {
|
||||
if (to_export_stop > 0) {
|
||||
if (last_exported_stop[i] != to_export_stop) {
|
||||
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
++exported_lines_count;
|
||||
}
|
||||
}
|
||||
else {
|
||||
bool is_last = false;
|
||||
auto next_it = it + 1;
|
||||
is_last |= (next_it == machine.g1_times_cache.end());
|
||||
|
||||
if (next_it != machine.g1_times_cache.end()) {
|
||||
auto next_it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), next_it->elapsed_time,
|
||||
[](float value, const TimeMachine::StopTime& t) { return value < t.elapsed_time; });
|
||||
is_last |= (next_it_stop != it_stop);
|
||||
|
||||
std::string time_float_str = format_time_float(time_in_last_minute(it_stop->elapsed_time - it->elapsed_time));
|
||||
std::string next_time_float_str = format_time_float(time_in_last_minute(it_stop->elapsed_time - next_it->elapsed_time));
|
||||
is_last |= (string_to_double_decimal_point(time_float_str) > 0. && string_to_double_decimal_point(next_time_float_str) == 0.);
|
||||
}
|
||||
|
||||
if (is_last) {
|
||||
if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1))
|
||||
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
|
||||
else
|
||||
export_line += format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time));
|
||||
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
++exported_lines_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return exported_lines_count;
|
||||
};
|
||||
|
||||
// helper function to write to disk
|
||||
size_t out_file_pos = 0;
|
||||
lines_ends.clear();
|
||||
auto write_string = [&export_line, &out, &out_path, &out_file_pos, &lines_ends](const std::string& str) {
|
||||
fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f);
|
||||
if (ferror(out.f)) {
|
||||
out.close();
|
||||
boost::nowide::remove(out_path.c_str());
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
|
||||
}
|
||||
for (size_t i = 0; i < export_line.size(); ++ i)
|
||||
if (export_line[i] == '\n')
|
||||
lines_ends.emplace_back(out_file_pos + i + 1);
|
||||
out_file_pos += export_line.size();
|
||||
export_line.clear();
|
||||
};
|
||||
|
||||
unsigned int line_id = 0;
|
||||
std::vector<std::pair<unsigned int, unsigned int>> offsets;
|
||||
|
||||
{
|
||||
// Read the input stream 64kB at a time, extract lines and process them.
|
||||
std::vector<char> buffer(65536 * 10, 0);
|
||||
// Line buffer.
|
||||
assert(gcode_line.empty());
|
||||
for (;;) {
|
||||
size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f);
|
||||
if (::ferror(in.f))
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
|
||||
bool eof = cnt_read == 0;
|
||||
auto it = buffer.begin();
|
||||
auto it_bufend = buffer.begin() + cnt_read;
|
||||
while (it != it_bufend || (eof && ! gcode_line.empty())) {
|
||||
// Find end of line.
|
||||
bool eol = false;
|
||||
auto it_end = it;
|
||||
for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
|
||||
// End of line is indicated also if end of file was reached.
|
||||
eol |= eof && it_end == it_bufend;
|
||||
gcode_line.insert(gcode_line.end(), it, it_end);
|
||||
if (eol) {
|
||||
++line_id;
|
||||
|
||||
gcode_line += "\n";
|
||||
// replace placeholder lines
|
||||
auto [processed, lines_added_count] = process_placeholders(gcode_line);
|
||||
if (processed && lines_added_count > 0)
|
||||
offsets.push_back({ line_id, lines_added_count });
|
||||
if (! processed && ! is_temporary_decoration(gcode_line) && GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) {
|
||||
// remove temporary lines, add lines M73 where needed
|
||||
unsigned int extra_lines_count = process_line_G1(g1_lines_counter ++);
|
||||
if (extra_lines_count > 0)
|
||||
offsets.push_back({ line_id, extra_lines_count });
|
||||
}
|
||||
|
||||
export_line += gcode_line;
|
||||
if (export_line.length() > 65535)
|
||||
write_string(export_line);
|
||||
gcode_line.clear();
|
||||
}
|
||||
// Skip EOL.
|
||||
it = it_end;
|
||||
if (it != it_bufend && *it == '\r')
|
||||
++ it;
|
||||
if (it != it_bufend && *it == '\n')
|
||||
++ it;
|
||||
}
|
||||
if (eof)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!export_line.empty())
|
||||
write_string(export_line);
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
|
||||
// updates moves' gcode ids which have been modified by the insertion of the M73 lines
|
||||
unsigned int curr_offset_id = 0;
|
||||
unsigned int total_offset = 0;
|
||||
for (GCodeProcessorResult::MoveVertex& move : moves) {
|
||||
while (curr_offset_id < static_cast<unsigned int>(offsets.size()) && offsets[curr_offset_id].first <= move.gcode_id) {
|
||||
total_offset += offsets[curr_offset_id].second;
|
||||
++curr_offset_id;
|
||||
}
|
||||
move.gcode_id += total_offset;
|
||||
}
|
||||
|
||||
if (rename_file(out_path, filename))
|
||||
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + filename + '\n' +
|
||||
"Is " + out_path + " locked?" + '\n');
|
||||
}
|
||||
#endif // !ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
|
||||
void GCodeProcessor::UsedFilaments::reset()
|
||||
{
|
||||
color_change_cache = 0.0;
|
||||
|
@ -755,9 +443,7 @@ void GCodeProcessorResult::reset() {
|
|||
extruder_colors = std::vector<std::string>();
|
||||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
filament_cost = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_COST);
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
|
||||
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
|
||||
time = 0;
|
||||
|
@ -774,9 +460,7 @@ void GCodeProcessorResult::reset() {
|
|||
extruder_colors = std::vector<std::string>();
|
||||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
filament_cost = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_COST);
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
|
||||
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
|
||||
}
|
||||
|
@ -873,9 +557,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
m_extruder_colors.resize(extruders_count);
|
||||
m_result.filament_diameters.resize(extruders_count);
|
||||
m_result.filament_densities.resize(extruders_count);
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
m_result.filament_cost.resize(extruders_count);
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
m_extruder_temps.resize(extruders_count);
|
||||
|
||||
for (size_t i = 0; i < extruders_count; ++ i) {
|
||||
|
@ -883,9 +565,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
m_extruder_colors[i] = static_cast<unsigned char>(i);
|
||||
m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.get_at(i));
|
||||
m_result.filament_densities[i] = static_cast<float>(config.filament_density.get_at(i));
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
m_result.filament_cost[i] = static_cast<float>(config.filament_cost.get_at(i));
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
}
|
||||
|
||||
if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfRepRapFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) {
|
||||
|
@ -1006,7 +686,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
const ConfigOptionFloats* filament_cost = config.option<ConfigOptionFloats>("filament_cost");
|
||||
if (filament_cost != nullptr) {
|
||||
m_result.filament_cost.clear();
|
||||
|
@ -1021,7 +700,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
m_result.filament_cost.emplace_back(DEFAULT_FILAMENT_COST);
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
|
||||
const ConfigOptionPoints* extruder_offset = config.option<ConfigOptionPoints>("extruder_offset");
|
||||
if (extruder_offset != nullptr) {
|
||||
|
@ -1438,11 +1116,7 @@ void GCodeProcessor::finalize(bool perform_post_process)
|
|||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
if (perform_post_process)
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
post_process();
|
||||
#else
|
||||
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends);
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start_time).count();
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
@ -1688,12 +1362,10 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||
} else if (comment.find("filamentDensities") != comment.npos) {
|
||||
m_result.filament_densities.clear();
|
||||
extract_floats(comment, "filamentDensities", m_result.filament_densities);
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
}
|
||||
else if (comment.find("filamentPricesPerKg") != comment.npos) {
|
||||
m_result.filament_cost.clear();
|
||||
extract_floats(comment, "filamentPricesPerKg", m_result.filament_cost);
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
} else if (comment.find("extruderDiameter") != comment.npos) {
|
||||
std::vector<float> extruder_diameters;
|
||||
extract_floats(comment, "extruderDiameter", extruder_diameters);
|
||||
|
@ -1709,14 +1381,9 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||
}
|
||||
});
|
||||
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
if (m_result.extruders_count == 0)
|
||||
m_result.extruders_count = std::max<size_t>(1, std::min(m_result.filament_diameters.size(),
|
||||
std::min(m_result.filament_densities.size(), m_result.filament_cost.size())));
|
||||
#else
|
||||
if (m_result.extruders_count == 0)
|
||||
m_result.extruders_count = std::max<size_t>(1, std::min(m_result.filament_diameters.size(), m_result.filament_densities.size()));
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
|
||||
if (bed_size.is_defined()) {
|
||||
m_result.bed_shape = {
|
||||
|
@ -1749,10 +1416,8 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool
|
|||
switch (cmd[1]) {
|
||||
case '0': { process_G0(line); break; } // Move
|
||||
case '1': { process_G1(line); break; } // Move
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
case '2': { process_G2_G3(line, true); break; } // CW Arc Move
|
||||
case '3': { process_G2_G3(line, false); break; } // CCW Arc Move
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
@ -2668,23 +2333,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
const float filament_diameter = (static_cast<size_t>(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back();
|
||||
const float filament_radius = 0.5f * filament_diameter;
|
||||
const float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
|
||||
#if !ENABLE_PROCESS_G2_G3_LINES
|
||||
auto absolute_position = [this, area_filament_cross_section](Axis axis, const GCodeReader::GCodeLine& lineG1) {
|
||||
bool is_relative = (m_global_positioning_type == EPositioningType::Relative);
|
||||
if (axis == E)
|
||||
is_relative |= (m_e_local_positioning_type == EPositioningType::Relative);
|
||||
|
||||
if (lineG1.has(Slic3r::Axis(axis))) {
|
||||
float lengthsScaleFactor = (m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
|
||||
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
|
||||
if (axis == E && m_use_volumetric_e)
|
||||
ret /= area_filament_cross_section;
|
||||
return is_relative ? m_start_position[axis] + ret : m_origin[axis] + ret;
|
||||
}
|
||||
else
|
||||
return m_start_position[axis];
|
||||
};
|
||||
#endif // !ENABLE_PROCESS_G2_G3_LINES
|
||||
|
||||
auto move_type = [this](const AxisCoords& delta_pos) {
|
||||
EMoveType type = EMoveType::Noop;
|
||||
|
@ -2712,11 +2360,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
|
||||
// updates axes positions from line
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
m_end_position[a] = extract_absolute_position_on_axis((Axis)a, line, double(area_filament_cross_section));
|
||||
#else
|
||||
m_end_position[a] = absolute_position((Axis)a, line);
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
}
|
||||
|
||||
// updates feedrate from line, if present
|
||||
|
@ -2749,7 +2393,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
m_mm3_per_mm_compare.update(area_toolpath_cross_section, m_extrusion_role);
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
if (m_forced_height > 0.0f)
|
||||
m_height = m_forced_height;
|
||||
else if (m_layer_id == 0)
|
||||
|
@ -2758,16 +2401,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
if (m_end_position[Z] > m_extruded_last_z + EPSILON && delta_pos[Z] == 0.0)
|
||||
m_height = m_end_position[Z] - m_extruded_last_z;
|
||||
}
|
||||
#else
|
||||
if (m_forced_height > 0.0f)
|
||||
m_height = m_forced_height;
|
||||
else if (m_layer_id == 0)
|
||||
m_height = (m_end_position[Z] <= double(m_first_layer_height)) ? m_end_position[Z] : m_first_layer_height;
|
||||
else {
|
||||
if (m_end_position[Z] > m_extruded_last_z + EPSILON)
|
||||
m_height = m_end_position[Z] - m_extruded_last_z;
|
||||
}
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
|
||||
if (m_height == 0.0f)
|
||||
m_height = DEFAULT_TOOLPATH_HEIGHT;
|
||||
|
@ -2775,9 +2408,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
if (m_end_position[Z] == 0.0f || (m_extrusion_role == erCustom && m_layer_id == 0))
|
||||
m_end_position[Z] = m_height;
|
||||
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
if (line.comment() != INTERNAL_G2G3_TAG)
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
m_extruded_last_z = m_end_position[Z];
|
||||
m_options_z_corrector.update(m_height);
|
||||
|
||||
|
@ -3010,14 +2641,9 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
}
|
||||
|
||||
// store move
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
store_move_vertex(type, line.comment() == INTERNAL_G2G3_TAG);
|
||||
#else
|
||||
store_move_vertex(type);
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
}
|
||||
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise)
|
||||
{
|
||||
if (!line.has('I') || !line.has('J'))
|
||||
|
@ -3219,7 +2845,6 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc
|
|||
process_gcode_line(line, false);
|
||||
});
|
||||
}
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
|
||||
void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
|
@ -3757,7 +3382,6 @@ void GCodeProcessor::process_T(const std::string_view command)
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
void GCodeProcessor::post_process()
|
||||
{
|
||||
FilePtr in{ boost::nowide::fopen(m_result.filename.c_str(), "rb") };
|
||||
|
@ -4110,13 +3734,8 @@ void GCodeProcessor::post_process()
|
|||
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + m_result.filename + '\n' +
|
||||
"Is " + out_path + " locked?" + '\n');
|
||||
}
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
void GCodeProcessor::store_move_vertex(EMoveType type, bool internal_only)
|
||||
#else
|
||||
void GCodeProcessor::store_move_vertex(EMoveType type)
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
{
|
||||
m_last_line_id = (type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ?
|
||||
m_line_id + 1 :
|
||||
|
@ -4136,12 +3755,8 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
|
|||
m_mm3_per_mm,
|
||||
m_fan_speed,
|
||||
m_extruder_temps[m_extruder_id],
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
static_cast<float>(m_result.moves.size()),
|
||||
internal_only
|
||||
#else
|
||||
static_cast<float>(m_result.moves.size())
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
});
|
||||
|
||||
// stores stop time placeholders for later use
|
||||
|
@ -4336,7 +3951,6 @@ void GCodeProcessor::update_estimated_times_stats()
|
|||
m_result.print_statistics.used_filaments_per_role = m_used_filaments.filaments_per_role;
|
||||
}
|
||||
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
double GCodeProcessor::extract_absolute_position_on_axis(Axis axis, const GCodeReader::GCodeLine& line, double area_filament_cross_section)
|
||||
{
|
||||
if (line.has(Slic3r::Axis(axis))) {
|
||||
|
@ -4353,7 +3967,6 @@ double GCodeProcessor::extract_absolute_position_on_axis(Axis axis, const GCodeR
|
|||
else
|
||||
return m_start_position[axis];
|
||||
}
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
||||
|
|
|
@ -63,9 +63,7 @@ namespace Slic3r {
|
|||
std::vector<double> volumes_per_color_change;
|
||||
std::map<size_t, double> volumes_per_extruder;
|
||||
std::map<ExtrusionRole, std::pair<double, double>> used_filaments_per_role;
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
std::map<size_t, double> cost_per_extruder;
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
|
||||
std::array<Mode, static_cast<size_t>(ETimeMode::Count)> modes;
|
||||
|
||||
|
@ -78,9 +76,7 @@ namespace Slic3r {
|
|||
volumes_per_color_change.clear();
|
||||
volumes_per_extruder.clear();
|
||||
used_filaments_per_role.clear();
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
cost_per_extruder.clear();
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -115,9 +111,7 @@ namespace Slic3r {
|
|||
float fan_speed{ 0.0f }; // percentage
|
||||
float temperature{ 0.0f }; // Celsius degrees
|
||||
float time{ 0.0f }; // s
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
bool internal_only{ false };
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
|
||||
float volumetric_rate() const { return feedrate * mm3_per_mm; }
|
||||
};
|
||||
|
@ -134,9 +128,7 @@ namespace Slic3r {
|
|||
std::vector<std::string> extruder_colors;
|
||||
std::vector<float> filament_diameters;
|
||||
std::vector<float> filament_densities;
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
std::vector<float> filament_cost;
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
|
||||
PrintEstimatedStatistics print_statistics;
|
||||
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
|
||||
|
@ -356,13 +348,7 @@ namespace Slic3r {
|
|||
|
||||
void reset();
|
||||
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
friend class GCodeProcessor;
|
||||
#else
|
||||
// post process the file with the given filename to add remaining time lines M73
|
||||
// and updates moves' gcode ids accordingly
|
||||
void post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends);
|
||||
#endif // !ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
};
|
||||
|
||||
struct UsedFilaments // filaments per ColorChange
|
||||
|
@ -661,10 +647,8 @@ namespace Slic3r {
|
|||
void process_G0(const GCodeReader::GCodeLine& line);
|
||||
void process_G1(const GCodeReader::GCodeLine& line);
|
||||
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
// Arc Move
|
||||
void process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise);
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
|
||||
// Retract or Set tool temperature
|
||||
void process_G10(const GCodeReader::GCodeLine& line);
|
||||
|
@ -766,18 +750,12 @@ namespace Slic3r {
|
|||
void process_T(const GCodeReader::GCodeLine& line);
|
||||
void process_T(const std::string_view command);
|
||||
|
||||
#if ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
// post process the file with the given filename to:
|
||||
// 1) add remaining time lines M73 and update moves' gcode ids accordingly
|
||||
// 2) update used filament data
|
||||
void post_process();
|
||||
#endif // ENABLE_USED_FILAMENT_POST_PROCESS
|
||||
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
void store_move_vertex(EMoveType type, bool internal_only = false);
|
||||
#else
|
||||
void store_move_vertex(EMoveType type);
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
|
||||
void set_extrusion_role(ExtrusionRole role);
|
||||
|
||||
|
@ -803,9 +781,7 @@ namespace Slic3r {
|
|||
|
||||
void update_estimated_times_stats();
|
||||
|
||||
#if ENABLE_PROCESS_G2_G3_LINES
|
||||
double extract_absolute_position_on_axis(Axis axis, const GCodeReader::GCodeLine& line, double area_filament_cross_section);
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
};
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
|
67
src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp
Normal file
67
src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../Layer.hpp"
|
||||
#include "../Polyline.hpp"
|
||||
|
||||
#include "RetractWhenCrossingPerimeters.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool RetractWhenCrossingPerimeters::travel_inside_internal_regions(const Layer &layer, const Polyline &travel)
|
||||
{
|
||||
if (m_layer != &layer) {
|
||||
// Update cache.
|
||||
m_layer = &layer;
|
||||
m_internal_islands.clear();
|
||||
m_aabbtree_internal_islands.clear();
|
||||
// Collect expolygons of internal slices.
|
||||
for (const LayerRegion *layerm : layer.regions())
|
||||
for (const Surface &surface : layerm->slices().surfaces)
|
||||
if (surface.is_internal())
|
||||
m_internal_islands.emplace_back(&surface.expolygon);
|
||||
// Calculate bounding boxes of internal slices.
|
||||
class BBoxWrapper {
|
||||
public:
|
||||
BBoxWrapper(const size_t idx, const BoundingBox &bbox) :
|
||||
m_idx(idx),
|
||||
// Inflate the bounding box a bit to account for numerical issues.
|
||||
m_bbox(bbox.min - Point(SCALED_EPSILON, SCALED_EPSILON), bbox.max + Point(SCALED_EPSILON, SCALED_EPSILON)) {}
|
||||
size_t idx() const { return m_idx; }
|
||||
const AABBTree::BoundingBox& bbox() const { return m_bbox; }
|
||||
Point centroid() const { return ((m_bbox.min().cast<int64_t>() + m_bbox.max().cast<int64_t>()) / 2).cast<int32_t>(); }
|
||||
private:
|
||||
size_t m_idx;
|
||||
AABBTree::BoundingBox m_bbox;
|
||||
};
|
||||
std::vector<BBoxWrapper> bboxes;
|
||||
bboxes.reserve(m_internal_islands.size());
|
||||
for (size_t i = 0; i < m_internal_islands.size(); ++ i)
|
||||
bboxes.emplace_back(i, get_extents(*m_internal_islands[i]));
|
||||
// Build AABB tree over bounding boxes of internal slices.
|
||||
m_aabbtree_internal_islands.build_modify_input(bboxes);
|
||||
}
|
||||
|
||||
BoundingBox bbox_travel = get_extents(travel);
|
||||
AABBTree::BoundingBox bbox_travel_eigen{ bbox_travel.min, bbox_travel.max };
|
||||
int result = -1;
|
||||
bbox_travel.offset(SCALED_EPSILON);
|
||||
AABBTreeIndirect::traverse(m_aabbtree_internal_islands,
|
||||
[&bbox_travel_eigen](const AABBTree::Node &node) {
|
||||
return bbox_travel_eigen.intersects(node.bbox);
|
||||
},
|
||||
[&travel, &bbox_travel, &result, &islands = m_internal_islands](const AABBTree::Node &node) {
|
||||
assert(node.is_leaf());
|
||||
assert(node.is_valid());
|
||||
Polygons clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*islands[node.idx], bbox_travel);
|
||||
if (diff_pl(travel, clipped).empty()) {
|
||||
// Travel path is completely inside an "internal" island. Don't retract.
|
||||
result = int(node.idx);
|
||||
// Stop traversal.
|
||||
return false;
|
||||
}
|
||||
// Continue traversal.
|
||||
return true;
|
||||
});
|
||||
return result != -1;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
32
src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp
Normal file
32
src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#ifndef slic3r_RetractWhenCrossingPerimeters_hpp_
|
||||
#define slic3r_RetractWhenCrossingPerimeters_hpp_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../AABBTreeIndirect.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Forward declarations.
|
||||
class ExPolygon;
|
||||
class Layer;
|
||||
class Polyline;
|
||||
|
||||
class RetractWhenCrossingPerimeters
|
||||
{
|
||||
public:
|
||||
bool travel_inside_internal_regions(const Layer &layer, const Polyline &travel);
|
||||
|
||||
private:
|
||||
// Last object layer visited, for which a cache of internal islands was created.
|
||||
const Layer *m_layer;
|
||||
// Internal islands only, referencing data owned by m_layer->regions()->surfaces().
|
||||
std::vector<const ExPolygon*> m_internal_islands;
|
||||
// Search structure over internal islands.
|
||||
using AABBTree = AABBTreeIndirect::Tree<2, coord_t>;
|
||||
AABBTree m_aabbtree_internal_islands;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_RetractWhenCrossingPerimeters_hpp_
|
|
@ -108,7 +108,7 @@ Circled circle_taubin_newton(const Vec2ds& input, size_t cycles)
|
|||
return out;
|
||||
}
|
||||
|
||||
Circled circle_ransac(const Vec2ds& input, size_t iterations)
|
||||
Circled circle_ransac(const Vec2ds& input, size_t iterations, double* min_error)
|
||||
{
|
||||
if (input.size() < 3)
|
||||
return Circled::make_invalid();
|
||||
|
@ -132,6 +132,8 @@ Circled circle_ransac(const Vec2ds& input, size_t iterations)
|
|||
circle_best = c;
|
||||
}
|
||||
}
|
||||
if (min_error)
|
||||
*min_error = err_min;
|
||||
return circle_best;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20
|
|||
Circled circle_taubin_newton(const Vec2ds& input, size_t cycles = 20);
|
||||
|
||||
// Find circle using RANSAC randomized algorithm.
|
||||
Circled circle_ransac(const Vec2ds& input, size_t iterations = 20);
|
||||
Circled circle_ransac(const Vec2ds& input, size_t iterations = 20, double* min_error = nullptr);
|
||||
|
||||
// Randomized algorithm by Emo Welzl, working with squared radii for efficiency. The returned circle radius is inflated by epsilon.
|
||||
template<typename Vector, typename Points>
|
||||
|
|
|
@ -8,13 +8,15 @@
|
|||
|
||||
#include <numeric>
|
||||
|
||||
#define DEBUG_EXTRACT_ALL_FEATURES_AT_ONCE 0
|
||||
|
||||
namespace Slic3r {
|
||||
namespace Measure {
|
||||
|
||||
|
||||
constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it
|
||||
|
||||
static std::pair<Vec3d, double> get_center_and_radius(const std::vector<Vec3d>& points, const Transform3d& trafo)
|
||||
static std::tuple<Vec3d, double, double> get_center_and_radius(const std::vector<Vec3d>& points, const Transform3d& trafo, const Transform3d& trafo_inv)
|
||||
{
|
||||
Vec2ds out;
|
||||
double z = 0.;
|
||||
|
@ -24,18 +26,17 @@ static std::pair<Vec3d, double> get_center_and_radius(const std::vector<Vec3d>&
|
|||
out.emplace_back(pt_transformed.x(), pt_transformed.y());
|
||||
}
|
||||
|
||||
auto circle = Geometry::circle_ransac(out, 20); // FIXME: iterations?
|
||||
const int iter = points.size() < 10 ? 2 :
|
||||
points.size() < 100 ? 4 :
|
||||
6;
|
||||
|
||||
return std::make_pair(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius);
|
||||
double error = std::numeric_limits<double>::max();
|
||||
auto circle = Geometry::circle_ransac(out, iter, &error);
|
||||
|
||||
return std::make_tuple(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius, error);
|
||||
}
|
||||
|
||||
static bool circle_fit_is_ok(const std::vector<Vec3d>& pts, const Vec3d& center, double radius)
|
||||
{
|
||||
for (const Vec3d& pt : pts)
|
||||
if (std::abs((pt - center).norm() - radius) > 0.05)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static std::array<Vec3d, 3> orthonormal_basis(const Vec3d& v)
|
||||
{
|
||||
|
@ -64,17 +65,18 @@ public:
|
|||
std::vector<SurfaceFeature> surface_features;
|
||||
Vec3d normal;
|
||||
float area;
|
||||
bool features_extracted = false;
|
||||
};
|
||||
|
||||
std::vector<SurfaceFeature> get_all_features() const;
|
||||
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point) const;
|
||||
std::vector<std::vector<int>> get_planes_triangle_indices() const;
|
||||
const std::vector<SurfaceFeature>& get_plane_features(unsigned int plane_id) const;
|
||||
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point);
|
||||
int get_num_of_planes() const;
|
||||
const std::vector<int>& get_plane_triangle_indices(int idx) const;
|
||||
const std::vector<SurfaceFeature>& get_plane_features(unsigned int plane_id);
|
||||
const TriangleMesh& get_mesh() const;
|
||||
|
||||
private:
|
||||
void update_planes();
|
||||
void extract_features();
|
||||
void extract_features(int plane_idx);
|
||||
|
||||
std::vector<PlaneData> m_planes;
|
||||
std::vector<size_t> m_face_to_plane;
|
||||
|
@ -90,7 +92,13 @@ MeasuringImpl::MeasuringImpl(const indexed_triangle_set& its)
|
|||
: m_mesh(its)
|
||||
{
|
||||
update_planes();
|
||||
extract_features();
|
||||
|
||||
// Extracting features will be done as needed.
|
||||
// To extract all planes at once, run the following:
|
||||
#if DEBUG_EXTRACT_ALL_FEATURES_AT_ONCE
|
||||
for (int i=0; i<int(m_planes.size()); ++i)
|
||||
extract_features(i);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -252,30 +260,33 @@ void MeasuringImpl::update_planes()
|
|||
|
||||
|
||||
|
||||
void MeasuringImpl::extract_features()
|
||||
void MeasuringImpl::extract_features(int plane_idx)
|
||||
{
|
||||
assert(! m_planes[plane_idx].features_extracted);
|
||||
|
||||
PlaneData& plane = m_planes[plane_idx];
|
||||
plane.surface_features.clear();
|
||||
const Vec3d& normal = plane.normal;
|
||||
|
||||
Eigen::Quaterniond q;
|
||||
q.setFromTwoVectors(plane.normal, Vec3d::UnitZ());
|
||||
Transform3d trafo = Transform3d::Identity();
|
||||
trafo.rotate(q);
|
||||
const Transform3d trafo_inv = trafo.inverse();
|
||||
|
||||
std::vector<double> angles; // placed in outer scope to prevent reallocations
|
||||
std::vector<double> lengths;
|
||||
|
||||
for (const std::vector<Vec3d>& border : plane.borders) {
|
||||
if (border.size() <= 1)
|
||||
continue;
|
||||
|
||||
for (int i=0; i<(int)m_planes.size(); ++i) {
|
||||
PlaneData& plane = m_planes[i];
|
||||
plane.surface_features.clear();
|
||||
const Vec3d& normal = plane.normal;
|
||||
bool done = false;
|
||||
|
||||
Eigen::Quaterniond q;
|
||||
q.setFromTwoVectors(plane.normal, Vec3d::UnitZ());
|
||||
Transform3d trafo = Transform3d::Identity();
|
||||
trafo.rotate(q);
|
||||
if (border.size() > 4) {
|
||||
const auto& [center, radius, err] = get_center_and_radius(border, trafo, trafo_inv);
|
||||
|
||||
for (const std::vector<Vec3d>& border : plane.borders) {
|
||||
if (border.size() <= 1)
|
||||
continue;
|
||||
|
||||
bool done = false;
|
||||
|
||||
if (const auto& [center, radius] = get_center_and_radius(border, trafo);
|
||||
(border.size()>4) && circle_fit_is_ok(border, center, radius)) {
|
||||
if (err < 0.05) {
|
||||
// The whole border is one circle. Just add it into the list of features
|
||||
// and we are done.
|
||||
|
||||
|
@ -298,200 +309,190 @@ void MeasuringImpl::extract_features()
|
|||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! done) {
|
||||
// In this case, the border is not a circle and may contain circular
|
||||
// segments. Try to find them and then add all remaining edges as edges.
|
||||
if (! done) {
|
||||
// In this case, the border is not a circle and may contain circular
|
||||
// segments. Try to find them and then add all remaining edges as edges.
|
||||
|
||||
auto are_angles_same = [](double a, double b) { return Slic3r::is_approx(a,b,0.01); };
|
||||
auto are_lengths_same = [](double a, double b) { return Slic3r::is_approx(a,b,0.01); };
|
||||
auto are_angles_same = [](double a, double b) { return Slic3r::is_approx(a,b,0.01); };
|
||||
auto are_lengths_same = [](double a, double b) { return Slic3r::is_approx(a,b,0.01); };
|
||||
|
||||
|
||||
// Given an idx into border, return the index that is idx+offset position,
|
||||
// while taking into account the need for wrap-around and the fact that
|
||||
// the first and last point are the same.
|
||||
auto offset_to_index = [border_size = int(border.size())](int idx, int offset) -> int {
|
||||
assert(std::abs(offset) < border_size);
|
||||
int out = idx+offset;
|
||||
if (out >= border_size)
|
||||
out = out - border_size;
|
||||
else if (out < 0)
|
||||
out = border_size + out;
|
||||
// Given an idx into border, return the index that is idx+offset position,
|
||||
// while taking into account the need for wrap-around and the fact that
|
||||
// the first and last point are the same.
|
||||
auto offset_to_index = [border_size = int(border.size())](int idx, int offset) -> int {
|
||||
assert(std::abs(offset) < border_size);
|
||||
int out = idx+offset;
|
||||
if (out >= border_size)
|
||||
out = out - border_size;
|
||||
else if (out < 0)
|
||||
out = border_size + out;
|
||||
|
||||
return out;
|
||||
};
|
||||
return out;
|
||||
};
|
||||
|
||||
// First calculate angles at all the vertices.
|
||||
angles.clear();
|
||||
lengths.clear();
|
||||
int first_different_angle_idx = 0;
|
||||
for (int i=0; i<int(border.size()); ++i) {
|
||||
const Vec3d& v2 = border[i] - (i == 0 ? border[border.size()-1] : border[i-1]);
|
||||
const Vec3d& v1 = (i == int(border.size()-1) ? border[0] : border[i+1]) - border[i];
|
||||
double angle = atan2(-normal.dot(v1.cross(v2)), -v1.dot(v2)) + M_PI;
|
||||
if (angle > M_PI)
|
||||
angle = 2*M_PI - angle;
|
||||
// First calculate angles at all the vertices.
|
||||
angles.clear();
|
||||
lengths.clear();
|
||||
int first_different_angle_idx = 0;
|
||||
for (int i=0; i<int(border.size()); ++i) {
|
||||
const Vec3d& v2 = border[i] - (i == 0 ? border[border.size()-1] : border[i-1]);
|
||||
const Vec3d& v1 = (i == int(border.size()-1) ? border[0] : border[i+1]) - border[i];
|
||||
double angle = atan2(-normal.dot(v1.cross(v2)), -v1.dot(v2)) + M_PI;
|
||||
if (angle > M_PI)
|
||||
angle = 2*M_PI - angle;
|
||||
|
||||
angles.push_back(angle);
|
||||
lengths.push_back(v2.norm());
|
||||
if (first_different_angle_idx == 0 && angles.size() > 1) {
|
||||
if (! are_angles_same(angles.back(), angles[angles.size()-2]))
|
||||
first_different_angle_idx = angles.size()-1;
|
||||
}
|
||||
angles.push_back(angle);
|
||||
lengths.push_back(v2.norm());
|
||||
if (first_different_angle_idx == 0 && angles.size() > 1) {
|
||||
if (! are_angles_same(angles.back(), angles[angles.size()-2]))
|
||||
first_different_angle_idx = angles.size()-1;
|
||||
}
|
||||
assert(border.size() == angles.size());
|
||||
assert(border.size() == lengths.size());
|
||||
}
|
||||
assert(border.size() == angles.size());
|
||||
assert(border.size() == lengths.size());
|
||||
|
||||
// First go around the border and pick what might be circular segments.
|
||||
// Save pair of indices to where such potential segments start and end.
|
||||
// Also remember the length of these segments.
|
||||
int start_idx = -1;
|
||||
bool circle = false;
|
||||
bool first_iter = true;
|
||||
std::vector<SurfaceFeature> circles;
|
||||
std::vector<SurfaceFeature> edges;
|
||||
std::vector<std::pair<int, int>> circles_idxs;
|
||||
//std::vector<double> circles_lengths;
|
||||
std::vector<Vec3d> single_circle; // could be in loop-scope, but reallocations
|
||||
double single_circle_length = 0.;
|
||||
int first_pt_idx = offset_to_index(first_different_angle_idx, 1);
|
||||
int i = first_pt_idx;
|
||||
while (i != first_pt_idx || first_iter) {
|
||||
if (are_angles_same(angles[i], angles[offset_to_index(i,-1)])
|
||||
&& i != offset_to_index(first_pt_idx, -1) // not the last point
|
||||
&& i != start_idx ) {
|
||||
// circle
|
||||
if (! circle) {
|
||||
circle = true;
|
||||
single_circle.clear();
|
||||
single_circle_length = 0.;
|
||||
start_idx = offset_to_index(i, -2);
|
||||
single_circle = { border[start_idx], border[offset_to_index(start_idx,1)] };
|
||||
single_circle_length += lengths[offset_to_index(i, -1)];
|
||||
}
|
||||
// First go around the border and pick what might be circular segments.
|
||||
// Save pair of indices to where such potential segments start and end.
|
||||
// Also remember the length of these segments.
|
||||
int start_idx = -1;
|
||||
bool circle = false;
|
||||
bool first_iter = true;
|
||||
std::vector<SurfaceFeature> circles;
|
||||
std::vector<SurfaceFeature> edges;
|
||||
std::vector<std::pair<int, int>> circles_idxs;
|
||||
//std::vector<double> circles_lengths;
|
||||
std::vector<Vec3d> single_circle; // could be in loop-scope, but reallocations
|
||||
double single_circle_length = 0.;
|
||||
int first_pt_idx = offset_to_index(first_different_angle_idx, 1);
|
||||
int i = first_pt_idx;
|
||||
while (i != first_pt_idx || first_iter) {
|
||||
if (are_angles_same(angles[i], angles[offset_to_index(i,-1)])
|
||||
&& i != offset_to_index(first_pt_idx, -1) // not the last point
|
||||
&& i != start_idx ) {
|
||||
// circle
|
||||
if (! circle) {
|
||||
circle = true;
|
||||
single_circle.clear();
|
||||
single_circle_length = 0.;
|
||||
start_idx = offset_to_index(i, -2);
|
||||
single_circle = { border[start_idx], border[offset_to_index(start_idx,1)] };
|
||||
single_circle_length += lengths[offset_to_index(i, -1)];
|
||||
}
|
||||
single_circle.emplace_back(border[i]);
|
||||
single_circle_length += lengths[i];
|
||||
} else {
|
||||
if (circle && single_circle.size() >= 5) { // Less than 5 vertices? Not a circle.
|
||||
single_circle.emplace_back(border[i]);
|
||||
single_circle_length += lengths[i];
|
||||
} else {
|
||||
if (circle && single_circle.size() >= 5) { // Less than 5 vertices? Not a circle.
|
||||
single_circle.emplace_back(border[i]);
|
||||
single_circle_length += lengths[i];
|
||||
|
||||
bool accept_circle = true;
|
||||
{
|
||||
// Check that lengths of internal (!!!) edges match.
|
||||
int j = offset_to_index(start_idx, 3);
|
||||
while (j != i) {
|
||||
if (! are_lengths_same(lengths[offset_to_index(j,-1)], lengths[j])) {
|
||||
accept_circle = false;
|
||||
break;
|
||||
}
|
||||
j = offset_to_index(j, 1);
|
||||
bool accept_circle = true;
|
||||
{
|
||||
// Check that lengths of internal (!!!) edges match.
|
||||
int j = offset_to_index(start_idx, 3);
|
||||
while (j != i) {
|
||||
if (! are_lengths_same(lengths[offset_to_index(j,-1)], lengths[j])) {
|
||||
accept_circle = false;
|
||||
break;
|
||||
}
|
||||
j = offset_to_index(j, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (accept_circle) {
|
||||
const auto& [center, radius, err] = get_center_and_radius(single_circle, trafo, trafo_inv);
|
||||
|
||||
// Check that the fit went well. The tolerance is high, only to
|
||||
// reject complete failures.
|
||||
accept_circle &= err < 0.05;
|
||||
|
||||
// If the segment subtends less than 90 degrees, throw it away.
|
||||
accept_circle &= single_circle_length / radius > 0.9*M_PI/2.;
|
||||
|
||||
if (accept_circle) {
|
||||
const auto& [center, radius] = get_center_and_radius(single_circle, trafo);
|
||||
|
||||
// Check that the fit went well. The tolerance is high, only to
|
||||
// reject complete failures.
|
||||
accept_circle &= circle_fit_is_ok(single_circle, center, radius);
|
||||
|
||||
// If the segment subtends less than 90 degrees, throw it away.
|
||||
accept_circle &= single_circle_length / radius > 0.9*M_PI/2.;
|
||||
|
||||
if (accept_circle) {
|
||||
// Add the circle and remember indices into borders.
|
||||
circles_idxs.emplace_back(start_idx, i);
|
||||
circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::nullopt, radius));
|
||||
}
|
||||
// Add the circle and remember indices into borders.
|
||||
circles_idxs.emplace_back(start_idx, i);
|
||||
circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::nullopt, radius));
|
||||
}
|
||||
}
|
||||
circle = false;
|
||||
}
|
||||
// Take care of the wrap around.
|
||||
first_iter = false;
|
||||
circle = false;
|
||||
}
|
||||
// Take care of the wrap around.
|
||||
first_iter = false;
|
||||
i = offset_to_index(i, 1);
|
||||
}
|
||||
|
||||
// We have the circles. Now go around again and pick edges, while jumping over circles.
|
||||
if (circles_idxs.empty()) {
|
||||
// Just add all edges.
|
||||
for (int i=1; i<int(border.size()); ++i)
|
||||
edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[i-1], border[i]));
|
||||
edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[0], border[border.size()-1]));
|
||||
} else if (circles_idxs.size() > 1 || circles_idxs.front().first != circles_idxs.front().second) {
|
||||
// There is at least one circular segment. Start at its end and add edges until the start of the next one.
|
||||
int i = circles_idxs.front().second;
|
||||
int circle_idx = 1;
|
||||
while (true) {
|
||||
i = offset_to_index(i, 1);
|
||||
}
|
||||
|
||||
// We have the circles. Now go around again and pick edges, while jumping over circles.
|
||||
if (circles_idxs.empty()) {
|
||||
// Just add all edges.
|
||||
for (int i=1; i<int(border.size()); ++i)
|
||||
edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[i-1], border[i]));
|
||||
edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[0], border[border.size()-1]));
|
||||
} else if (circles_idxs.size() > 1 || circles_idxs.front().first != circles_idxs.front().second) {
|
||||
// There is at least one circular segment. Start at its end and add edges until the start of the next one.
|
||||
int i = circles_idxs.front().second;
|
||||
int circle_idx = 1;
|
||||
while (true) {
|
||||
i = offset_to_index(i, 1);
|
||||
edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[offset_to_index(i,-1)], border[i]));
|
||||
if (circle_idx < int(circles_idxs.size()) && i == circles_idxs[circle_idx].first) {
|
||||
i = circles_idxs[circle_idx].second;
|
||||
++circle_idx;
|
||||
}
|
||||
if (i == circles_idxs.front().first)
|
||||
break;
|
||||
edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[offset_to_index(i,-1)], border[i]));
|
||||
if (circle_idx < int(circles_idxs.size()) && i == circles_idxs[circle_idx].first) {
|
||||
i = circles_idxs[circle_idx].second;
|
||||
++circle_idx;
|
||||
}
|
||||
if (i == circles_idxs.front().first)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge adjacent edges where needed.
|
||||
assert(std::all_of(edges.begin(), edges.end(),
|
||||
[](const SurfaceFeature& f) { return f.get_type() == SurfaceFeatureType::Edge; }));
|
||||
for (int i=edges.size()-1; i>=0; --i) {
|
||||
const auto& [first_start, first_end] = edges[i==0 ? edges.size()-1 : i-1].get_edge();
|
||||
const auto& [second_start, second_end] = edges[i].get_edge();
|
||||
// Merge adjacent edges where needed.
|
||||
assert(std::all_of(edges.begin(), edges.end(),
|
||||
[](const SurfaceFeature& f) { return f.get_type() == SurfaceFeatureType::Edge; }));
|
||||
for (int i=edges.size()-1; i>=0; --i) {
|
||||
const auto& [first_start, first_end] = edges[i==0 ? edges.size()-1 : i-1].get_edge();
|
||||
const auto& [second_start, second_end] = edges[i].get_edge();
|
||||
|
||||
if (Slic3r::is_approx(first_end, second_start)
|
||||
&& Slic3r::is_approx((first_end-first_start).normalized().dot((second_end-second_start).normalized()), 1.)) {
|
||||
// The edges have the same direction and share a point. Merge them.
|
||||
edges[i==0 ? edges.size()-1 : i-1] = SurfaceFeature(SurfaceFeatureType::Edge, first_start, second_end);
|
||||
edges.erase(edges.begin() + i);
|
||||
}
|
||||
if (Slic3r::is_approx(first_end, second_start)
|
||||
&& Slic3r::is_approx((first_end-first_start).normalized().dot((second_end-second_start).normalized()), 1.)) {
|
||||
// The edges have the same direction and share a point. Merge them.
|
||||
edges[i==0 ? edges.size()-1 : i-1] = SurfaceFeature(SurfaceFeatureType::Edge, first_start, second_end);
|
||||
edges.erase(edges.begin() + i);
|
||||
}
|
||||
|
||||
// Now move the circles and edges into the feature list for the plane.
|
||||
assert(std::all_of(circles.begin(), circles.end(), [](const SurfaceFeature& f) {
|
||||
return f.get_type() == SurfaceFeatureType::Circle;
|
||||
}));
|
||||
assert(std::all_of(edges.begin(), edges.end(), [](const SurfaceFeature& f) {
|
||||
return f.get_type() == SurfaceFeatureType::Edge;
|
||||
}));
|
||||
plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(circles.begin()),
|
||||
std::make_move_iterator(circles.end()));
|
||||
plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(edges.begin()),
|
||||
std::make_move_iterator(edges.end()));
|
||||
}
|
||||
}
|
||||
|
||||
// The last surface feature is the plane itself.
|
||||
Vec3d cog = Vec3d::Zero();
|
||||
size_t counter = 0;
|
||||
for (const std::vector<Vec3d>& b : plane.borders) {
|
||||
for (size_t i = 1; i < b.size(); ++i) {
|
||||
cog += b[i];
|
||||
++counter;
|
||||
}
|
||||
// Now move the circles and edges into the feature list for the plane.
|
||||
assert(std::all_of(circles.begin(), circles.end(), [](const SurfaceFeature& f) {
|
||||
return f.get_type() == SurfaceFeatureType::Circle;
|
||||
}));
|
||||
assert(std::all_of(edges.begin(), edges.end(), [](const SurfaceFeature& f) {
|
||||
return f.get_type() == SurfaceFeatureType::Edge;
|
||||
}));
|
||||
plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(circles.begin()),
|
||||
std::make_move_iterator(circles.end()));
|
||||
plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(edges.begin()),
|
||||
std::make_move_iterator(edges.end()));
|
||||
}
|
||||
cog /= double(counter);
|
||||
plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane,
|
||||
plane.normal, cog, std::optional<Vec3d>(), i + 0.0001));
|
||||
|
||||
plane.borders.clear();
|
||||
plane.borders.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
// The last surface feature is the plane itself.
|
||||
Vec3d cog = Vec3d::Zero();
|
||||
size_t counter = 0;
|
||||
for (const std::vector<Vec3d>& b : plane.borders) {
|
||||
for (size_t i = 1; i < b.size(); ++i) {
|
||||
cog += b[i];
|
||||
++counter;
|
||||
}
|
||||
}
|
||||
cog /= double(counter);
|
||||
plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane,
|
||||
plane.normal, cog, std::optional<Vec3d>(), plane_idx + 0.0001));
|
||||
|
||||
plane.borders.clear();
|
||||
plane.borders.shrink_to_fit();
|
||||
|
||||
std::vector<SurfaceFeature> MeasuringImpl::get_all_features() const
|
||||
{
|
||||
std::vector<SurfaceFeature> features;
|
||||
//PlaneData& plane = m_planes[0];
|
||||
for (const PlaneData& plane : m_planes)
|
||||
for (const SurfaceFeature& feature : plane.surface_features)
|
||||
features.emplace_back(feature);
|
||||
return features;
|
||||
plane.features_extracted = true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -499,12 +500,17 @@ std::vector<SurfaceFeature> MeasuringImpl::get_all_features() const
|
|||
|
||||
|
||||
|
||||
std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) const
|
||||
|
||||
|
||||
std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point)
|
||||
{
|
||||
if (face_idx >= m_face_to_plane.size())
|
||||
return std::optional<SurfaceFeature>();
|
||||
|
||||
const PlaneData& plane = m_planes[m_face_to_plane[face_idx]];
|
||||
|
||||
if (! plane.features_extracted)
|
||||
extract_features(m_face_to_plane[face_idx]);
|
||||
|
||||
size_t closest_feature_idx = size_t(-1);
|
||||
double min_dist = std::numeric_limits<double>::max();
|
||||
|
@ -554,17 +560,24 @@ std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const
|
|||
|
||||
|
||||
|
||||
std::vector<std::vector<int>> MeasuringImpl::get_planes_triangle_indices() const
|
||||
int MeasuringImpl::get_num_of_planes() const
|
||||
{
|
||||
std::vector<std::vector<int>> out;
|
||||
for (const PlaneData& plane : m_planes)
|
||||
out.emplace_back(plane.facets);
|
||||
return out;
|
||||
return (m_planes.size());
|
||||
}
|
||||
|
||||
const std::vector<SurfaceFeature>& MeasuringImpl::get_plane_features(unsigned int plane_id) const
|
||||
|
||||
|
||||
const std::vector<int>& MeasuringImpl::get_plane_triangle_indices(int idx) const
|
||||
{
|
||||
assert(idx >= 0 && idx < int(m_planes.size()));
|
||||
return m_planes[idx].facets;
|
||||
}
|
||||
|
||||
const std::vector<SurfaceFeature>& MeasuringImpl::get_plane_features(unsigned int plane_id)
|
||||
{
|
||||
assert(plane_id < m_planes.size());
|
||||
if (! m_planes[plane_id].features_extracted)
|
||||
extract_features(plane_id);
|
||||
return m_planes[plane_id].surface_features;
|
||||
}
|
||||
|
||||
|
@ -590,11 +603,6 @@ Measuring::Measuring(const indexed_triangle_set& its)
|
|||
Measuring::~Measuring() {}
|
||||
|
||||
|
||||
std::vector<SurfaceFeature> Measuring::get_all_features() const
|
||||
{
|
||||
return priv->get_all_features();
|
||||
}
|
||||
|
||||
|
||||
std::optional<SurfaceFeature> Measuring::get_feature(size_t face_idx, const Vec3d& point) const
|
||||
{
|
||||
|
@ -602,10 +610,15 @@ std::optional<SurfaceFeature> Measuring::get_feature(size_t face_idx, const Vec3
|
|||
}
|
||||
|
||||
|
||||
|
||||
std::vector<std::vector<int>> Measuring::get_planes_triangle_indices() const
|
||||
int Measuring::get_num_of_planes() const
|
||||
{
|
||||
return priv->get_planes_triangle_indices();
|
||||
return priv->get_num_of_planes();
|
||||
}
|
||||
|
||||
|
||||
const std::vector<int>& Measuring::get_plane_triangle_indices(int idx) const
|
||||
{
|
||||
return priv->get_plane_triangle_indices(idx);
|
||||
}
|
||||
|
||||
const std::vector<SurfaceFeature>& Measuring::get_plane_features(unsigned int plane_id) const
|
||||
|
|
|
@ -93,20 +93,18 @@ public:
|
|||
// Construct the measurement object on a given its.
|
||||
explicit Measuring(const indexed_triangle_set& its);
|
||||
~Measuring();
|
||||
|
||||
// Return a reference to a list of all features identified on the its.
|
||||
// Use only for debugging. Expensive, do not call often.
|
||||
std::vector<SurfaceFeature> get_all_features() const;
|
||||
|
||||
|
||||
// Given a face_idx where the mouse cursor points, return a feature that
|
||||
// should be highlighted (if any).
|
||||
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point) const;
|
||||
|
||||
// Returns a list of triangle indices for each identified plane. Each
|
||||
// Plane object contains an index into this vector. Expensive, do not
|
||||
// call too often.
|
||||
std::vector<std::vector<int>> get_planes_triangle_indices() const;
|
||||
|
||||
// Return total number of planes.
|
||||
int get_num_of_planes() const;
|
||||
|
||||
// Returns a list of triangle indices for given plane.
|
||||
const std::vector<int>& get_plane_triangle_indices(int idx) const;
|
||||
|
||||
// Returns the surface features of the plane with the given index
|
||||
const std::vector<SurfaceFeature>& get_plane_features(unsigned int plane_id) const;
|
||||
|
||||
|
|
|
@ -11,11 +11,6 @@
|
|||
|
||||
namespace Slic3r { namespace sla {
|
||||
|
||||
const RasterBase::TMirroring RasterBase::NoMirror = {false, false};
|
||||
const RasterBase::TMirroring RasterBase::MirrorX = {true, false};
|
||||
const RasterBase::TMirroring RasterBase::MirrorY = {false, true};
|
||||
const RasterBase::TMirroring RasterBase::MirrorXY = {true, true};
|
||||
|
||||
EncodedRaster PNGRasterEncoder::operator()(const void *ptr, size_t w, size_t h,
|
||||
size_t num_components)
|
||||
{
|
||||
|
|
|
@ -60,10 +60,10 @@ public:
|
|||
enum Orientation { roLandscape, roPortrait };
|
||||
|
||||
using TMirroring = std::array<bool, 2>;
|
||||
static const TMirroring NoMirror;
|
||||
static const TMirroring MirrorX;
|
||||
static const TMirroring MirrorY;
|
||||
static const TMirroring MirrorXY;
|
||||
static const constexpr TMirroring NoMirror = {false, false};
|
||||
static const constexpr TMirroring MirrorX = {true, false};
|
||||
static const constexpr TMirroring MirrorY = {false, true};
|
||||
static const constexpr TMirroring MirrorXY = {true, true};
|
||||
|
||||
struct Trafo {
|
||||
bool mirror_x = false, mirror_y = false, flipXY = false;
|
||||
|
|
|
@ -413,6 +413,8 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
|||
[&part_to_drill, &hollowed_mesh](const auto& node)
|
||||
{
|
||||
part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[node.idx]);
|
||||
// continue traversal
|
||||
return true;
|
||||
});
|
||||
|
||||
auto cgal_meshpart = MeshBoolean::cgal::triangle_mesh_to_cgal(
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#define ENABLE_MEASURE_GIZMO_DEBUG 0
|
||||
// Enable scene raycast picking debug window
|
||||
#define ENABLE_RAYCAST_PICKING_DEBUG 0
|
||||
// Shows an imgui dialog with GLModel statistics data
|
||||
#define ENABLE_GLMODEL_STATISTICS 0
|
||||
|
||||
|
||||
// Enable rendering of objects using environment map
|
||||
|
@ -43,26 +45,18 @@
|
|||
//====================
|
||||
#define ENABLE_2_6_0_ALPHA1 1
|
||||
|
||||
// Enable removal of legacy OpenGL calls
|
||||
#define ENABLE_LEGACY_OPENGL_REMOVAL (1 && ENABLE_2_6_0_ALPHA1)
|
||||
// Enable OpenGL ES
|
||||
#define ENABLE_OPENGL_ES (0 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||
#define ENABLE_OPENGL_ES 0
|
||||
// Enable OpenGL core profile context (tested against Mesa 20.1.8 on Windows)
|
||||
#define ENABLE_GL_CORE_PROFILE (1 && ENABLE_LEGACY_OPENGL_REMOVAL && !ENABLE_OPENGL_ES)
|
||||
#define ENABLE_GL_CORE_PROFILE (1 && !ENABLE_OPENGL_ES)
|
||||
// Enable OpenGL debug messages using debug context
|
||||
#define ENABLE_OPENGL_DEBUG_OPTION (1 && ENABLE_GL_CORE_PROFILE)
|
||||
// Shows an imgui dialog with GLModel statistics data
|
||||
#define ENABLE_GLMODEL_STATISTICS (0 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||
// Enable rework of Reload from disk command
|
||||
#define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_6_0_ALPHA1)
|
||||
// Enable editing volumes transformation in world coordinates and instances in local coordinates
|
||||
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_6_0_ALPHA1)
|
||||
// Enable alternative version of file_wildcards()
|
||||
#define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_6_0_ALPHA1)
|
||||
// Enable processing of gcode G2 and G3 lines
|
||||
#define ENABLE_PROCESS_G2_G3_LINES (1 && ENABLE_2_6_0_ALPHA1)
|
||||
// Enable fix of used filament data exported to gcode file
|
||||
#define ENABLE_USED_FILAMENT_POST_PROCESS (1 && ENABLE_2_6_0_ALPHA1)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
|
@ -738,6 +738,13 @@ void its_flip_triangles(indexed_triangle_set &its)
|
|||
std::swap(face(1), face(2));
|
||||
}
|
||||
|
||||
int its_num_degenerate_faces(const indexed_triangle_set &its)
|
||||
{
|
||||
return std::count_if(its.indices.begin(), its.indices.end(), [](auto &face) {
|
||||
return face(0) == face(1) || face(0) == face(2) || face(1) == face(2);
|
||||
});
|
||||
}
|
||||
|
||||
int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit)
|
||||
{
|
||||
auto it = std::remove_if(its.indices.begin(), its.indices.end(), [](auto &face) {
|
||||
|
|
|
@ -204,6 +204,8 @@ void its_flip_triangles(indexed_triangle_set &its);
|
|||
// or more than two faces share the same edge position!
|
||||
int its_merge_vertices(indexed_triangle_set &its, bool shrink_to_fit = true);
|
||||
|
||||
// Calculate number of degenerate faces. There should be no degenerate faces in a nice mesh.
|
||||
int its_num_degenerate_faces(const indexed_triangle_set &its);
|
||||
// Remove degenerate faces, return number of faces removed.
|
||||
int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit = true);
|
||||
|
||||
|
|
|
@ -242,7 +242,12 @@ static FacetSliceType slice_facet(
|
|||
std::swap(a, b);
|
||||
}
|
||||
IntersectionPoint &point = points[num_points];
|
||||
double t = (double(slice_z) - double(b->z())) / (double(a->z()) - double(b->z()));
|
||||
double t = (double(slice_z) - double(a->z())) / (double(b->z()) - double(a->z()));
|
||||
#if 0
|
||||
// If the intersection point falls into one of the end points, mark it with the end point identifier.
|
||||
// While this sounds like a good idea, it likely breaks the chaining by logical addresses of the intersection points
|
||||
// and the branch for 0 < t < 1 does not guarantee uniqness of the interection point anyways.
|
||||
// Thus this branch is only kept for reference and it is not used in production code.
|
||||
if (t <= 0.) {
|
||||
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
|
||||
point.x() = a->x();
|
||||
|
@ -258,11 +263,26 @@ static FacetSliceType slice_facet(
|
|||
point.point_id = b_id;
|
||||
}
|
||||
} else {
|
||||
point.x() = coord_t(floor(double(b->x()) + (double(a->x()) - double(b->x())) * t + 0.5));
|
||||
point.y() = coord_t(floor(double(b->y()) + (double(a->y()) - double(b->y())) * t + 0.5));
|
||||
point.x() = coord_t(floor(double(a->x()) + (double(b->x()) - double(a->x())) * t + 0.5));
|
||||
point.y() = coord_t(floor(double(a->y()) + (double(b->y()) - double(a->y())) * t + 0.5));
|
||||
point.edge_id = edge_id;
|
||||
++ num_points;
|
||||
}
|
||||
#else
|
||||
// Just clamp the intersection point to source triangle edge.
|
||||
if (t <= 0.) {
|
||||
point.x() = a->x();
|
||||
point.y() = a->y();
|
||||
} else if (t >= 1.) {
|
||||
point.x() = b->x();
|
||||
point.y() = b->y();
|
||||
} else {
|
||||
point.x() = coord_t(floor(double(a->x()) + (double(b->x()) - double(a->x())) * t + 0.5));
|
||||
point.y() = coord_t(floor(double(a->y()) + (double(b->y()) - double(a->y())) * t + 0.5));
|
||||
}
|
||||
point.edge_id = edge_id;
|
||||
++ num_points;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,6 +304,11 @@ static FacetSliceType slice_facet(
|
|||
assert(line_out.edge_a_id != -1 || line_out.edge_b_id != -1);
|
||||
// General slicing position, use the segment for both slicing and object cutting.
|
||||
#if 0
|
||||
// See the discussion on calculating the intersection point on a triangle edge.
|
||||
// Even if the intersection point is clamped to one of the end points of the triangle edge,
|
||||
// the intersection point is still marked as "on edge", not "on vertex". Such implementation
|
||||
// may produce degenerate triangles, but is topologically correct.
|
||||
// Therefore this block for solving snapping of an intersection edge to triangle vertices is not used.
|
||||
if (line_out.a_id != -1 && line_out.b_id != -1) {
|
||||
// Solving a degenerate case, where both the intersections snapped to an edge.
|
||||
// Correctly classify the face as below or above based on the position of the 3rd point.
|
||||
|
@ -2009,6 +2034,7 @@ static void triangulate_slice(
|
|||
(l.first.y() == r.first.y() && l.second < r.second))); });
|
||||
|
||||
// 2) Discover duplicate points on the slice. Remap duplicate vertices to a vertex with a lowest index.
|
||||
// Remove denegerate triangles, if they happen to be created by merging duplicate vertices.
|
||||
{
|
||||
std::vector<int> map_duplicate_vertex(int(its.vertices.size()) - num_original_vertices, -1);
|
||||
int i = 0;
|
||||
|
@ -2031,10 +2057,20 @@ static void triangulate_slice(
|
|||
i = j;
|
||||
}
|
||||
map_vertex_to_index.erase(map_vertex_to_index.begin() + k, map_vertex_to_index.end());
|
||||
for (stl_triangle_vertex_indices &f : its.indices)
|
||||
for (i = 0; i < 3; ++ i)
|
||||
if (f(i) >= num_original_vertices)
|
||||
f(i) = map_duplicate_vertex[f(i) - num_original_vertices];
|
||||
for (i = 0; i < int(its.indices.size());) {
|
||||
stl_triangle_vertex_indices &f = its.indices[i];
|
||||
// Remap the newly added face vertices.
|
||||
for (k = 0; k < 3; ++ k)
|
||||
if (f(k) >= num_original_vertices)
|
||||
f(k) = map_duplicate_vertex[f(k) - num_original_vertices];
|
||||
if (f(0) == f(1) || f(0) == f(2) || f(1) == f(2)) {
|
||||
// Remove degenerate face.
|
||||
f = its.indices.back();
|
||||
its.indices.pop_back();
|
||||
} else
|
||||
// Keep the face.
|
||||
++ i;
|
||||
}
|
||||
}
|
||||
|
||||
if (triangulate) {
|
||||
|
@ -2108,6 +2144,10 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
|
|||
if (upper == nullptr && lower == nullptr)
|
||||
return;
|
||||
|
||||
#ifndef NDEBUG
|
||||
const size_t had_degenerate_faces = its_num_degenerate_faces(mesh);
|
||||
#endif // NDEBUG
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "cut_mesh - slicing object";
|
||||
|
||||
if (upper) {
|
||||
|
@ -2251,8 +2291,27 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
|
|||
new_face(lower, iv0, iv0v1_lower, iv2v0_lower);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
char buf[2048];
|
||||
static int irun = 0;
|
||||
++irun;
|
||||
temp.indices.emplace_back(int(temp.vertices.size()), int(temp.vertices.size() + 1), int(temp.vertices.size() + 2));
|
||||
temp.vertices.emplace_back(vertices[0]);
|
||||
temp.vertices.emplace_back(vertices[1]);
|
||||
temp.vertices.emplace_back(vertices[2]);
|
||||
sprintf(buf, "D:\\temp\\test\\temp-%d.obj", irun);
|
||||
its_write_obj(temp, buf);
|
||||
sprintf(buf, "D:\\temp\\test\\upper-%d.obj", irun);
|
||||
its_write_obj(*upper, buf);
|
||||
sprintf(buf, "D:\\temp\\test\\lower-%d.obj", irun);
|
||||
its_write_obj(*lower, buf);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
assert(had_degenerate_faces || ! upper || its_num_degenerate_faces(*upper) == 0);
|
||||
assert(had_degenerate_faces || ! lower || its_num_degenerate_faces(*lower) == 0);
|
||||
|
||||
if (upper != nullptr) {
|
||||
triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_DOWN);
|
||||
#ifndef NDEBUG
|
||||
|
@ -2272,6 +2331,9 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
|
|||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
|
||||
assert(had_degenerate_faces || ! upper || its_num_degenerate_faces(*upper) == 0);
|
||||
assert(had_degenerate_faces || ! lower || its_num_degenerate_faces(*lower) == 0);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -130,6 +130,6 @@ void cut_mesh(
|
|||
indexed_triangle_set *lower,
|
||||
bool triangulate_caps = true);
|
||||
|
||||
}
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_TriangleMeshSlicer_hpp_
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue