mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-25 17:51:10 -06:00
Merge remote-tracking branch 'origin/master' into lh_avoid_crossing_perimeters
# Conflicts: # src/libslic3r/MotionPlanner.cpp # src/libslic3r/libslic3r.h
This commit is contained in:
commit
87879034f6
175 changed files with 34821 additions and 26174 deletions
|
|
@ -726,6 +726,36 @@ inline bool is_any_triangle_in_radius(
|
|||
return hit_point.allFinite();
|
||||
}
|
||||
|
||||
|
||||
// Traverse the tree and return the index of an entity whose bounding box
|
||||
// contains a given point. Returns size_t(-1) when the point is outside.
|
||||
template<typename TreeType, typename VectorType>
|
||||
size_t get_candidate_idx(const TreeType& tree, const VectorType& v)
|
||||
{
|
||||
if (tree.empty() || ! tree.node(0).bbox.contains(v))
|
||||
return size_t(-1);
|
||||
|
||||
size_t node_idx = 0;
|
||||
while (true) {
|
||||
decltype(tree.node(node_idx)) node = tree.node(node_idx);
|
||||
static_assert(std::is_reference<decltype(node)>::value,
|
||||
"Nodes shall be addressed by reference.");
|
||||
assert(node.is_valid());
|
||||
assert(node.bbox.contains(v));
|
||||
|
||||
if (! node.is_leaf()) {
|
||||
if (tree.left_child(node_idx).bbox.contains(v))
|
||||
node_idx = tree.left_child_idx(node_idx);
|
||||
else if (tree.right_child(node_idx).bbox.contains(v))
|
||||
node_idx = tree.right_child_idx(node_idx);
|
||||
else
|
||||
return size_t(-1);
|
||||
} else
|
||||
return node.idx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace AABBTreeIndirect
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,15 @@ void AppConfig::set_defaults()
|
|||
if (get("export_sources_full_pathnames").empty())
|
||||
set("export_sources_full_pathnames", "0");
|
||||
|
||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
#ifdef _WIN32
|
||||
if (get("associate_3mf").empty())
|
||||
set("associate_3mf", "0");
|
||||
if (get("associate_stl").empty())
|
||||
set("associate_stl", "0");
|
||||
#endif // _WIN32
|
||||
#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
|
||||
// remove old 'use_legacy_opengl' parameter from this config, if present
|
||||
if (!get("use_legacy_opengl").empty())
|
||||
erase("", "use_legacy_opengl");
|
||||
|
|
@ -108,7 +117,21 @@ void AppConfig::set_defaults()
|
|||
|
||||
if (get("use_inches").empty())
|
||||
set("use_inches", "0");
|
||||
|
||||
if (get("default_action_on_close_application").empty())
|
||||
set("default_action_on_close_application", "none"); // , "discard" or "save"
|
||||
|
||||
if (get("default_action_on_select_preset").empty())
|
||||
set("default_action_on_select_preset", "none"); // , "transfer", "discard" or "save"
|
||||
}
|
||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
else {
|
||||
#ifdef _WIN32
|
||||
if (get("associate_gcode").empty())
|
||||
set("associate_gcode", "0");
|
||||
#endif // _WIN32
|
||||
}
|
||||
#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
|
||||
if (get("seq_top_layer_only").empty())
|
||||
set("seq_top_layer_only", "1");
|
||||
|
|
@ -125,11 +148,12 @@ void AppConfig::set_defaults()
|
|||
if (get("show_splash_screen").empty())
|
||||
set("show_splash_screen", "1");
|
||||
|
||||
if (get("default_action_on_close_application").empty())
|
||||
set("default_action_on_close_application", "none"); // , "discard" or "save"
|
||||
|
||||
if (get("default_action_on_select_preset").empty())
|
||||
set("default_action_on_select_preset", "none"); // , "transfer", "discard" or "save"
|
||||
#if ENABLE_CTRL_M_ON_WINDOWS
|
||||
#ifdef _WIN32
|
||||
if (get("use_legacy_3DConnexion").empty())
|
||||
set("use_legacy_3DConnexion", "0");
|
||||
#endif // _WIN32
|
||||
#endif // ENABLE_CTRL_M_ON_WINDOWS
|
||||
|
||||
// Remove legacy window positions/sizes
|
||||
erase("", "main_frame_maximized");
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <libnest2d/optimizers/nlopt/subplex.hpp>
|
||||
#include <libnest2d/placers/nfpplacer.hpp>
|
||||
#include <libnest2d/selections/firstfit.hpp>
|
||||
#include <libnest2d/utils/rotcalipers.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <ClipperUtils.hpp>
|
||||
|
|
@ -83,7 +84,7 @@ const double BIG_ITEM_TRESHOLD = 0.02;
|
|||
// Fill in the placer algorithm configuration with values carefully chosen for
|
||||
// Slic3r.
|
||||
template<class PConf>
|
||||
void fill_config(PConf& pcfg) {
|
||||
void fill_config(PConf& pcfg, const ArrangeParams ¶ms) {
|
||||
|
||||
// Align the arranged pile into the center of the bin
|
||||
pcfg.alignment = PConf::Alignment::CENTER;
|
||||
|
|
@ -93,14 +94,17 @@ void fill_config(PConf& pcfg) {
|
|||
|
||||
// TODO cannot use rotations until multiple objects of same geometry can
|
||||
// handle different rotations.
|
||||
pcfg.rotations = { 0.0 };
|
||||
if (params.allow_rotations)
|
||||
pcfg.rotations = {0., PI / 2., PI, 3. * PI / 2. };
|
||||
else
|
||||
pcfg.rotations = {0.};
|
||||
|
||||
// The accuracy of optimization.
|
||||
// Goes from 0.0 to 1.0 and scales performance as well
|
||||
pcfg.accuracy = 0.65f;
|
||||
pcfg.accuracy = params.accuracy;
|
||||
|
||||
// Allow parallel execution.
|
||||
pcfg.parallel = true;
|
||||
pcfg.parallel = params.parallel;
|
||||
}
|
||||
|
||||
// Apply penalty to object function result. This is used only when alignment
|
||||
|
|
@ -277,10 +281,10 @@ protected:
|
|||
if (result.empty())
|
||||
score = 0.50 * dist + 0.50 * density;
|
||||
else
|
||||
score = R * 0.60 * dist +
|
||||
(1.0 - R) * 0.20 * density +
|
||||
0.20 * alignment_score;
|
||||
|
||||
// Let the density matter more when fewer objects remain
|
||||
score = 0.50 * dist + (1.0 - R) * 0.20 * density +
|
||||
0.30 * alignment_score;
|
||||
|
||||
break;
|
||||
}
|
||||
case LAST_BIG_ITEM: {
|
||||
|
|
@ -304,15 +308,15 @@ protected:
|
|||
|
||||
public:
|
||||
AutoArranger(const TBin & bin,
|
||||
Distance dist,
|
||||
const ArrangeParams ¶ms,
|
||||
std::function<void(unsigned)> progressind,
|
||||
std::function<bool(void)> stopcond)
|
||||
: m_pck(bin, dist)
|
||||
: m_pck(bin, params.min_obj_distance)
|
||||
, m_bin(bin)
|
||||
, m_bin_area(sl::area(bin))
|
||||
, m_norm(std::sqrt(m_bin_area))
|
||||
{
|
||||
fill_config(m_pconf);
|
||||
fill_config(m_pconf, params);
|
||||
|
||||
// Set up a callback that is called just before arranging starts
|
||||
// This functionality is provided by the Nester class (m_pack).
|
||||
|
|
@ -343,18 +347,31 @@ public:
|
|||
};
|
||||
|
||||
m_pconf.object_function = get_objfn();
|
||||
|
||||
auto on_packed = params.on_packed;
|
||||
|
||||
if (progressind) m_pck.progressIndicator(progressind);
|
||||
if (progressind || on_packed)
|
||||
m_pck.progressIndicator([this, progressind, on_packed](unsigned rem) {
|
||||
|
||||
if (progressind)
|
||||
progressind(rem);
|
||||
|
||||
if (on_packed) {
|
||||
int last_bed = m_pck.lastPackedBinId();
|
||||
if (last_bed >= 0) {
|
||||
Item &last_packed = m_pck.lastResult()[last_bed].back();
|
||||
ArrangePolygon ap;
|
||||
ap.bed_idx = last_packed.binId();
|
||||
ap.priority = last_packed.priority();
|
||||
on_packed(ap);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (stopcond) m_pck.stopCondition(stopcond);
|
||||
|
||||
m_pck.configure(m_pconf);
|
||||
}
|
||||
|
||||
AutoArranger(const TBin & bin,
|
||||
std::function<void(unsigned)> progressind,
|
||||
std::function<bool(void)> stopcond)
|
||||
: AutoArranger{bin, 0 /* no min distance */, progressind, stopcond}
|
||||
{}
|
||||
|
||||
template<class It> inline void operator()(It from, It to) {
|
||||
m_rtree.clear();
|
||||
|
|
@ -452,12 +469,18 @@ template<class Bin> void remove_large_items(std::vector<Item> &items, Bin &&bin)
|
|||
++it : it = items.erase(it);
|
||||
}
|
||||
|
||||
template<class S> Radians min_area_boundingbox_rotation(const S &sh)
|
||||
{
|
||||
return minAreaBoundingBox<S, TCompute<S>, boost::rational<LargeInt>>(sh)
|
||||
.angleToX();
|
||||
}
|
||||
|
||||
template<class BinT> // Arrange for arbitrary bin type
|
||||
void _arrange(
|
||||
std::vector<Item> & shapes,
|
||||
std::vector<Item> & excludes,
|
||||
const BinT & bin,
|
||||
const ArrangeParams & params,
|
||||
const ArrangeParams ¶ms,
|
||||
std::function<void(unsigned)> progressfn,
|
||||
std::function<bool()> stopfn)
|
||||
{
|
||||
|
|
@ -467,11 +490,10 @@ void _arrange(
|
|||
|
||||
auto corrected_bin = bin;
|
||||
sl::offset(corrected_bin, md);
|
||||
|
||||
AutoArranger<BinT> arranger{corrected_bin, progressfn, stopfn};
|
||||
|
||||
arranger.config().accuracy = params.accuracy;
|
||||
arranger.config().parallel = params.parallel;
|
||||
ArrangeParams mod_params = params;
|
||||
mod_params.min_obj_distance = 0;
|
||||
|
||||
AutoArranger<BinT> arranger{corrected_bin, mod_params, progressfn, stopfn};
|
||||
|
||||
auto infl = coord_t(std::ceil(params.min_obj_distance / 2.0));
|
||||
for (Item& itm : shapes) itm.inflate(infl);
|
||||
|
|
@ -487,6 +509,13 @@ void _arrange(
|
|||
for (auto &itm : shapes ) inp.emplace_back(itm);
|
||||
for (auto &itm : excludes) inp.emplace_back(itm);
|
||||
|
||||
// Use the minimum bounding box rotation as a starting point.
|
||||
// TODO: This only works for convex hull. If we ever switch to concave
|
||||
// polygon nesting, a convex hull needs to be calculated.
|
||||
if (params.allow_rotations)
|
||||
for (auto &itm : shapes)
|
||||
itm.rotation(min_area_boundingbox_rotation(itm.rawShape()));
|
||||
|
||||
arranger(inp.begin(), inp.end());
|
||||
for (Item &itm : inp) itm.inflate(-infl);
|
||||
}
|
||||
|
|
@ -556,28 +585,35 @@ static void process_arrangeable(const ArrangePolygon &arrpoly,
|
|||
outp.back().priority(arrpoly.priority);
|
||||
}
|
||||
|
||||
template<class Fn> auto call_with_bed(const Points &bed, Fn &&fn)
|
||||
{
|
||||
if (bed.empty())
|
||||
return fn(InfiniteBed{});
|
||||
else if (bed.size() == 1)
|
||||
return fn(InfiniteBed{bed.front()});
|
||||
else {
|
||||
auto bb = BoundingBox(bed);
|
||||
CircleBed circ = to_circle(bb.center(), bed);
|
||||
auto parea = poly_area(bed);
|
||||
|
||||
if ((1.0 - parea / area(bb)) < 1e-3)
|
||||
return fn(bb);
|
||||
else if (!std::isnan(circ.radius()))
|
||||
return fn(circ);
|
||||
else
|
||||
return fn(Polygon(bed));
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void arrange(ArrangePolygons & items,
|
||||
const ArrangePolygons &excludes,
|
||||
const Points & bed,
|
||||
const ArrangeParams & params)
|
||||
{
|
||||
if (bed.empty())
|
||||
arrange(items, excludes, InfiniteBed{}, params);
|
||||
else if (bed.size() == 1)
|
||||
arrange(items, excludes, InfiniteBed{bed.front()}, params);
|
||||
else {
|
||||
auto bb = BoundingBox(bed);
|
||||
CircleBed circ = to_circle(bb.center(), bed);
|
||||
auto parea = poly_area(bed);
|
||||
|
||||
if ((1.0 - parea / area(bb)) < 1e-3)
|
||||
arrange(items, excludes, bb, params);
|
||||
else if (!std::isnan(circ.radius()))
|
||||
arrange(items, excludes, circ, params);
|
||||
else
|
||||
arrange(items, excludes, Polygon(bed), params);
|
||||
}
|
||||
call_with_bed(bed, [&](const auto &bin) {
|
||||
arrange(items, excludes, bin, params);
|
||||
});
|
||||
}
|
||||
|
||||
template<class BedT>
|
||||
|
|
|
|||
|
|
@ -74,14 +74,18 @@ struct ArrangeParams {
|
|||
|
||||
/// The accuracy of optimization.
|
||||
/// Goes from 0.0 to 1.0 and scales performance as well
|
||||
float accuracy = 0.65f;
|
||||
float accuracy = 1.f;
|
||||
|
||||
/// Allow parallel execution.
|
||||
bool parallel = true;
|
||||
|
||||
bool allow_rotations = false;
|
||||
|
||||
/// Progress indicator callback called when an object gets packed.
|
||||
/// The unsigned argument is the number of items remaining to pack.
|
||||
std::function<void(unsigned)> progressind;
|
||||
|
||||
std::function<void(const ArrangePolygon &)> on_packed;
|
||||
|
||||
/// A predicate returning true if abort is needed.
|
||||
std::function<bool(void)> stopcondition;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ public:
|
|||
void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; }
|
||||
void translate(const Vec2d &v) { this->min += v; this->max += v; }
|
||||
void offset(coordf_t delta);
|
||||
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
bool contains(const PointClass &point) const {
|
||||
return point(0) >= this->min(0) && point(0) <= this->max(0)
|
||||
|
|
@ -91,6 +92,7 @@ public:
|
|||
void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; }
|
||||
void translate(const Vec3d &v) { this->min += v; this->max += v; }
|
||||
void offset(coordf_t delta);
|
||||
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
coordf_t max_size() const;
|
||||
|
||||
|
|
@ -159,6 +161,8 @@ public:
|
|||
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {}
|
||||
BoundingBox(const Points &points) : BoundingBoxBase<Point>(points) {}
|
||||
|
||||
BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; }
|
||||
|
||||
friend BoundingBox get_extents_rotated(const Points &points, double angle);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -58,10 +58,10 @@ add_library(libslic3r STATIC
|
|||
Fill/FillGyroid.hpp
|
||||
Fill/FillPlanePath.cpp
|
||||
Fill/FillPlanePath.hpp
|
||||
Fill/FillLine.cpp
|
||||
Fill/FillLine.hpp
|
||||
Fill/FillRectilinear.cpp
|
||||
Fill/FillRectilinear.hpp
|
||||
Fill/FillRectilinear2.cpp
|
||||
Fill/FillRectilinear2.hpp
|
||||
Flow.cpp
|
||||
Flow.hpp
|
||||
format.hpp
|
||||
|
|
@ -159,8 +159,8 @@ add_library(libslic3r STATIC
|
|||
PrintConfig.hpp
|
||||
PrintObject.cpp
|
||||
PrintRegion.cpp
|
||||
PNGRead.hpp
|
||||
PNGRead.cpp
|
||||
PNGReadWrite.hpp
|
||||
PNGReadWrite.cpp
|
||||
Semver.cpp
|
||||
ShortestPath.cpp
|
||||
ShortestPath.hpp
|
||||
|
|
|
|||
|
|
@ -1069,7 +1069,7 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::v
|
|||
ClipperLib::Paths holes;
|
||||
holes.reserve(expoly.holes.size());
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false));
|
||||
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false));
|
||||
#ifndef NDEBUG
|
||||
for (auto &c : holes)
|
||||
assert(ClipperLib::Area(c) > 0.);
|
||||
|
|
@ -1113,7 +1113,7 @@ for (const std::vector<float>& ds : deltas)
|
|||
ClipperLib::Paths holes;
|
||||
holes.reserve(expoly.holes.size());
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
|
||||
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
|
||||
#ifndef NDEBUG
|
||||
for (auto &c : holes)
|
||||
assert(ClipperLib::Area(c) > 0.);
|
||||
|
|
@ -1157,7 +1157,7 @@ for (const std::vector<float>& ds : deltas)
|
|||
ClipperLib::Paths holes;
|
||||
holes.reserve(expoly.holes.size());
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
|
||||
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
|
||||
#ifndef NDEBUG
|
||||
for (auto &c : holes)
|
||||
assert(ClipperLib::Area(c) > 0.);
|
||||
|
|
@ -1205,7 +1205,7 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s
|
|||
ClipperLib::Paths holes;
|
||||
holes.reserve(expoly.holes.size());
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false));
|
||||
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false));
|
||||
#ifndef NDEBUG
|
||||
for (auto &c : holes)
|
||||
assert(ClipperLib::Area(c) > 0.);
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@
|
|||
#include <float.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#if 0
|
||||
// #ifdef SLIC3R_GUI
|
||||
#include <wx/image.h>
|
||||
#endif /* SLIC3R_GUI */
|
||||
#include <png.h>
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "EdgeGrid.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "SVG.hpp"
|
||||
#include "PNGReadWrite.hpp"
|
||||
|
||||
// #define EDGE_GRID_DEBUG_OUTPUT
|
||||
|
||||
#if 0
|
||||
// Enable debugging and assert in this file.
|
||||
|
|
@ -55,6 +55,24 @@ void EdgeGrid::Grid::create(const Polygons &polygons, coord_t resolution)
|
|||
create_from_m_contours(resolution);
|
||||
}
|
||||
|
||||
void EdgeGrid::Grid::create(const std::vector<const Polygon*> &polygons, coord_t resolution)
|
||||
{
|
||||
// Count the contours.
|
||||
size_t ncontours = 0;
|
||||
for (size_t j = 0; j < polygons.size(); ++ j)
|
||||
if (! polygons[j]->points.empty())
|
||||
++ ncontours;
|
||||
|
||||
// Collect the contours.
|
||||
m_contours.assign(ncontours, nullptr);
|
||||
ncontours = 0;
|
||||
for (size_t j = 0; j < polygons.size(); ++ j)
|
||||
if (! polygons[j]->points.empty())
|
||||
m_contours[ncontours ++] = &polygons[j]->points;
|
||||
|
||||
create_from_m_contours(resolution);
|
||||
}
|
||||
|
||||
void EdgeGrid::Grid::create(const std::vector<Points> &polygons, coord_t resolution)
|
||||
{
|
||||
// Count the contours.
|
||||
|
|
@ -659,6 +677,11 @@ struct PropagateDanielssonSingleVStep3 {
|
|||
|
||||
void EdgeGrid::Grid::calculate_sdf()
|
||||
{
|
||||
#ifdef EDGE_GRID_DEBUG_OUTPUT
|
||||
static int iRun = 0;
|
||||
++ iRun;
|
||||
#endif
|
||||
|
||||
// 1) Initialize a signum and an unsigned vector to a zero iso surface.
|
||||
size_t nrows = m_rows + 1;
|
||||
size_t ncols = m_cols + 1;
|
||||
|
|
@ -756,19 +779,12 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int iRun = 0;
|
||||
++ iRun;
|
||||
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||
wxImage::AddHandler(new wxPNGHandler);
|
||||
//#ifdef SLIC3R_GUI
|
||||
#ifdef EDGE_GRID_DEBUG_OUTPUT
|
||||
{
|
||||
wxImage img(ncols, nrows);
|
||||
unsigned char *data = img.GetData();
|
||||
memset(data, 0, ncols * nrows * 3);
|
||||
for (coord_t r = 0; r < nrows; ++r) {
|
||||
for (coord_t c = 0; c < ncols; ++c) {
|
||||
unsigned char *pxl = data + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
std::vector<uint8_t> pixels(ncols * nrows * 3, 0);
|
||||
for (coord_t r = 0; r < nrows; ++ r) {
|
||||
for (coord_t c = 0; c < ncols; ++ c) {
|
||||
uint8_t *pxl = pixels.data() + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
float d = m_signed_distance_field[r * ncols + c];
|
||||
if (d != search_radius) {
|
||||
float s = 255 * d / search_radius;
|
||||
|
|
@ -784,15 +800,13 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||
}
|
||||
}
|
||||
}
|
||||
img.SaveFile(debug_out_path("unsigned_df-%d.png", iRun), wxBITMAP_TYPE_PNG);
|
||||
png::write_rgb_to_file_scaled(debug_out_path("unsigned_df-%d.png", iRun), ncols, nrows, pixels, 10);
|
||||
}
|
||||
{
|
||||
wxImage img(ncols, nrows);
|
||||
unsigned char *data = img.GetData();
|
||||
memset(data, 0, ncols * nrows * 3);
|
||||
for (coord_t r = 0; r < nrows; ++r) {
|
||||
for (coord_t c = 0; c < ncols; ++c) {
|
||||
unsigned char *pxl = data + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
std::vector<uint8_t> pixels(ncols * nrows * 3, 0);
|
||||
for (coord_t r = 0; r < nrows; ++ r) {
|
||||
for (coord_t c = 0; c < ncols; ++ c) {
|
||||
unsigned char *pxl = pixels.data() + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
float d = m_signed_distance_field[r * ncols + c];
|
||||
if (d != search_radius) {
|
||||
float s = 255 * d / search_radius;
|
||||
|
|
@ -817,9 +831,9 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||
}
|
||||
}
|
||||
}
|
||||
img.SaveFile(debug_out_path("signed_df-%d.png", iRun), wxBITMAP_TYPE_PNG);
|
||||
png::write_rgb_to_file_scaled(debug_out_path("signed_df-%d.png", iRun), ncols, nrows, pixels, 10);
|
||||
}
|
||||
#endif /* SLIC3R_GUI */
|
||||
#endif // EDGE_GRID_DEBUG_OUTPUT
|
||||
|
||||
// 2) Propagate the signum.
|
||||
#define PROPAGATE_SIGNUM_SINGLE_STEP(DELTA) do { \
|
||||
|
|
@ -891,17 +905,14 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
//#ifdef SLIC3R_GUI
|
||||
#ifdef EDGE_GRID_DEBUG_OUTPUT
|
||||
{
|
||||
wxImage img(ncols, nrows);
|
||||
unsigned char *data = img.GetData();
|
||||
memset(data, 0, ncols * nrows * 3);
|
||||
std::vector<uint8_t> pixels(ncols * nrows * 3, 0);
|
||||
float search_radius = float(m_resolution * 5);
|
||||
for (coord_t r = 0; r < nrows; ++r) {
|
||||
for (coord_t c = 0; c < ncols; ++c) {
|
||||
unsigned char *pxl = data + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
unsigned char sign = signs[r * ncols + c];
|
||||
uint8_t *pxl = pixels.data() + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
uint8_t sign = signs[r * ncols + c];
|
||||
switch (sign) {
|
||||
case 0:
|
||||
// Positive, outside of a narrow band.
|
||||
|
|
@ -942,20 +953,17 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||
}
|
||||
}
|
||||
}
|
||||
img.SaveFile(debug_out_path("signed_df-signs-%d.png", iRun), wxBITMAP_TYPE_PNG);
|
||||
png::write_rgb_to_file_scaled(debug_out_path("signed_df-signs-%d.png", iRun), ncols, nrows, pixels, 10);
|
||||
}
|
||||
#endif /* SLIC3R_GUI */
|
||||
#endif // EDGE_GRID_DEBUG_OUTPUT
|
||||
|
||||
#if 0
|
||||
//#ifdef SLIC3R_GUI
|
||||
#ifdef EDGE_GRID_DEBUG_OUTPUT
|
||||
{
|
||||
wxImage img(ncols, nrows);
|
||||
unsigned char *data = img.GetData();
|
||||
memset(data, 0, ncols * nrows * 3);
|
||||
std::vector<uint8_t> pixels(ncols * nrows * 3, 0);
|
||||
float search_radius = float(m_resolution * 5);
|
||||
for (coord_t r = 0; r < nrows; ++r) {
|
||||
for (coord_t c = 0; c < ncols; ++c) {
|
||||
unsigned char *pxl = data + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
uint8_t *pxl = pixels.data() + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
float d = m_signed_distance_field[r * ncols + c];
|
||||
float s = 255.f * fabs(d) / search_radius;
|
||||
int is = std::max(0, std::min(255, int(floor(s + 0.5f))));
|
||||
|
|
@ -971,9 +979,9 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||
}
|
||||
}
|
||||
}
|
||||
img.SaveFile(debug_out_path("signed_df2-%d.png", iRun), wxBITMAP_TYPE_PNG);
|
||||
png::write_rgb_to_file_scaled(debug_out_path("signed_df2-%d.png", iRun), ncols, nrows, pixels, 10);
|
||||
}
|
||||
#endif /* SLIC3R_GUI */
|
||||
#endif // EDGE_GRID_DEBUG_OUTPUT
|
||||
}
|
||||
|
||||
float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const
|
||||
|
|
@ -1150,7 +1158,7 @@ EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point(const Point &pt
|
|||
if (result.contour_idx != size_t(-1) && d_min <= double(search_radius)) {
|
||||
result.distance = d_min * sign_min;
|
||||
result.t /= l2_seg_min;
|
||||
assert(result.t >= 0. && result.t < 1.);
|
||||
assert(result.t >= 0. && result.t <= 1.);
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
const Slic3r::Points &pts = *m_contours[result.contour_idx];
|
||||
|
|
@ -1473,26 +1481,18 @@ bool EdgeGrid::Grid::has_intersecting_edges() const
|
|||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path)
|
||||
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path, size_t scale)
|
||||
{
|
||||
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||
wxImage::AddHandler(new wxPNGHandler);
|
||||
|
||||
unsigned int w = (bbox.max(0) - bbox.min(0) + resolution - 1) / resolution;
|
||||
unsigned int h = (bbox.max(1) - bbox.min(1) + resolution - 1) / resolution;
|
||||
wxImage img(w, h);
|
||||
unsigned char *data = img.GetData();
|
||||
memset(data, 0, w * h * 3);
|
||||
|
||||
static int iRun = 0;
|
||||
++iRun;
|
||||
|
||||
std::vector<uint8_t> pixels(w * h * 3, 0);
|
||||
|
||||
const coord_t search_radius = grid.resolution() * 2;
|
||||
const coord_t display_blend_radius = grid.resolution() * 2;
|
||||
for (coord_t r = 0; r < h; ++r) {
|
||||
for (coord_t c = 0; c < w; ++ c) {
|
||||
unsigned char *pxl = data + (((h - r - 1) * w) + c) * 3;
|
||||
unsigned char *pxl = pixels.data() + (((h - r - 1) * w) + c) * 3;
|
||||
Point pt(c * resolution + bbox.min(0), r * resolution + bbox.min(1));
|
||||
coordf_t min_dist;
|
||||
bool on_segment = true;
|
||||
|
|
@ -1566,9 +1566,8 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo
|
|||
}
|
||||
}
|
||||
|
||||
img.SaveFile(path, wxBITMAP_TYPE_PNG);
|
||||
png::write_rgb_to_file_scaled(path, w, h, pixels, scale);
|
||||
}
|
||||
#endif /* SLIC3R_GUI */
|
||||
|
||||
// Find all pairs of intersectiong edges from the set of polygons.
|
||||
std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersecting_edges(const Polygons &polygons)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ public:
|
|||
void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; }
|
||||
|
||||
void create(const Polygons &polygons, coord_t resolution);
|
||||
void create(const std::vector<const Polygon*> &polygons, coord_t resolution);
|
||||
void create(const std::vector<Points> &polygons, coord_t resolution);
|
||||
void create(const ExPolygon &expoly, coord_t resolution);
|
||||
void create(const ExPolygons &expolygons, coord_t resolution);
|
||||
|
|
@ -83,10 +84,14 @@ public:
|
|||
template<typename VISITOR> void visit_cells_intersecting_line(Slic3r::Point p1, Slic3r::Point p2, VISITOR &visitor) const
|
||||
{
|
||||
// End points of the line segment.
|
||||
p1(0) -= m_bbox.min(0);
|
||||
p1(1) -= m_bbox.min(1);
|
||||
p2(0) -= m_bbox.min(0);
|
||||
p2(1) -= m_bbox.min(1);
|
||||
assert(m_bbox.contains(p1));
|
||||
assert(m_bbox.contains(p2));
|
||||
p1 -= m_bbox.min;
|
||||
p2 -= m_bbox.min;
|
||||
assert(p1.x() >= 0 && p1.x() < m_cols * m_resolution);
|
||||
assert(p1.y() >= 0 && p1.y() < m_rows * m_resolution);
|
||||
assert(p2.x() >= 0 && p2.x() < m_cols * m_resolution);
|
||||
assert(p2.y() >= 0 && p2.y() < m_rows * m_resolution);
|
||||
// Get the cells of the end points.
|
||||
coord_t ix = p1(0) / m_resolution;
|
||||
coord_t iy = p1(1) / m_resolution;
|
||||
|
|
@ -114,18 +119,22 @@ public:
|
|||
ey -= ex;
|
||||
ex = int64_t(dy) * m_resolution;
|
||||
ix += 1;
|
||||
assert(ix <= ixb);
|
||||
}
|
||||
else if (ex == ey) {
|
||||
ex = int64_t(dy) * m_resolution;
|
||||
ey = int64_t(dx) * m_resolution;
|
||||
ix += 1;
|
||||
iy += 1;
|
||||
assert(ix <= ixb);
|
||||
assert(iy <= iyb);
|
||||
}
|
||||
else {
|
||||
assert(ex > ey);
|
||||
ex -= ey;
|
||||
ey = int64_t(dx) * m_resolution;
|
||||
iy += 1;
|
||||
assert(iy <= iyb);
|
||||
}
|
||||
if (! visitor(iy, ix))
|
||||
return;
|
||||
|
|
@ -140,11 +149,13 @@ public:
|
|||
ey -= ex;
|
||||
ex = int64_t(dy) * m_resolution;
|
||||
ix += 1;
|
||||
assert(ix <= ixb);
|
||||
}
|
||||
else {
|
||||
ex -= ey;
|
||||
ey = int64_t(dx) * m_resolution;
|
||||
iy -= 1;
|
||||
assert(iy >= iyb);
|
||||
}
|
||||
if (! visitor(iy, ix))
|
||||
return;
|
||||
|
|
@ -162,12 +173,14 @@ public:
|
|||
ey -= ex;
|
||||
ex = int64_t(dy) * m_resolution;
|
||||
ix -= 1;
|
||||
assert(ix >= ixb);
|
||||
}
|
||||
else {
|
||||
assert(ex >= ey);
|
||||
ex -= ey;
|
||||
ey = int64_t(dx) * m_resolution;
|
||||
iy += 1;
|
||||
assert(iy <= iyb);
|
||||
}
|
||||
if (! visitor(iy, ix))
|
||||
return;
|
||||
|
|
@ -182,6 +195,7 @@ public:
|
|||
ey -= ex;
|
||||
ex = int64_t(dy) * m_resolution;
|
||||
ix -= 1;
|
||||
assert(ix >= ixb);
|
||||
}
|
||||
else if (ex == ey) {
|
||||
// The lower edge of a grid cell belongs to the cell.
|
||||
|
|
@ -190,10 +204,12 @@ public:
|
|||
if (dx > 0) {
|
||||
ex = int64_t(dy) * m_resolution;
|
||||
ix -= 1;
|
||||
assert(ix >= ixb);
|
||||
}
|
||||
if (dy > 0) {
|
||||
ey = int64_t(dx) * m_resolution;
|
||||
iy -= 1;
|
||||
assert(iy >= iyb);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -201,6 +217,7 @@ public:
|
|||
ex -= ey;
|
||||
ey = int64_t(dx) * m_resolution;
|
||||
iy -= 1;
|
||||
assert(iy >= iyb);
|
||||
}
|
||||
if (! visitor(iy, ix))
|
||||
return;
|
||||
|
|
@ -230,6 +247,10 @@ public:
|
|||
|
||||
std::pair<std::vector<std::pair<size_t, size_t>>::const_iterator, std::vector<std::pair<size_t, size_t>>::const_iterator> cell_data_range(coord_t row, coord_t col) const
|
||||
{
|
||||
assert(row >= 0);
|
||||
assert(row < m_rows);
|
||||
assert(col >= 0);
|
||||
assert(col < m_cols);
|
||||
const EdgeGrid::Grid::Cell &cell = m_cells[row * m_cols + col];
|
||||
return std::make_pair(m_cell_data.begin() + cell.begin, m_cell_data.begin() + cell.end);
|
||||
}
|
||||
|
|
@ -295,10 +316,8 @@ protected:
|
|||
std::vector<float> m_signed_distance_field;
|
||||
};
|
||||
|
||||
#if 0
|
||||
// Debugging utility. Save the signed distance field.
|
||||
extern void save_png(const Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path);
|
||||
#endif /* SLIC3R_GUI */
|
||||
extern void save_png(const Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path, size_t scale = 1);
|
||||
|
||||
} // namespace EdgeGrid
|
||||
|
||||
|
|
|
|||
|
|
@ -350,23 +350,10 @@ void ExPolygon::get_trapezoids2(Polygons* polygons) const
|
|||
// find trapezoids by looping from first to next-to-last coordinate
|
||||
for (std::vector<coord_t>::const_iterator x = xx.begin(); x != xx.end()-1; ++x) {
|
||||
coord_t next_x = *(x + 1);
|
||||
if (*x == next_x) continue;
|
||||
|
||||
// build rectangle
|
||||
Polygon poly;
|
||||
poly.points.resize(4);
|
||||
poly[0](0) = *x;
|
||||
poly[0](1) = bb.min(1);
|
||||
poly[1](0) = next_x;
|
||||
poly[1](1) = bb.min(1);
|
||||
poly[2](0) = next_x;
|
||||
poly[2](1) = bb.max(1);
|
||||
poly[3](0) = *x;
|
||||
poly[3](1) = bb.max(1);
|
||||
|
||||
// intersect with this expolygon
|
||||
// append results to return value
|
||||
polygons_append(*polygons, intersection(poly, to_polygons(*this)));
|
||||
if (*x != next_x)
|
||||
// intersect with rectangle
|
||||
// append results to return value
|
||||
polygons_append(*polygons, intersection({ { { *x, bb.min.y() }, { next_x, bb.min.y() }, { next_x, bb.max.y() }, { *x, bb.max.y() } } }, to_polygons(*this)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ typedef std::vector<ExPolygon> ExPolygons;
|
|||
class ExPolygon
|
||||
{
|
||||
public:
|
||||
ExPolygon() {}
|
||||
ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {}
|
||||
ExPolygon(ExPolygon &&other) noexcept : contour(std::move(other.contour)), holes(std::move(other.holes)) {}
|
||||
ExPolygon() = default;
|
||||
ExPolygon(const ExPolygon &other) = default;
|
||||
ExPolygon(ExPolygon &&other) = default;
|
||||
explicit ExPolygon(const Polygon &contour) : contour(contour) {}
|
||||
explicit ExPolygon(Polygon &&contour) : contour(std::move(contour)) {}
|
||||
explicit ExPolygon(const Points &contour) : contour(contour) {}
|
||||
|
|
@ -31,10 +31,10 @@ public:
|
|||
ExPolygon(std::initializer_list<Point> contour) : contour(contour) {}
|
||||
ExPolygon(std::initializer_list<Point> contour, std::initializer_list<Point> hole) : contour(contour), holes({ hole }) {}
|
||||
|
||||
ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; }
|
||||
ExPolygon& operator=(ExPolygon &&other) noexcept { contour = std::move(other.contour); holes = std::move(other.holes); return *this; }
|
||||
ExPolygon& operator=(const ExPolygon &other) = default;
|
||||
ExPolygon& operator=(ExPolygon &&other) = default;
|
||||
|
||||
Polygon contour;
|
||||
Polygon contour;
|
||||
Polygons holes;
|
||||
|
||||
operator Points() const;
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ namespace Slic3r {
|
|||
|
||||
void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
|
||||
{
|
||||
this->_inflate_collection(intersection_pl(this->polyline, (Polygons)collection), retval);
|
||||
this->_inflate_collection(intersection_pl((Polylines)polyline, to_polygons(collection.expolygons)), retval);
|
||||
}
|
||||
|
||||
void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
|
||||
{
|
||||
this->_inflate_collection(diff_pl(this->polyline, (Polygons)collection), retval);
|
||||
this->_inflate_collection(diff_pl((Polylines)this->polyline, to_polygons(collection.expolygons)), retval);
|
||||
}
|
||||
|
||||
void ExtrusionPath::clip_end(double distance)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
#include "FillRectilinear2.hpp"
|
||||
#include "FillRectilinear.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -33,10 +33,12 @@ struct SurfaceFillParams
|
|||
|
||||
// FillParams
|
||||
float density = 0.f;
|
||||
// Don't connect the fill lines around the inner perimeter.
|
||||
bool dont_connect = false;
|
||||
// Don't adjust spacing to fill the space evenly.
|
||||
bool dont_adjust = false;
|
||||
// Length of the infill anchor along the perimeter line.
|
||||
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
|
||||
float anchor_length = 1000.f;
|
||||
float anchor_length_max = 1000.f;
|
||||
|
||||
// width, height of extrusion, nozzle diameter, is bridge
|
||||
// For the output, for fill generator.
|
||||
|
|
@ -65,8 +67,9 @@ struct SurfaceFillParams
|
|||
RETURN_COMPARE_NON_EQUAL(overlap);
|
||||
RETURN_COMPARE_NON_EQUAL(angle);
|
||||
RETURN_COMPARE_NON_EQUAL(density);
|
||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_connect);
|
||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
|
||||
RETURN_COMPARE_NON_EQUAL(anchor_length);
|
||||
RETURN_COMPARE_NON_EQUAL(anchor_length_max);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.width);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.height);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter);
|
||||
|
|
@ -83,8 +86,9 @@ struct SurfaceFillParams
|
|||
this->overlap == rhs.overlap &&
|
||||
this->angle == rhs.angle &&
|
||||
this->density == rhs.density &&
|
||||
this->dont_connect == rhs.dont_connect &&
|
||||
this->dont_adjust == rhs.dont_adjust &&
|
||||
this->anchor_length == rhs.anchor_length &&
|
||||
this->anchor_length_max == rhs.anchor_length_max &&
|
||||
this->flow == rhs.flow &&
|
||||
this->extrusion_role == rhs.extrusion_role;
|
||||
}
|
||||
|
|
@ -115,16 +119,17 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|||
if (surface.surface_type == stInternalVoid)
|
||||
has_internal_voids = true;
|
||||
else {
|
||||
const PrintRegionConfig ®ion_config = layerm.region()->config();
|
||||
FlowRole extrusion_role = surface.is_top() ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill);
|
||||
bool is_bridge = layer.id() > 0 && surface.is_bridge();
|
||||
params.extruder = layerm.region()->extruder(extrusion_role);
|
||||
params.pattern = layerm.region()->config().fill_pattern.value;
|
||||
params.density = float(layerm.region()->config().fill_density);
|
||||
params.pattern = region_config.fill_pattern.value;
|
||||
params.density = float(region_config.fill_density);
|
||||
|
||||
if (surface.is_solid()) {
|
||||
params.density = 100.f;
|
||||
params.pattern = (surface.is_external() && ! is_bridge) ?
|
||||
(surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) :
|
||||
(surface.is_top() ? region_config.top_fill_pattern.value : region_config.bottom_fill_pattern.value) :
|
||||
ipRectilinear;
|
||||
} else if (params.density <= 0)
|
||||
continue;
|
||||
|
|
@ -136,7 +141,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|||
(surface.is_top() ? erTopSolidInfill : erSolidInfill) :
|
||||
erInternalInfill);
|
||||
params.bridge_angle = float(surface.bridge_angle);
|
||||
params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
|
||||
params.angle = float(Geometry::deg2rad(region_config.fill_angle.value));
|
||||
|
||||
// calculate the actual flow we'll be using for this infill
|
||||
params.flow = layerm.region()->flow(
|
||||
|
|
@ -149,7 +154,11 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|||
);
|
||||
|
||||
// Calculate flow spacing for infill pattern generation.
|
||||
if (! surface.is_solid() && ! is_bridge) {
|
||||
if (surface.is_solid() || is_bridge) {
|
||||
params.spacing = params.flow.spacing();
|
||||
// Don't limit anchor length for solid or bridging infill.
|
||||
params.anchor_length = 1000.f;
|
||||
} else {
|
||||
// it's internal infill, so we can calculate a generic flow spacing
|
||||
// for all layers, for avoiding the ugly effect of
|
||||
// misaligned infill on first layer because of different extrusion width and
|
||||
|
|
@ -162,8 +171,15 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|||
-1, // auto width
|
||||
*layer.object()
|
||||
).spacing();
|
||||
} else
|
||||
params.spacing = params.flow.spacing();
|
||||
// Anchor a sparse infill to inner perimeters with the following anchor length:
|
||||
params.anchor_length = float(region_config.infill_anchor);
|
||||
if (region_config.infill_anchor.percent)
|
||||
params.anchor_length = float(params.anchor_length * 0.01 * params.spacing);
|
||||
params.anchor_length_max = float(region_config.infill_anchor_max);
|
||||
if (region_config.infill_anchor_max.percent)
|
||||
params.anchor_length_max = float(params.anchor_length_max * 0.01 * params.spacing);
|
||||
}
|
||||
params.anchor_length = std::min(params.anchor_length, params.anchor_length_max);
|
||||
|
||||
auto it_params = set_surface_params.find(params);
|
||||
if (it_params == set_surface_params.end())
|
||||
|
|
@ -367,8 +383,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
|||
|
||||
// apply half spacing using this flow's own spacing and generate infill
|
||||
FillParams params;
|
||||
params.density = float(0.01 * surface_fill.params.density);
|
||||
params.dont_adjust = surface_fill.params.dont_adjust; // false
|
||||
params.density = float(0.01 * surface_fill.params.density);
|
||||
params.dont_adjust = surface_fill.params.dont_adjust; // false
|
||||
params.anchor_length = surface_fill.params.anchor_length;
|
||||
params.anchor_length_max = surface_fill.params.anchor_length_max;
|
||||
|
||||
for (ExPolygon &expoly : surface_fill.expolygons) {
|
||||
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
||||
|
|
@ -526,15 +544,13 @@ void Layer::make_ironing()
|
|||
}
|
||||
std::sort(by_extruder.begin(), by_extruder.end());
|
||||
|
||||
FillRectilinear2 fill;
|
||||
FillRectilinear fill;
|
||||
FillParams fill_params;
|
||||
fill.set_bounding_box(this->object()->bounding_box());
|
||||
fill.layer_id = this->id();
|
||||
fill.z = this->print_z;
|
||||
fill.overlap = 0;
|
||||
fill_params.density = 1.;
|
||||
// fill_params.dont_connect = true;
|
||||
fill_params.dont_connect = false;
|
||||
fill_params.monotonic = true;
|
||||
|
||||
for (size_t i = 0; i < by_extruder.size(); ++ i) {
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ class LayerRegion;
|
|||
class Filler
|
||||
{
|
||||
public:
|
||||
Filler() : fill(NULL) {}
|
||||
Filler() : fill(nullptr) {}
|
||||
~Filler() {
|
||||
delete fill;
|
||||
fill = NULL;
|
||||
fill = nullptr;
|
||||
}
|
||||
Fill *fill;
|
||||
FillParams params;
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ void Fill3DHoneycomb::_fill_surface_single(
|
|||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// no rotation is supported for this infill pattern
|
||||
|
|
@ -162,15 +162,13 @@ void Fill3DHoneycomb::_fill_surface_single(
|
|||
pl.translate(bb.min);
|
||||
|
||||
// clip pattern to boundaries, chain the clipped polylines
|
||||
Polylines polylines_chained = chain_polylines(intersection_pl(polylines, to_polygons(expolygon)));
|
||||
polylines = intersection_pl(polylines, to_polygons(expolygon));
|
||||
|
||||
// connect lines if needed
|
||||
if (! polylines_chained.empty()) {
|
||||
if (params.dont_connect)
|
||||
append(polylines_out, std::move(polylines_chained));
|
||||
else
|
||||
this->connect_infill(std::move(polylines_chained), expolygon, polylines_out, this->spacing, params);
|
||||
}
|
||||
if (params.dont_connect() || polylines.size() <= 1)
|
||||
append(polylines_out, chain_polylines(std::move(polylines)));
|
||||
else
|
||||
this->connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -12,19 +12,19 @@ namespace Slic3r {
|
|||
class Fill3DHoneycomb : public Fill
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new Fill3DHoneycomb(*this); };
|
||||
virtual ~Fill3DHoneycomb() {}
|
||||
Fill* clone() const override { return new Fill3DHoneycomb(*this); };
|
||||
~Fill3DHoneycomb() override {}
|
||||
|
||||
// require bridge flow since most of this pattern hangs in air
|
||||
virtual bool use_bridge_flow() const { return true; }
|
||||
bool use_bridge_flow() const override { return true; }
|
||||
|
||||
protected:
|
||||
virtual void _fill_surface_single(
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -14,11 +14,18 @@
|
|||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
// Boost pool: Don't use mutexes to synchronize memory allocation.
|
||||
#define BOOST_POOL_NO_MT
|
||||
#include <boost/pool/object_pool.hpp>
|
||||
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/geometries/point.hpp>
|
||||
#include <boost/geometry/geometries/segment.hpp>
|
||||
#include <boost/geometry/index/rtree.hpp>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
namespace FillAdaptive {
|
||||
|
||||
|
|
@ -288,7 +295,7 @@ std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_ob
|
|||
bool build_octree = false;
|
||||
const std::vector<double> &nozzle_diameters = print_object.print()->config().nozzle_diameter.values;
|
||||
double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end());
|
||||
double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, max_nozzle_diameter);
|
||||
double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter));
|
||||
for (const PrintRegion *region : print_object.print()->regions()) {
|
||||
const PrintRegionConfig &config = region->config();
|
||||
bool nonempty = config.fill_density > 0;
|
||||
|
|
@ -475,7 +482,7 @@ static void generate_infill_lines_recursive(
|
|||
Line new_line(Point::new_scale(from), Point::new_scale(to));
|
||||
if (last_line.a.x() == std::numeric_limits<coord_t>::max()) {
|
||||
last_line.a = new_line.a;
|
||||
} else if ((new_line.a - last_line.b).cwiseAbs().maxCoeff() > 300) { // SCALED_EPSILON is 100 and it is not enough
|
||||
} else if ((new_line.a - last_line.b).cwiseAbs().maxCoeff() > 1000) { // SCALED_EPSILON is 100 and it is not enough
|
||||
context.output_lines.emplace_back(last_line);
|
||||
last_line.a = new_line.a;
|
||||
}
|
||||
|
|
@ -501,7 +508,7 @@ static void generate_infill_lines_recursive(
|
|||
#endif
|
||||
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
static void export_infill_lines_to_svg(const ExPolygon &expoly, const Polylines &polylines, const std::string &path)
|
||||
static void export_infill_lines_to_svg(const ExPolygon &expoly, const Polylines &polylines, const std::string &path, const Points &pts = Points())
|
||||
{
|
||||
BoundingBox bbox = get_extents(expoly);
|
||||
bbox.offset(scale_(3.));
|
||||
|
|
@ -511,46 +518,805 @@ static void export_infill_lines_to_svg(const ExPolygon &expoly, const Polylines
|
|||
svg.draw_outline(expoly, "green");
|
||||
svg.draw(polylines, "red");
|
||||
static constexpr double trim_length = scale_(0.4);
|
||||
for (Polyline polyline : polylines) {
|
||||
Vec2d a = polyline.points.front().cast<double>();
|
||||
Vec2d d = polyline.points.back().cast<double>();
|
||||
if (polyline.size() == 2) {
|
||||
Vec2d v = d - a;
|
||||
double l = v.norm();
|
||||
if (l > 2. * trim_length) {
|
||||
a += v * trim_length / l;
|
||||
d -= v * trim_length / l;
|
||||
polyline.points.front() = a.cast<coord_t>();
|
||||
polyline.points.back() = d.cast<coord_t>();
|
||||
} else
|
||||
polyline.points.clear();
|
||||
} else if (polyline.size() > 2) {
|
||||
Vec2d b = polyline.points[1].cast<double>();
|
||||
Vec2d c = polyline.points[polyline.points.size() - 2].cast<double>();
|
||||
Vec2d v = b - a;
|
||||
double l = v.norm();
|
||||
if (l > trim_length) {
|
||||
a += v * trim_length / l;
|
||||
polyline.points.front() = a.cast<coord_t>();
|
||||
} else
|
||||
polyline.points.erase(polyline.points.begin());
|
||||
v = d - c;
|
||||
l = v.norm();
|
||||
if (l > trim_length)
|
||||
polyline.points.back() = (d - v * trim_length / l).cast<coord_t>();
|
||||
else
|
||||
polyline.points.pop_back();
|
||||
for (Polyline polyline : polylines)
|
||||
if (! polyline.empty()) {
|
||||
Vec2d a = polyline.points.front().cast<double>();
|
||||
Vec2d d = polyline.points.back().cast<double>();
|
||||
if (polyline.size() == 2) {
|
||||
Vec2d v = d - a;
|
||||
double l = v.norm();
|
||||
if (l > 2. * trim_length) {
|
||||
a += v * trim_length / l;
|
||||
d -= v * trim_length / l;
|
||||
polyline.points.front() = a.cast<coord_t>();
|
||||
polyline.points.back() = d.cast<coord_t>();
|
||||
} else
|
||||
polyline.points.clear();
|
||||
} else if (polyline.size() > 2) {
|
||||
Vec2d b = polyline.points[1].cast<double>();
|
||||
Vec2d c = polyline.points[polyline.points.size() - 2].cast<double>();
|
||||
Vec2d v = b - a;
|
||||
double l = v.norm();
|
||||
if (l > trim_length) {
|
||||
a += v * trim_length / l;
|
||||
polyline.points.front() = a.cast<coord_t>();
|
||||
} else
|
||||
polyline.points.erase(polyline.points.begin());
|
||||
v = d - c;
|
||||
l = v.norm();
|
||||
if (l > trim_length)
|
||||
polyline.points.back() = (d - v * trim_length / l).cast<coord_t>();
|
||||
else
|
||||
polyline.points.pop_back();
|
||||
}
|
||||
svg.draw(polyline, "black");
|
||||
}
|
||||
svg.draw(polyline, "black");
|
||||
}
|
||||
svg.draw(pts, "magenta");
|
||||
}
|
||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
||||
|
||||
// Representing a T-joint (in general case) between two infill lines
|
||||
// (between one end point of intersect_pl/intersect_line and
|
||||
struct Intersection
|
||||
{
|
||||
// Closest line to intersect_point.
|
||||
const Line *closest_line;
|
||||
|
||||
// The line for which is computed closest line from intersect_point to closest_line
|
||||
const Line *intersect_line;
|
||||
// Pointer to the polyline from which is computed closest_line
|
||||
Polyline *intersect_pl;
|
||||
// Point for which is computed closest line (closest_line)
|
||||
Point intersect_point;
|
||||
// Indicate if intersect_point is the first or the last point of intersect_pl
|
||||
bool front;
|
||||
// Signum of intersect_line_dir.cross(closest_line.dir()):
|
||||
bool left;
|
||||
|
||||
// Indication if this intersection has been proceed
|
||||
bool used = false;
|
||||
|
||||
bool fresh() const throw() { return ! used && ! intersect_pl->empty(); }
|
||||
|
||||
Intersection(const Line &closest_line, const Line &intersect_line, Polyline *intersect_pl, const Point &intersect_point, bool front) :
|
||||
closest_line(&closest_line), intersect_line(&intersect_line), intersect_pl(intersect_pl), intersect_point(intersect_point), front(front)
|
||||
{
|
||||
// Calculate side of this intersection line of the closest line.
|
||||
Vec2d v1((this->closest_line->b - this->closest_line->a).cast<double>());
|
||||
Vec2d v2(this->intersect_line_dir());
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
Vec2d v1n = v1.normalized();
|
||||
Vec2d v2n = v2.normalized();
|
||||
double c = cross2(v1n, v2n);
|
||||
assert(std::abs(c) > sin(M_PI / 12.));
|
||||
}
|
||||
#endif // NDEBUG
|
||||
this->left = cross2(v1, v2) > 0.;
|
||||
}
|
||||
|
||||
std::optional<Line> other_hook() const {
|
||||
std::optional<Line> out;
|
||||
const Points &pts = intersect_pl->points;
|
||||
if (pts.size() >= 3)
|
||||
out = this->front ? Line(pts[1], pts[2]) : Line(pts[pts.size() - 2], pts[pts.size() - 3]);
|
||||
return out;
|
||||
}
|
||||
|
||||
bool other_hook_intersects(const Line &l, Point &pt) {
|
||||
std::optional<Line> h = other_hook();
|
||||
return h && h->intersection(l, &pt);
|
||||
}
|
||||
bool other_hook_intersects(const Line &l) { Point pt; return this->other_hook_intersects(l, pt); }
|
||||
|
||||
// Direction to intersect_point.
|
||||
Vec2d intersect_line_dir() const throw() {
|
||||
return (this->intersect_point == intersect_line->a ? intersect_line->b - intersect_line->a : intersect_line->a - intersect_line->b).cast<double>();
|
||||
}
|
||||
};
|
||||
|
||||
static inline Intersection* get_nearest_intersection(std::vector<std::pair<Intersection*, double>>& intersect_line, const size_t first_idx)
|
||||
{
|
||||
assert(intersect_line.size() >= 2);
|
||||
bool take_next = false;
|
||||
if (first_idx == 0)
|
||||
take_next = true;
|
||||
else if (first_idx + 1 == intersect_line.size())
|
||||
take_next = false;
|
||||
else {
|
||||
// Has both prev and next.
|
||||
const std::pair<Intersection*, double> &ithis = intersect_line[first_idx];
|
||||
const std::pair<Intersection*, double> &iprev = intersect_line[first_idx - 1];
|
||||
const std::pair<Intersection*, double> &inext = intersect_line[first_idx + 1];
|
||||
take_next = iprev.first->fresh() && inext.first->fresh() ?
|
||||
inext.second - ithis.second < ithis.second - iprev.second :
|
||||
inext.first->fresh();
|
||||
}
|
||||
return intersect_line[take_next ? first_idx + 1 : first_idx - 1].first;
|
||||
}
|
||||
|
||||
// Create a line representing the anchor aka hook extrusion based on line_to_offset
|
||||
// translated in the direction of the intersection line (intersection.intersect_line).
|
||||
static Line create_offset_line(Line offset_line, const Intersection &intersection, const double scaled_offset)
|
||||
{
|
||||
offset_line.translate((perp(intersection.closest_line->vector().cast<double>().normalized()) * (intersection.left ? scaled_offset : - scaled_offset)).cast<coord_t>());
|
||||
// Extend the line by a small value to guarantee a collision with adjacent lines
|
||||
offset_line.extend(coord_t(scaled_offset * 1.16)); // / cos(PI/6)
|
||||
return offset_line;
|
||||
}
|
||||
|
||||
namespace bg = boost::geometry;
|
||||
namespace bgm = boost::geometry::model;
|
||||
namespace bgi = boost::geometry::index;
|
||||
|
||||
// float is needed because for coord_t bgi::intersects throws "bad numeric conversion: positive overflow"
|
||||
using rtree_point_t = bgm::point<float, 2, boost::geometry::cs::cartesian>;
|
||||
using rtree_segment_t = bgm::segment<rtree_point_t>;
|
||||
using rtree_t = bgi::rtree<std::pair<rtree_segment_t, size_t>, bgi::rstar<16, 4>>;
|
||||
|
||||
static inline rtree_point_t mk_rtree_point(const Point &pt) {
|
||||
return rtree_point_t(float(pt.x()), float(pt.y()));
|
||||
}
|
||||
static inline rtree_segment_t mk_rtree_seg(const Point &a, const Point &b) {
|
||||
return { mk_rtree_point(a), mk_rtree_point(b) };
|
||||
}
|
||||
static inline rtree_segment_t mk_rtree_seg(const Line &l) {
|
||||
return mk_rtree_seg(l.a, l.b);
|
||||
}
|
||||
|
||||
// Create a hook based on hook_line and append it to the begin or end of the polyline in the intersection
|
||||
static void add_hook(
|
||||
const Intersection &intersection, const double scaled_offset,
|
||||
const coordf_t hook_length, double scaled_trim_distance,
|
||||
const rtree_t &rtree, const Lines &lines_src)
|
||||
{
|
||||
if (hook_length < SCALED_EPSILON)
|
||||
// Ignore open hooks.
|
||||
return;
|
||||
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
const Vec2d v = (intersection.closest_line->b - intersection.closest_line->a).cast<double>();
|
||||
const Vec2d va = (intersection.intersect_point - intersection.closest_line->a).cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
assert(l2 > 0.);
|
||||
const double t = va.dot(v) / l2;
|
||||
assert(t > 0. && t < 1.);
|
||||
const double d = (t * v - va).norm();
|
||||
assert(d < 1000.);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
// Trim the hook start by the infill line it will connect to.
|
||||
Point hook_start;
|
||||
bool intersection_found = intersection.intersect_line->intersection(
|
||||
create_offset_line(*intersection.closest_line, intersection, scaled_offset),
|
||||
&hook_start);
|
||||
assert(intersection_found);
|
||||
|
||||
std::optional<Line> other_hook = intersection.other_hook();
|
||||
|
||||
Vec2d hook_vector_norm = intersection.closest_line->vector().cast<double>().normalized();
|
||||
// hook_vector is extended by the thickness of the infill line, so that a collision is found against
|
||||
// the infill centerline to be later trimmed by the thickened line.
|
||||
Vector hook_vector = ((hook_length + 1.16 * scaled_trim_distance) * hook_vector_norm).cast<coord_t>();
|
||||
Line hook_forward(hook_start, hook_start + hook_vector);
|
||||
|
||||
auto filter_itself = [&intersection, &lines_src](const auto &item) { return item.second != intersection.intersect_line - lines_src.data(); };
|
||||
|
||||
std::vector<std::pair<rtree_segment_t, size_t>> hook_intersections;
|
||||
rtree.query(bgi::intersects(mk_rtree_seg(hook_forward)) && bgi::satisfies(filter_itself), std::back_inserter(hook_intersections));
|
||||
Point self_intersection_point;
|
||||
bool self_intersection = other_hook && other_hook->intersection(hook_forward, &self_intersection_point);
|
||||
|
||||
// Find closest intersection of a line segment starting with pt pointing in dir
|
||||
// with any of the hook_intersections, returns Euclidian distance.
|
||||
// dir is normalized.
|
||||
auto max_hook_length = [hook_length, scaled_trim_distance, &lines_src](
|
||||
const Vec2d &pt, const Vec2d &dir,
|
||||
const std::vector<std::pair<rtree_segment_t, size_t>> &hook_intersections,
|
||||
bool self_intersection, const std::optional<Line> &self_intersection_line, const Point &self_intersection_point) {
|
||||
// No hook is longer than hook_length, there shouldn't be any intersection closer than that.
|
||||
auto max_length = hook_length;
|
||||
auto update_max_length = [&max_length](double d) {
|
||||
if (d < max_length)
|
||||
max_length = d;
|
||||
};
|
||||
// Shift the trimming point away from the colliding thick line.
|
||||
auto shift_from_thick_line = [&dir, scaled_trim_distance](const Vec2d& dir2) {
|
||||
return scaled_trim_distance * std::abs(cross2(dir, dir2.normalized()));
|
||||
};
|
||||
|
||||
for (const auto &hook_intersection : hook_intersections) {
|
||||
const rtree_segment_t &segment = hook_intersection.first;
|
||||
// Segment start and end points, segment vector.
|
||||
Vec2d pt2(bg::get<0, 0>(segment), bg::get<0, 1>(segment));
|
||||
Vec2d dir2 = Vec2d(bg::get<1, 0>(segment), bg::get<1, 1>(segment)) - pt2;
|
||||
// Find intersection of (pt, dir) with (pt2, dir2), where dir is normalized.
|
||||
double denom = cross2(dir, dir2);
|
||||
assert(std::abs(denom) > EPSILON);
|
||||
double t = cross2(pt2 - pt, dir2) / denom;
|
||||
if (hook_intersection.second < lines_src.size())
|
||||
// Trimming by another infill line. Reduce overlap.
|
||||
t -= shift_from_thick_line(dir2);
|
||||
update_max_length(t);
|
||||
}
|
||||
if (self_intersection) {
|
||||
double t = (self_intersection_point.cast<double>() - pt).dot(dir) - shift_from_thick_line((*self_intersection_line).vector().cast<double>());
|
||||
max_length = std::min(max_length, t);
|
||||
}
|
||||
return std::max(0., max_length);
|
||||
};
|
||||
|
||||
Vec2d hook_startf = hook_start.cast<double>();
|
||||
double hook_forward_max_length = max_hook_length(hook_startf, hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point);
|
||||
double hook_backward_max_length = 0.;
|
||||
if (hook_forward_max_length < hook_length - SCALED_EPSILON) {
|
||||
// Try the other side.
|
||||
hook_intersections.clear();
|
||||
Line hook_backward(hook_start, hook_start - hook_vector);
|
||||
rtree.query(bgi::intersects(mk_rtree_seg(hook_backward)) && bgi::satisfies(filter_itself), std::back_inserter(hook_intersections));
|
||||
self_intersection = other_hook && other_hook->intersection(hook_backward, &self_intersection_point);
|
||||
hook_backward_max_length = max_hook_length(hook_startf, - hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point);
|
||||
}
|
||||
|
||||
// Take the longer hook.
|
||||
Vec2d hook_dir = (hook_forward_max_length > hook_backward_max_length ? hook_forward_max_length : - hook_backward_max_length) * hook_vector_norm;
|
||||
Point hook_end = hook_start + hook_dir.cast<coord_t>();
|
||||
|
||||
Points &pl = intersection.intersect_pl->points;
|
||||
if (intersection.front) {
|
||||
pl.front() = hook_start;
|
||||
pl.emplace(pl.begin(), hook_end);
|
||||
} else {
|
||||
pl.back() = hook_start;
|
||||
pl.emplace_back(hook_end);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool validate_intersection_t_joint(const Intersection &intersection)
|
||||
{
|
||||
const Vec2d v = (intersection.closest_line->b - intersection.closest_line->a).cast<double>();
|
||||
const Vec2d va = (intersection.intersect_point - intersection.closest_line->a).cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
assert(l2 > 0.);
|
||||
const double t = va.dot(v);
|
||||
assert(t > SCALED_EPSILON && t < l2 - SCALED_EPSILON);
|
||||
const double d = ((t / l2) * v - va).norm();
|
||||
assert(d < 1000.);
|
||||
return true;
|
||||
}
|
||||
bool validate_intersections(const std::vector<Intersection> &intersections)
|
||||
{
|
||||
for (const Intersection& intersection : intersections)
|
||||
assert(validate_intersection_t_joint(intersection));
|
||||
return true;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &boundary, const double spacing, const coordf_t hook_length, const coordf_t hook_length_max)
|
||||
{
|
||||
rtree_t rtree;
|
||||
size_t poly_idx = 0;
|
||||
|
||||
// 19% overlap, slightly lower than the allowed overlap in Fill::connect_infill()
|
||||
const float scaled_offset = float(scale_(spacing) * 0.81);
|
||||
// 25% overlap
|
||||
const float scaled_trim_distance = float(scale_(spacing) * 0.5 * 0.75);
|
||||
|
||||
// Keeping the vector of closest points outside the loop, so the vector does not need to be reallocated.
|
||||
std::vector<std::pair<rtree_segment_t, size_t>> closest;
|
||||
// Pairs of lines touching at one end point. The pair is sorted to make the end point connection test symmetric.
|
||||
std::vector<std::pair<const Polyline*, const Polyline*>> lines_touching_at_endpoints;
|
||||
{
|
||||
// Insert infill lines into rtree, merge close collinear segments split by the infill boundary,
|
||||
// collect lines_touching_at_endpoints.
|
||||
double r2_close = Slic3r::sqr(1200.);
|
||||
for (Polyline &poly : lines) {
|
||||
assert(poly.points.size() == 2);
|
||||
if (&poly != lines.data()) {
|
||||
// Join collinear segments separated by a tiny gap. These gaps were likely created by clipping the infill lines with a concave dent in an infill boundary.
|
||||
auto collinear_segment = [&rtree, &closest, &lines, &lines_touching_at_endpoints, r2_close](const Point& pt, const Point& pt_other, const Polyline* polyline) -> std::pair<Polyline*, bool> {
|
||||
closest.clear();
|
||||
rtree.query(bgi::nearest(mk_rtree_point(pt), 1), std::back_inserter(closest));
|
||||
const Polyline *other = &lines[closest.front().second];
|
||||
double dist2_front = (other->points.front() - pt).cast<double>().squaredNorm();
|
||||
double dist2_back = (other->points.back() - pt).cast<double>().squaredNorm();
|
||||
double dist2_min = std::min(dist2_front, dist2_back);
|
||||
if (dist2_min < r2_close) {
|
||||
// Don't connect the segments in an opposite direction.
|
||||
double dist2_min_other = std::min((other->points.front() - pt_other).cast<double>().squaredNorm(), (other->points.back() - pt_other).cast<double>().squaredNorm());
|
||||
if (dist2_min_other > dist2_min) {
|
||||
// End points of the two lines are very close, they should have been merged together if they are collinear.
|
||||
Vec2d v1 = (pt_other - pt).cast<double>();
|
||||
Vec2d v2 = (other->points.back() - other->points.front()).cast<double>();
|
||||
Vec2d v1n = v1.normalized();
|
||||
Vec2d v2n = v2.normalized();
|
||||
// The vectors must not be collinear.
|
||||
double d = v1n.dot(v2n);
|
||||
if (std::abs(d) > 0.99f) {
|
||||
// Lines are collinear, merge them.
|
||||
rtree.remove(closest.front());
|
||||
return std::make_pair(const_cast<Polyline*>(other), dist2_min == dist2_front);
|
||||
} else {
|
||||
if (polyline > other)
|
||||
std::swap(polyline, other);
|
||||
lines_touching_at_endpoints.emplace_back(polyline, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_pair(static_cast<Polyline*>(nullptr), false);
|
||||
};
|
||||
auto collinear_front = collinear_segment(poly.points.front(), poly.points.back(), &poly);
|
||||
auto collinear_back = collinear_segment(poly.points.back(), poly.points.front(), &poly);
|
||||
assert(! collinear_front.first || ! collinear_back.first || collinear_front.first != collinear_back.first);
|
||||
if (collinear_front.first) {
|
||||
Polyline &other = *collinear_front.first;
|
||||
assert(&other != &poly);
|
||||
poly.points.front() = collinear_front.second ? other.points.back() : other.points.front();
|
||||
other.points.clear();
|
||||
}
|
||||
if (collinear_back.first) {
|
||||
Polyline &other = *collinear_back.first;
|
||||
assert(&other != &poly);
|
||||
poly.points.back() = collinear_back.second ? other.points.back() : other.points.front();
|
||||
other.points.clear();
|
||||
}
|
||||
}
|
||||
rtree.insert(std::make_pair(mk_rtree_seg(poly.points.front(), poly.points.back()), poly_idx++));
|
||||
}
|
||||
}
|
||||
|
||||
// Convert input polylines to lines_src after the colinear segments were merged.
|
||||
Lines lines_src;
|
||||
lines_src.reserve(lines.size());
|
||||
std::transform(lines.begin(), lines.end(), std::back_inserter(lines_src), [](const Polyline &pl) {
|
||||
return pl.empty() ? Line(Point(0, 0), Point(0, 0)) : Line(pl.points.front(), pl.points.back()); });
|
||||
|
||||
sort_remove_duplicates(lines_touching_at_endpoints);
|
||||
|
||||
std::vector<Intersection> intersections;
|
||||
{
|
||||
// Minimum lenght of an infill line to anchor. Very short lines cannot be trimmed from both sides,
|
||||
// it does not help to anchor extremely short infill lines, it consumes too much plastic while not adding
|
||||
// to the object rigidity.
|
||||
assert(scaled_offset > scaled_trim_distance);
|
||||
const double line_len_threshold_drop_both_sides = scaled_offset * (2. / cos(PI / 6.) + 0.5) + SCALED_EPSILON;
|
||||
const double line_len_threshold_anchor_both_sides = line_len_threshold_drop_both_sides + scaled_offset;
|
||||
const double line_len_threshold_drop_single_side = scaled_offset * (1. / cos(PI / 6.) + 1.5) + SCALED_EPSILON;
|
||||
const double line_len_threshold_anchor_single_side = line_len_threshold_drop_single_side + scaled_offset;
|
||||
for (size_t line_idx = 0; line_idx < lines.size(); ++ line_idx) {
|
||||
Polyline &line = lines[line_idx];
|
||||
if (line.points.empty())
|
||||
continue;
|
||||
|
||||
Point &front_point = line.points.front();
|
||||
Point &back_point = line.points.back();
|
||||
|
||||
// Find the nearest line from the start point of the line.
|
||||
std::optional<size_t> tjoint_front, tjoint_back;
|
||||
{
|
||||
auto has_tjoint = [&closest, line_idx, &rtree, &lines, &lines_src](const Point &pt) {
|
||||
auto filter_t_joint = [line_idx, &lines_src, pt](const auto &item) {
|
||||
if (item.second != line_idx) {
|
||||
// Verify that the point projects onto the line.
|
||||
const Line &line = lines_src[item.second];
|
||||
const Vec2d v = (line.b - line.a).cast<double>();
|
||||
const Vec2d va = (pt - line.a).cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
if (l2 > 0.) {
|
||||
const double t = va.dot(v);
|
||||
return t > SCALED_EPSILON && t < l2 - SCALED_EPSILON;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
closest.clear();
|
||||
rtree.query(bgi::nearest(mk_rtree_point(pt), 1) && bgi::satisfies(filter_t_joint), std::back_inserter(closest));
|
||||
std::optional<size_t> out;
|
||||
if (! closest.empty()) {
|
||||
const Polyline &pl = lines[closest.front().second];
|
||||
if (pl.points.empty()) {
|
||||
// The closest infill line was already dropped as it was too short.
|
||||
// Such an infill line should not make a T-joint anyways.
|
||||
#if 0 // #ifndef NDEBUG
|
||||
const auto &seg = closest.front().first;
|
||||
struct Linef { Vec2d a; Vec2d b; };
|
||||
Linef l { { bg::get<0, 0>(seg), bg::get<0, 1>(seg) }, { bg::get<1, 0>(seg), bg::get<1, 1>(seg) } };
|
||||
assert(line_alg::distance_to_squared(l, Vec2d(pt.cast<double>())) > 1000 * 1000);
|
||||
#endif // NDEBUG
|
||||
} else if (((Line)pl).distance_to_squared(pt) <= 1000 * 1000)
|
||||
out = closest.front().second;
|
||||
}
|
||||
return out;
|
||||
};
|
||||
// Refuse to create a T-joint if the infill lines touch at their ends.
|
||||
auto filter_end_point_connections = [&lines_touching_at_endpoints, &lines, &line](std::optional<size_t> in) {
|
||||
std::optional<size_t> out;
|
||||
if (in) {
|
||||
const Polyline *lo = &line;
|
||||
const Polyline *hi = &lines[*in];
|
||||
if (lo > hi)
|
||||
std::swap(lo, hi);
|
||||
if (! std::binary_search(lines_touching_at_endpoints.begin(), lines_touching_at_endpoints.end(), std::make_pair(lo, hi)))
|
||||
// Not an end-point connection, it is a valid T-joint.
|
||||
out = in;
|
||||
}
|
||||
return out;
|
||||
};
|
||||
tjoint_front = filter_end_point_connections(has_tjoint(front_point));
|
||||
tjoint_back = filter_end_point_connections(has_tjoint(back_point));
|
||||
}
|
||||
|
||||
int num_tjoints = int(tjoint_front.has_value()) + int(tjoint_back.has_value());
|
||||
if (num_tjoints > 0) {
|
||||
double line_len = line.length();
|
||||
bool drop = false;
|
||||
bool anchor = false;
|
||||
if (num_tjoints == 1) {
|
||||
// Connected to perimeters on a single side only, connected to another infill line on the other side.
|
||||
drop = line_len < line_len_threshold_drop_single_side;
|
||||
anchor = line_len > line_len_threshold_anchor_single_side;
|
||||
} else {
|
||||
// Not connected to perimeters at all, connected to two infill lines.
|
||||
assert(num_tjoints == 2);
|
||||
drop = line_len < line_len_threshold_drop_both_sides;
|
||||
anchor = line_len > line_len_threshold_anchor_both_sides;
|
||||
}
|
||||
if (drop) {
|
||||
// Drop a very short line if connected to another infill line.
|
||||
// Lines shorter than spacing are skipped because it is needed to shrink a line by the value of spacing.
|
||||
// A shorter line than spacing could produce a degenerate polyline.
|
||||
line.points.clear();
|
||||
} else if (anchor) {
|
||||
if (tjoint_front) {
|
||||
// T-joint of line's front point with the 'closest' line.
|
||||
intersections.emplace_back(lines_src[*tjoint_front], lines_src[line_idx], &line, front_point, true);
|
||||
assert(validate_intersection_t_joint(intersections.back()));
|
||||
}
|
||||
if (tjoint_back) {
|
||||
// T-joint of line's back point with the 'closest' line.
|
||||
intersections.emplace_back(lines_src[*tjoint_back], lines_src[line_idx], &line, back_point, false);
|
||||
assert(validate_intersection_t_joint(intersections.back()));
|
||||
}
|
||||
} else {
|
||||
if (tjoint_front)
|
||||
// T joint at the front at a 60 degree angle, the line is very short.
|
||||
// Trim the front side.
|
||||
front_point += ((scaled_trim_distance * 1.155) * (back_point - front_point).cast<double>().normalized()).cast<coord_t>();
|
||||
if (tjoint_back)
|
||||
// T joint at the front at a 60 degree angle, the line is very short.
|
||||
// Trim the front side.
|
||||
back_point += ((scaled_trim_distance * 1.155) * (front_point - back_point).cast<double>().normalized()).cast<coord_t>();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove those intersections, that point to a dropped line.
|
||||
for (auto it = intersections.begin(); it != intersections.end(); ) {
|
||||
assert(! lines[it->intersect_line - lines_src.data()].points.empty());
|
||||
if (lines[it->closest_line - lines_src.data()].points.empty()) {
|
||||
*it = intersections.back();
|
||||
intersections.pop_back();
|
||||
} else
|
||||
++ it;
|
||||
}
|
||||
}
|
||||
assert(validate_intersections(intersections));
|
||||
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
static int iRun = 0;
|
||||
int iStep = 0;
|
||||
{
|
||||
Points pts;
|
||||
for (const Intersection &i : intersections)
|
||||
pts.emplace_back(i.intersect_point);
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-Tjoints-%d.svg", iRun++), pts);
|
||||
}
|
||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
||||
|
||||
// Sort lexicographically by closest_line_idx and left/right orientation.
|
||||
std::sort(intersections.begin(), intersections.end(),
|
||||
[](const Intersection &i1, const Intersection &i2) {
|
||||
return (i1.closest_line == i2.closest_line) ?
|
||||
int(i1.left) < int(i2.left) :
|
||||
i1.closest_line < i2.closest_line;
|
||||
});
|
||||
|
||||
std::vector<size_t> merged_with(lines.size());
|
||||
std::iota(merged_with.begin(), merged_with.end(), 0);
|
||||
|
||||
// Appends the boundary polygon with all holes to rtree for detection to check whether hooks are not crossing the boundary
|
||||
{
|
||||
Point prev = boundary.contour.points.back();
|
||||
for (const Point &point : boundary.contour.points) {
|
||||
rtree.insert(std::make_pair(mk_rtree_seg(prev, point), poly_idx++));
|
||||
prev = point;
|
||||
}
|
||||
for (const Polygon &polygon : boundary.holes) {
|
||||
Point prev = polygon.points.back();
|
||||
for (const Point &point : polygon.points) {
|
||||
rtree.insert(std::make_pair(mk_rtree_seg(prev, point), poly_idx++));
|
||||
prev = point;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto update_merged_polyline_idx = [&merged_with](size_t pl_idx) {
|
||||
// Update the polyline index to index which is merged
|
||||
for (size_t last = pl_idx;;) {
|
||||
size_t lower = merged_with[last];
|
||||
if (lower == last) {
|
||||
merged_with[pl_idx] = lower;
|
||||
return lower;
|
||||
}
|
||||
last = lower;
|
||||
}
|
||||
assert(false);
|
||||
return size_t(0);
|
||||
};
|
||||
auto update_merged_polyline = [&lines, update_merged_polyline_idx](Intersection& intersection) {
|
||||
// Update the polyline index to index which is merged
|
||||
size_t intersect_pl_idx = update_merged_polyline_idx(intersection.intersect_pl - lines.data());
|
||||
intersection.intersect_pl = &lines[intersect_pl_idx];
|
||||
// After polylines are merged, it is necessary to update "forward" based on if intersect_point is the first or the last point of intersect_pl.
|
||||
if (intersection.fresh()) {
|
||||
assert(intersection.intersect_pl->points.front() == intersection.intersect_point ||
|
||||
intersection.intersect_pl->points.back() == intersection.intersect_point);
|
||||
intersection.front = intersection.intersect_pl->points.front() == intersection.intersect_point;
|
||||
}
|
||||
};
|
||||
|
||||
// Merge polylines touching at their ends. This should be a very rare case, but it happens surprisingly often.
|
||||
for (auto it = lines_touching_at_endpoints.rbegin(); it != lines_touching_at_endpoints.rend(); ++ it) {
|
||||
Polyline *pl1 = const_cast<Polyline*>(it->first);
|
||||
Polyline *pl2 = const_cast<Polyline*>(it->second);
|
||||
assert(pl1 < pl2);
|
||||
// pl1 was visited for the 1st time.
|
||||
// pl2 may have alread been merged with another polyline, even with this one.
|
||||
pl2 = &lines[update_merged_polyline_idx(pl2 - lines.data())];
|
||||
assert(pl1 <= pl2);
|
||||
// Avoid closing a loop, ignore dropped infill lines.
|
||||
if (pl1 != pl2 && ! pl1->points.empty() && ! pl2->points.empty()) {
|
||||
// Merge the polylines.
|
||||
assert(pl1 < pl2);
|
||||
assert(pl1->points.size() >= 2);
|
||||
assert(pl2->points.size() >= 2);
|
||||
double d11 = (pl1->points.front() - pl2->points.front()).cast<double>().squaredNorm();
|
||||
double d12 = (pl1->points.front() - pl2->points.back()) .cast<double>().squaredNorm();
|
||||
double d21 = (pl1->points.back() - pl2->points.front()).cast<double>().squaredNorm();
|
||||
double d22 = (pl1->points.back() - pl2->points.back()) .cast<double>().squaredNorm();
|
||||
double d1min = std::min(d11, d12);
|
||||
double d2min = std::min(d21, d22);
|
||||
if (d1min < d2min) {
|
||||
pl1->reverse();
|
||||
if (d12 == d1min)
|
||||
pl2->reverse();
|
||||
} else if (d22 == d2min)
|
||||
pl2->reverse();
|
||||
pl1->points.back() = (pl1->points.back() + pl2->points.front()) / 2;
|
||||
pl1->append(pl2->points.begin() + 1, pl2->points.end());
|
||||
pl2->points.clear();
|
||||
merged_with[pl2 - lines.data()] = pl1 - lines.data();
|
||||
}
|
||||
}
|
||||
|
||||
// Keep intersect_line outside the loop, so it does not get reallocated.
|
||||
std::vector<std::pair<Intersection*, double>> intersect_line;
|
||||
for (size_t min_idx = 0; min_idx < intersections.size();) {
|
||||
intersect_line.clear();
|
||||
// All the nearest points (T-joints) ending at the same line are projected onto this line. Because of it, it can easily find the nearest point.
|
||||
{
|
||||
const Vec2d line_dir = intersections[min_idx].closest_line->vector().cast<double>();
|
||||
size_t max_idx = min_idx;
|
||||
for (; max_idx < intersections.size() &&
|
||||
intersections[min_idx].closest_line == intersections[max_idx].closest_line &&
|
||||
intersections[min_idx].left == intersections[max_idx].left;
|
||||
++ max_idx)
|
||||
intersect_line.emplace_back(&intersections[max_idx], line_dir.dot(intersections[max_idx].intersect_point.cast<double>()));
|
||||
min_idx = max_idx;
|
||||
assert(intersect_line.size() > 0);
|
||||
// Sort the intersections along line_dir.
|
||||
std::sort(intersect_line.begin(), intersect_line.end(), [](const auto &i1, const auto &i2) { return i1.second < i2.second; });
|
||||
}
|
||||
|
||||
if (intersect_line.size() == 1) {
|
||||
// Simple case: The current intersection is the only one touching its adjacent line.
|
||||
Intersection &first_i = *intersect_line.front().first;
|
||||
update_merged_polyline(first_i);
|
||||
if (first_i.fresh()) {
|
||||
// Try to connect left or right. If not enough space for hook_length, take the longer side.
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook0-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
|
||||
#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
add_hook(first_i, scaled_offset, hook_length, scaled_trim_distance, rtree, lines_src);
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook0-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
|
||||
++ iStep;
|
||||
#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
first_i.used = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t first_idx = 0; first_idx < intersect_line.size(); ++ first_idx) {
|
||||
Intersection &first_i = *intersect_line[first_idx].first;
|
||||
update_merged_polyline(first_i);
|
||||
if (! first_i.fresh())
|
||||
// The intersection has been processed, or the polyline has been merged to another polyline.
|
||||
continue;
|
||||
|
||||
// Get the previous or next intersection on the same line, pick the closer one.
|
||||
if (first_idx > 0)
|
||||
update_merged_polyline(*intersect_line[first_idx - 1].first);
|
||||
if (first_idx + 1 < intersect_line.size())
|
||||
update_merged_polyline(*intersect_line[first_idx + 1].first);
|
||||
Intersection &nearest_i = *get_nearest_intersection(intersect_line, first_idx);
|
||||
assert(first_i.closest_line == nearest_i.closest_line);
|
||||
assert(first_i.intersect_line != nearest_i.intersect_line);
|
||||
assert(first_i.intersect_line != first_i.closest_line);
|
||||
assert(nearest_i.intersect_line != first_i.closest_line);
|
||||
// A line between two intersections points
|
||||
Line offset_line = create_offset_line(Line(first_i.intersect_point, nearest_i.intersect_point), first_i, scaled_offset);
|
||||
// Check if both intersections lie on the offset_line and simultaneously get their points of intersecting.
|
||||
// These points are used as start and end of the hook
|
||||
Point first_i_point, nearest_i_point;
|
||||
bool could_connect = false;
|
||||
if (nearest_i.fresh()) {
|
||||
could_connect = first_i.intersect_line->intersection(offset_line, &first_i_point) &&
|
||||
nearest_i.intersect_line->intersection(offset_line, &nearest_i_point);
|
||||
assert(could_connect);
|
||||
}
|
||||
Points &first_points = first_i.intersect_pl->points;
|
||||
Points &second_points = nearest_i.intersect_pl->points;
|
||||
could_connect &= (nearest_i_point - first_i_point).cast<double>().squaredNorm() <= Slic3r::sqr(hook_length_max);
|
||||
if (could_connect) {
|
||||
// Both intersections are so close that their polylines can be connected.
|
||||
// Verify that no other infill line intersects this anchor line.
|
||||
closest.clear();
|
||||
rtree.query(
|
||||
bgi::intersects(mk_rtree_seg(first_i_point, nearest_i_point)) &&
|
||||
bgi::satisfies([&first_i, &nearest_i, &lines_src](const auto &item)
|
||||
{ return item.second != first_i.intersect_line - lines_src.data() && item.second != nearest_i.intersect_line - lines_src.data(); }),
|
||||
std::back_inserter(closest));
|
||||
could_connect = closest.empty();
|
||||
#if 0
|
||||
// Avoid self intersections. Maybe it is better to trim the self intersection after the connection?
|
||||
if (could_connect && first_i.intersect_pl != nearest_i.intersect_pl) {
|
||||
Line l(first_i_point, nearest_i_point);
|
||||
could_connect = ! first_i.other_hook_intersects(l) && ! nearest_i.other_hook_intersects(l);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
bool connected = false;
|
||||
if (could_connect) {
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-connecting-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point, nearest_i.intersect_point });
|
||||
#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
// No other infill line intersects this anchor line. Extrude it as a whole.
|
||||
if (first_i.intersect_pl == nearest_i.intersect_pl) {
|
||||
// Both intersections are on the same polyline, that means a loop is being closed.
|
||||
assert(first_i.front != nearest_i.front);
|
||||
if (! first_i.front)
|
||||
std::swap(first_i_point, nearest_i_point);
|
||||
first_points.front() = first_i_point;
|
||||
first_points.back() = nearest_i_point;
|
||||
//FIXME trim the end of a closed loop a bit?
|
||||
first_points.emplace(first_points.begin(), nearest_i_point);
|
||||
} else {
|
||||
// Both intersections are on different polylines
|
||||
Line l(first_i_point, nearest_i_point);
|
||||
l.translate((perp(first_i.closest_line->vector().cast<double>().normalized()) * (first_i.left ? scaled_trim_distance : - scaled_trim_distance)).cast<coord_t>());
|
||||
Point pt_start, pt_end;
|
||||
bool trim_start = first_i .intersect_pl->points.size() == 3 && first_i .other_hook_intersects(l, pt_start);
|
||||
bool trim_end = nearest_i.intersect_pl->points.size() == 3 && nearest_i.other_hook_intersects(l, pt_end);
|
||||
first_points.reserve(first_points.size() + second_points.size());
|
||||
if (first_i.front)
|
||||
std::reverse(first_points.begin(), first_points.end());
|
||||
if (trim_start)
|
||||
first_points.front() = pt_start;
|
||||
first_points.back() = first_i_point;
|
||||
first_points.emplace_back(nearest_i_point);
|
||||
if (nearest_i.front)
|
||||
first_points.insert(first_points.end(), second_points.begin() + 1, second_points.end());
|
||||
else
|
||||
first_points.insert(first_points.end(), second_points.rbegin() + 1, second_points.rend());
|
||||
if (trim_end)
|
||||
first_points.back() = pt_end;
|
||||
// Keep the polyline at the lower index slot.
|
||||
if (first_i.intersect_pl < nearest_i.intersect_pl) {
|
||||
second_points.clear();
|
||||
merged_with[nearest_i.intersect_pl - lines.data()] = first_i.intersect_pl - lines.data();
|
||||
} else {
|
||||
second_points = std::move(first_points);
|
||||
first_points.clear();
|
||||
merged_with[first_i.intersect_pl - lines.data()] = nearest_i.intersect_pl - lines.data();
|
||||
}
|
||||
}
|
||||
nearest_i.used = true;
|
||||
connected = true;
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-connecting-post-%d-%d.svg", iRun, iStep), { first_i.intersect_point, nearest_i.intersect_point });
|
||||
#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
}
|
||||
if (! connected) {
|
||||
// Try to connect left or right. If not enough space for hook_length, take the longer side.
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
|
||||
#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
add_hook(first_i, scaled_offset, hook_length, scaled_trim_distance, rtree, lines_src);
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook-post-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
|
||||
#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
}
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
++ iStep;
|
||||
#endif ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
first_i.used = true;
|
||||
}
|
||||
}
|
||||
|
||||
Polylines polylines_out;
|
||||
polylines_out.reserve(polylines_out.size() + std::count_if(lines.begin(), lines.end(), [](const Polyline &pl) { return !pl.empty(); }));
|
||||
for (Polyline &pl : lines)
|
||||
if (!pl.empty()) polylines_out.emplace_back(std::move(pl));
|
||||
return polylines_out;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool has_no_collinear_lines(const Polylines &polylines)
|
||||
{
|
||||
// Create line end point lookup.
|
||||
struct LineEnd {
|
||||
LineEnd(const Polyline *line, bool start) : line(line), start(start) {}
|
||||
const Polyline *line;
|
||||
// Is it the start or end point?
|
||||
bool start;
|
||||
const Point& point() const { return start ? line->points.front() : line->points.back(); }
|
||||
const Point& other_point() const { return start ? line->points.back() : line->points.front(); }
|
||||
LineEnd other_end() const { return LineEnd(line, !start); }
|
||||
Vec2d vec() const { return Vec2d((this->other_point() - this->point()).cast<double>()); }
|
||||
bool operator==(const LineEnd &rhs) const { return this->line == rhs.line && this->start == rhs.start; }
|
||||
};
|
||||
struct LineEndAccessor {
|
||||
const Point* operator()(const LineEnd &pt) const { return &pt.point(); }
|
||||
};
|
||||
typedef ClosestPointInRadiusLookup<LineEnd, LineEndAccessor> ClosestPointLookupType;
|
||||
ClosestPointLookupType closest_end_point_lookup(coord_t(1001. * sqrt(2.)));
|
||||
for (const Polyline& pl : polylines) {
|
||||
// assert(pl.points.size() == 2);
|
||||
auto line_start = LineEnd(&pl, true);
|
||||
auto line_end = LineEnd(&pl, false);
|
||||
|
||||
auto assert_not_collinear = [&closest_end_point_lookup](const LineEnd &line_start) {
|
||||
std::vector<std::pair<const LineEnd*, double>> hits = closest_end_point_lookup.find_all(line_start.point());
|
||||
for (const std::pair<const LineEnd*, double> &hit : hits)
|
||||
if ((line_start.point() - hit.first->point()).cwiseAbs().maxCoeff() <= 1000) {
|
||||
// End points of the two lines are very close, they should have been merged together if they are collinear.
|
||||
Vec2d v1 = line_start.vec();
|
||||
Vec2d v2 = hit.first->vec();
|
||||
Vec2d v1n = v1.normalized();
|
||||
Vec2d v2n = v2.normalized();
|
||||
// The vectors must not be collinear.
|
||||
assert(std::abs(v1n.dot(v2n)) < cos(M_PI / 12.));
|
||||
}
|
||||
};
|
||||
assert_not_collinear(line_start);
|
||||
assert_not_collinear(line_end);
|
||||
|
||||
closest_end_point_lookup.insert(line_start);
|
||||
closest_end_point_lookup.insert(line_end);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Filler::_fill_surface_single(
|
||||
const FillParams & params,
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
assert (this->adapt_fill_octree);
|
||||
|
|
@ -569,6 +1335,23 @@ void Filler::_fill_surface_single(
|
|||
generate_infill_lines_recursive(context, adapt_fill_octree->root_cube, 0, int(adapt_fill_octree->cubes_properties.size()) - 1);
|
||||
num_lines += context.output_lines.size() + context.temp_lines.size();
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Collect the lines, trim them by the expolygon.
|
||||
all_polylines.reserve(num_lines);
|
||||
auto boundary = to_polygons(expolygon);
|
||||
for (auto &context : contexts) {
|
||||
Polylines lines;
|
||||
lines.reserve(context.output_lines.size() + context.temp_lines.size());
|
||||
std::transform(context.output_lines.begin(), context.output_lines.end(), std::back_inserter(lines), [](const Line& l) { return Polyline{ l.a, l.b }; });
|
||||
for (const Line &l : context.temp_lines)
|
||||
if (l.a.x() != std::numeric_limits<coord_t>::max())
|
||||
lines.push_back({ l.a, l.b });
|
||||
// Crop all polylines
|
||||
append(all_polylines, intersection_pl(std::move(lines), boundary));
|
||||
}
|
||||
// assert(has_no_collinear_lines(all_polylines));
|
||||
#else
|
||||
// Collect the lines.
|
||||
std::vector<Line> lines;
|
||||
lines.reserve(num_lines);
|
||||
|
|
@ -578,18 +1361,21 @@ void Filler::_fill_surface_single(
|
|||
if (line.a.x() != std::numeric_limits<coord_t>::max())
|
||||
lines.emplace_back(line);
|
||||
}
|
||||
#if 0
|
||||
// Chain touching line segments, convert lines to polylines.
|
||||
//all_polylines = chain_lines(lines, 300.); // SCALED_EPSILON is 100 and it is not enough
|
||||
#else
|
||||
// Convert lines to polylines.
|
||||
all_polylines.reserve(lines.size());
|
||||
std::transform(lines.begin(), lines.end(), std::back_inserter(all_polylines), [](const Line& l) { return Polyline{ l.a, l.b }; });
|
||||
// Crop all polylines
|
||||
all_polylines = intersection_pl(std::move(all_polylines), to_polygons(expolygon));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Crop all polylines
|
||||
all_polylines = intersection_pl(std::move(all_polylines), to_polygons(expolygon));
|
||||
// After intersection_pl some polylines with only one line are split into more lines
|
||||
for (Polyline &polyline : all_polylines) {
|
||||
//FIXME assert that all the points are collinear and in between the start and end point.
|
||||
if (polyline.points.size() > 2)
|
||||
polyline.points.erase(polyline.points.begin() + 1, polyline.points.end() - 1);
|
||||
}
|
||||
// assert(has_no_collinear_lines(all_polylines));
|
||||
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
{
|
||||
|
|
@ -598,10 +1384,22 @@ void Filler::_fill_surface_single(
|
|||
}
|
||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
||||
|
||||
if (params.dont_connect || all_polylines.size() <= 1)
|
||||
append(polylines_out, std::move(all_polylines));
|
||||
const auto hook_length = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length)));
|
||||
const auto hook_length_max = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length_max)));
|
||||
|
||||
Polylines all_polylines_with_hooks = all_polylines.size() > 1 ? connect_lines_using_hooks(std::move(all_polylines), expolygon, this->spacing, hook_length, hook_length_max) : std::move(all_polylines);
|
||||
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
{
|
||||
static int iRun = 0;
|
||||
export_infill_lines_to_svg(expolygon, all_polylines_with_hooks, debug_out_path("FillAdaptive-hooks-%d.svg", iRun++));
|
||||
}
|
||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
||||
|
||||
if (params.dont_connect() || all_polylines_with_hooks.size() <= 1)
|
||||
append(polylines_out, chain_polylines(std::move(all_polylines_with_hooks)));
|
||||
else
|
||||
connect_infill(chain_polylines(std::move(all_polylines)), expolygon, polylines_out, this->spacing, params);
|
||||
connect_infill(std::move(all_polylines_with_hooks), expolygon, polylines_out, this->spacing, params);
|
||||
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,17 +56,17 @@ FillAdaptive::OctreePtr build_octree(
|
|||
class Filler : public Slic3r::Fill
|
||||
{
|
||||
public:
|
||||
virtual ~Filler() {}
|
||||
~Filler() override {}
|
||||
|
||||
protected:
|
||||
virtual Fill* clone() const { return new Filler(*this); };
|
||||
virtual void _fill_surface_single(
|
||||
Fill* clone() const override { return new Filler(*this); };
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
virtual bool no_sort() const { return true; }
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
bool no_sort() const override { return true; }
|
||||
};
|
||||
|
||||
}; // namespace FillAdaptive
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -33,12 +33,16 @@ public:
|
|||
struct FillParams
|
||||
{
|
||||
bool full_infill() const { return density > 0.9999f; }
|
||||
// Don't connect the fill lines around the inner perimeter.
|
||||
bool dont_connect() const { return anchor_length_max < 0.05f; }
|
||||
|
||||
// Fill density, fraction in <0, 1>
|
||||
float density { 0.f };
|
||||
|
||||
// Don't connect the fill lines around the inner perimeter.
|
||||
bool dont_connect { false };
|
||||
// Length of an infill anchor along the perimeter.
|
||||
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
|
||||
float anchor_length { 1000.f };
|
||||
float anchor_length_max { 1000.f };
|
||||
|
||||
// Don't adjust spacing to fill the space evenly.
|
||||
bool dont_adjust { true };
|
||||
|
|
@ -80,6 +84,7 @@ public:
|
|||
|
||||
public:
|
||||
virtual ~Fill() {}
|
||||
virtual Fill* clone() const = 0;
|
||||
|
||||
static Fill* new_from_type(const InfillPattern type);
|
||||
static Fill* new_from_type(const std::string &type);
|
||||
|
|
@ -116,7 +121,7 @@ protected:
|
|||
const FillParams & /* params */,
|
||||
unsigned int /* thickness_layers */,
|
||||
const std::pair<float, Point> & /* direction */,
|
||||
ExPolygon & /* expolygon */,
|
||||
ExPolygon /* expolygon */,
|
||||
Polylines & /* polylines_out */) {};
|
||||
|
||||
virtual float _layer_angle(size_t idx) const { return (idx & 1) ? float(M_PI/2.) : 0; }
|
||||
|
|
@ -124,7 +129,9 @@ protected:
|
|||
virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;
|
||||
|
||||
public:
|
||||
static void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, double spacing, const FillParams ¶ms);
|
||||
static void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const double spacing, const FillParams ¶ms);
|
||||
static void connect_infill(Polylines &&infill_ordered, const Polygons &boundary, const BoundingBox& bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms);
|
||||
static void connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary, const BoundingBox &bbox, Polylines &polylines_out, double spacing, const FillParams ¶ms);
|
||||
|
||||
static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ void FillConcentric::_fill_surface_single(
|
|||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// no rotation is supported for this infill pattern
|
||||
|
|
@ -39,7 +39,7 @@ void FillConcentric::_fill_surface_single(
|
|||
size_t iPathFirst = polylines_out.size();
|
||||
Point last_pos(0, 0);
|
||||
for (const Polygon &loop : loops) {
|
||||
polylines_out.push_back(loop.split_at_index(last_pos.nearest_point_index(loop)));
|
||||
polylines_out.push_back(loop.split_at_index(last_pos.nearest_point_index(loop.points)));
|
||||
last_pos = polylines_out.back().last_point();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,18 +8,18 @@ namespace Slic3r {
|
|||
class FillConcentric : public Fill
|
||||
{
|
||||
public:
|
||||
virtual ~FillConcentric() {}
|
||||
~FillConcentric() override {}
|
||||
|
||||
protected:
|
||||
virtual Fill* clone() const { return new FillConcentric(*this); };
|
||||
virtual void _fill_surface_single(
|
||||
Fill* clone() const override { return new FillConcentric(*this); };
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
|
||||
virtual bool no_sort() const { return true; }
|
||||
bool no_sort() const override { return true; }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ static inline Polyline make_wave(
|
|||
double period = points.back()(0);
|
||||
if (width != period) // do not extend if already truncated
|
||||
{
|
||||
points.reserve(one_period.size() * floor(width / period));
|
||||
points.reserve(one_period.size() * size_t(floor(width / period)));
|
||||
points.pop_back();
|
||||
|
||||
int n = points.size();
|
||||
size_t n = points.size();
|
||||
do {
|
||||
points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1)));
|
||||
points.emplace_back(points[points.size()-n].x() + period, points[points.size()-n].y());
|
||||
} while (points.back()(0) < width - EPSILON);
|
||||
|
||||
points.emplace_back(Vec2d(width, f(width, z_sin, z_cos, vertical, flip)));
|
||||
|
|
@ -67,7 +67,7 @@ static std::vector<Vec2d> make_one_period(double width, double scaleFactor, doub
|
|||
std::vector<Vec2d> points;
|
||||
double dx = M_PI_2; // exact coordinates on main inflexion lobes
|
||||
double limit = std::min(2*M_PI, width);
|
||||
points.reserve(ceil(limit / tolerance / 3));
|
||||
points.reserve(coord_t(ceil(limit / tolerance / 3)));
|
||||
|
||||
for (double x = 0.; x < limit - EPSILON; x += dx) {
|
||||
points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
|
||||
|
|
@ -152,10 +152,10 @@ void FillGyroid::_fill_surface_single(
|
|||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
float infill_angle = this->angle + (CorrectionAngle * 2*M_PI) / 360.;
|
||||
auto infill_angle = float(this->angle + (CorrectionAngle * 2*M_PI) / 360.);
|
||||
if(std::abs(infill_angle) >= EPSILON)
|
||||
expolygon.rotate(-infill_angle);
|
||||
|
||||
|
|
@ -182,19 +182,20 @@ void FillGyroid::_fill_surface_single(
|
|||
|
||||
polylines = intersection_pl(polylines, to_polygons(expolygon));
|
||||
|
||||
if (! polylines.empty())
|
||||
// remove too small bits (larger than longer)
|
||||
if (! polylines.empty()) {
|
||||
// Remove very small bits, but be careful to not remove infill lines connecting thin walls!
|
||||
// The infill perimeter lines should be separated by around a single infill line width.
|
||||
const double minlength = scale_(0.8 * this->spacing);
|
||||
polylines.erase(
|
||||
//FIXME what is the small size? Removing tiny extrusions disconnects walls!
|
||||
std::remove_if(polylines.begin(), polylines.end(), [this](const Polyline &pl) { return pl.length() < scale_(this->spacing * 3); }),
|
||||
std::remove_if(polylines.begin(), polylines.end(), [minlength](const Polyline &pl) { return pl.length() < minlength; }),
|
||||
polylines.end());
|
||||
}
|
||||
|
||||
if (! polylines.empty()) {
|
||||
polylines = chain_polylines(polylines);
|
||||
// connect lines
|
||||
size_t polylines_out_first_idx = polylines_out.size();
|
||||
if (params.dont_connect)
|
||||
append(polylines_out, std::move(polylines));
|
||||
if (params.dont_connect())
|
||||
append(polylines_out, chain_polylines(polylines));
|
||||
else
|
||||
this->connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ class FillGyroid : public Fill
|
|||
{
|
||||
public:
|
||||
FillGyroid() {}
|
||||
virtual Fill* clone() const { return new FillGyroid(*this); }
|
||||
Fill* clone() const override { return new FillGyroid(*this); }
|
||||
|
||||
// require bridge flow since most of this pattern hangs in air
|
||||
virtual bool use_bridge_flow() const { return false; }
|
||||
bool use_bridge_flow() const override { return false; }
|
||||
|
||||
// Correction applied to regular infill angle to maximize printing
|
||||
// speed in default configuration (degrees)
|
||||
|
|
@ -28,12 +28,12 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
virtual void _fill_surface_single(
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ void FillHoneycomb::_fill_surface_single(
|
|||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// cache hexagons math
|
||||
|
|
@ -18,21 +18,21 @@ void FillHoneycomb::_fill_surface_single(
|
|||
Cache::iterator it_m = this->cache.find(cache_id);
|
||||
if (it_m == this->cache.end()) {
|
||||
it_m = this->cache.insert(it_m, std::pair<CacheID, CacheData>(cache_id, CacheData()));
|
||||
CacheData &m = it_m->second;
|
||||
coord_t min_spacing = scale_(this->spacing);
|
||||
m.distance = min_spacing / params.density;
|
||||
m.hex_side = m.distance / (sqrt(3)/2);
|
||||
m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3);
|
||||
coord_t hex_height = m.hex_side * 2;
|
||||
m.pattern_height = hex_height + m.hex_side;
|
||||
m.y_short = m.distance * sqrt(3)/3;
|
||||
m.x_offset = min_spacing / 2;
|
||||
m.y_offset = m.x_offset * sqrt(3)/3;
|
||||
m.hex_center = Point(m.hex_width/2, m.hex_side);
|
||||
CacheData &m = it_m->second;
|
||||
coord_t min_spacing = coord_t(scale_(this->spacing));
|
||||
m.distance = coord_t(min_spacing / params.density);
|
||||
m.hex_side = coord_t(m.distance / (sqrt(3)/2));
|
||||
m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3);
|
||||
coord_t hex_height = m.hex_side * 2;
|
||||
m.pattern_height = hex_height + m.hex_side;
|
||||
m.y_short = coord_t(m.distance * sqrt(3)/3);
|
||||
m.x_offset = min_spacing / 2;
|
||||
m.y_offset = coord_t(m.x_offset * sqrt(3)/3);
|
||||
m.hex_center = Point(m.hex_width/2, m.hex_side);
|
||||
}
|
||||
CacheData &m = it_m->second;
|
||||
|
||||
Polygons polygons;
|
||||
Polylines all_polylines;
|
||||
{
|
||||
// adjust actual bounding box to the nearest multiple of our hex pattern
|
||||
// and align it so that it matches across layers
|
||||
|
|
@ -52,7 +52,7 @@ void FillHoneycomb::_fill_surface_single(
|
|||
|
||||
coord_t x = bounding_box.min(0);
|
||||
while (x <= bounding_box.max(0)) {
|
||||
Polygon p;
|
||||
Polyline p;
|
||||
coord_t ax[2] = { x + m.x_offset, x + m.distance - m.x_offset };
|
||||
for (size_t i = 0; i < 2; ++ i) {
|
||||
std::reverse(p.points.begin(), p.points.end()); // turn first half upside down
|
||||
|
|
@ -69,55 +69,15 @@ void FillHoneycomb::_fill_surface_single(
|
|||
x += m.distance;
|
||||
}
|
||||
p.rotate(-direction.first, m.hex_center);
|
||||
polygons.push_back(p);
|
||||
all_polylines.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.complete || true) {
|
||||
// we were requested to complete each loop;
|
||||
// in this case we don't try to make more continuous paths
|
||||
Polygons polygons_trimmed = intersection((Polygons)expolygon, polygons);
|
||||
for (Polygons::iterator it = polygons_trimmed.begin(); it != polygons_trimmed.end(); ++ it)
|
||||
polylines_out.push_back(it->split_at_first_point());
|
||||
} else {
|
||||
// consider polygons as polylines without re-appending the initial point:
|
||||
// this cuts the last segment on purpose, so that the jump to the next
|
||||
// path is more straight
|
||||
Polylines paths;
|
||||
{
|
||||
Polylines p;
|
||||
for (Polygon &poly : polygons)
|
||||
p.emplace_back(poly.points);
|
||||
paths = intersection_pl(p, to_polygons(expolygon));
|
||||
}
|
||||
|
||||
// connect paths
|
||||
if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
Polylines chained = chain_polylines(std::move(paths));
|
||||
assert(paths.empty());
|
||||
paths.clear();
|
||||
for (Polyline &path : chained) {
|
||||
if (! paths.empty()) {
|
||||
// distance between first point of this path and last point of last path
|
||||
double distance = (path.first_point() - paths.back().last_point()).cast<double>().norm();
|
||||
if (distance <= m.hex_width) {
|
||||
paths.back().points.insert(paths.back().points.end(), path.points.begin(), path.points.end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Don't connect the paths.
|
||||
paths.push_back(std::move(path));
|
||||
}
|
||||
}
|
||||
|
||||
// clip paths again to prevent connection segments from crossing the expolygon boundaries
|
||||
paths = intersection_pl(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)));
|
||||
// Move the polylines to the output, avoid a deep copy.
|
||||
size_t j = polylines_out.size();
|
||||
polylines_out.resize(j + paths.size(), Polyline());
|
||||
for (size_t i = 0; i < paths.size(); ++ i)
|
||||
std::swap(polylines_out[j ++], paths[i]);
|
||||
}
|
||||
all_polylines = intersection_pl(std::move(all_polylines), to_polygons(expolygon));
|
||||
if (params.dont_connect() || all_polylines.size() <= 1)
|
||||
append(polylines_out, chain_polylines(std::move(all_polylines)));
|
||||
else
|
||||
connect_infill(std::move(all_polylines), expolygon, polylines_out, this->spacing, params);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -12,16 +12,16 @@ namespace Slic3r {
|
|||
class FillHoneycomb : public Fill
|
||||
{
|
||||
public:
|
||||
virtual ~FillHoneycomb() {}
|
||||
~FillHoneycomb() override {}
|
||||
|
||||
protected:
|
||||
virtual Fill* clone() const { return new FillHoneycomb(*this); };
|
||||
virtual void _fill_surface_single(
|
||||
Fill* clone() const override { return new FillHoneycomb(*this); };
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
|
||||
// Caching the
|
||||
struct CacheID
|
||||
|
|
@ -49,7 +49,7 @@ protected:
|
|||
typedef std::map<CacheID, CacheData> Cache;
|
||||
Cache cache;
|
||||
|
||||
virtual float _layer_angle(size_t idx) const { return float(M_PI/3.) * (idx % 3); }
|
||||
float _layer_angle(size_t idx) const override { return float(M_PI/3.) * (idx % 3); }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
122
src/libslic3r/Fill/FillLine.cpp
Normal file
122
src/libslic3r/Fill/FillLine.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../ExPolygon.hpp"
|
||||
#include "../ShortestPath.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillLine.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void FillLine::_fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// rotate polygons so that we can work with vertical lines here
|
||||
expolygon.rotate(- direction.first);
|
||||
|
||||
this->_min_spacing = scale_(this->spacing);
|
||||
assert(params.density > 0.0001f && params.density <= 1.f);
|
||||
this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / params.density);
|
||||
this->_diagonal_distance = this->_line_spacing * 2;
|
||||
this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill
|
||||
BoundingBox bounding_box = expolygon.contour.bounding_box();
|
||||
|
||||
// define flow spacing according to requested density
|
||||
if (params.density > 0.9999f && !params.dont_adjust) {
|
||||
this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), this->_line_spacing);
|
||||
this->spacing = unscale<double>(this->_line_spacing);
|
||||
} else {
|
||||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
// Transform the reference point to the rotated coordinate system.
|
||||
bounding_box.merge(_align_to_grid(
|
||||
bounding_box.min,
|
||||
Point(this->_line_spacing, this->_line_spacing),
|
||||
direction.second.rotated(- direction.first)));
|
||||
}
|
||||
|
||||
// generate the basic pattern
|
||||
coord_t x_max = bounding_box.max(0) + SCALED_EPSILON;
|
||||
Lines lines;
|
||||
for (coord_t x = bounding_box.min(0); x <= x_max; x += this->_line_spacing)
|
||||
lines.push_back(this->_line(lines.size(), x, bounding_box.min(1), bounding_box.max(1)));
|
||||
|
||||
// clip paths against a slightly larger expolygon, so that the first and last paths
|
||||
// are kept even if the expolygon has vertical sides
|
||||
// the minimum offset for preventing edge lines from being clipped is SCALED_EPSILON;
|
||||
// however we use a larger offset to support expolygons with slightly skewed sides and
|
||||
// not perfectly straight
|
||||
//FIXME Vojtech: Update the intersecton function to work directly with lines.
|
||||
Polylines polylines_src;
|
||||
polylines_src.reserve(lines.size());
|
||||
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++ it) {
|
||||
polylines_src.push_back(Polyline());
|
||||
Points &pts = polylines_src.back().points;
|
||||
pts.reserve(2);
|
||||
pts.push_back(it->a);
|
||||
pts.push_back(it->b);
|
||||
}
|
||||
Polylines polylines = intersection_pl(polylines_src, offset(to_polygons(expolygon), scale_(0.02)), false);
|
||||
|
||||
// FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
|
||||
const float INFILL_OVERLAP_OVER_SPACING = 0.3f;
|
||||
// How much to extend an infill path from expolygon outside?
|
||||
coord_t extra = coord_t(floor(this->_min_spacing * INFILL_OVERLAP_OVER_SPACING + 0.5f));
|
||||
for (Polylines::iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
|
||||
Point *first_point = &it_polyline->points.front();
|
||||
Point *last_point = &it_polyline->points.back();
|
||||
if (first_point->y() > last_point->y())
|
||||
std::swap(first_point, last_point);
|
||||
first_point->y() -= extra;
|
||||
last_point->y() += extra;
|
||||
}
|
||||
|
||||
size_t n_polylines_out_old = polylines_out.size();
|
||||
|
||||
// connect lines
|
||||
if (! params.dont_connect() && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
// offset the expolygon by max(min_spacing/2, extra)
|
||||
ExPolygon expolygon_off;
|
||||
{
|
||||
ExPolygons expolygons_off = offset_ex(expolygon, this->_min_spacing/2);
|
||||
if (! expolygons_off.empty()) {
|
||||
// When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
|
||||
assert(expolygons_off.size() == 1);
|
||||
std::swap(expolygon_off, expolygons_off.front());
|
||||
}
|
||||
}
|
||||
bool first = true;
|
||||
for (Polyline &polyline : chain_polylines(std::move(polylines))) {
|
||||
if (! first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
const Point &first_point = polyline.points.front();
|
||||
const Point &last_point = pts_end.back();
|
||||
// Distance in X, Y.
|
||||
const Vector distance = last_point - first_point;
|
||||
// TODO: we should also check that both points are on a fill_boundary to avoid
|
||||
// connecting paths on the boundaries of internal regions
|
||||
if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) &&
|
||||
expolygon_off.contains(Line(last_point, first_point))) {
|
||||
// Append the polyline.
|
||||
pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// The lines cannot be connected.
|
||||
polylines_out.emplace_back(std::move(polyline));
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
// paths must be rotated back
|
||||
for (Polylines::iterator it = polylines_out.begin() + n_polylines_out_old; it != polylines_out.end(); ++ it) {
|
||||
// No need to translate, the absolute position is irrelevant.
|
||||
// it->translate(- direction.second(0), - direction.second(1));
|
||||
it->rotate(direction.first);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
49
src/libslic3r/Fill/FillLine.hpp
Normal file
49
src/libslic3r/Fill/FillLine.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef slic3r_FillLine_hpp_
|
||||
#define slic3r_FillLine_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Surface;
|
||||
|
||||
class FillLine : public Fill
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillLine(*this); };
|
||||
~FillLine() override = default;
|
||||
|
||||
protected:
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
|
||||
coord_t _min_spacing;
|
||||
coord_t _line_spacing;
|
||||
// distance threshold for allowing the horizontal infill lines to be connected into a continuous path
|
||||
coord_t _diagonal_distance;
|
||||
// only for line infill
|
||||
coord_t _line_oscillation;
|
||||
|
||||
Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const {
|
||||
coord_t osc = (i & 1) ? this->_line_oscillation : 0;
|
||||
return Line(Point(x - osc, y_min), Point(x + osc, y_max));
|
||||
}
|
||||
|
||||
bool _can_connect(coord_t dist_X, coord_t dist_Y)
|
||||
{
|
||||
coord_t TOLERANCE = 10 * SCALED_EPSILON;
|
||||
return (dist_X >= (this->_line_spacing - this->_line_oscillation) - TOLERANCE)
|
||||
&& (dist_X <= (this->_line_spacing + this->_line_oscillation) + TOLERANCE)
|
||||
&& (dist_Y <= this->_diagonal_distance);
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillLine_hpp_
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../ShortestPath.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillPlanePath.hpp"
|
||||
|
|
@ -9,7 +10,7 @@ void FillPlanePath::_fill_surface_single(
|
|||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
expolygon.rotate(- direction.first);
|
||||
|
|
@ -23,14 +24,14 @@ void FillPlanePath::_fill_surface_single(
|
|||
Point shift = this->_centered() ?
|
||||
bounding_box.center() :
|
||||
bounding_box.min;
|
||||
expolygon.translate(-shift(0), -shift(1));
|
||||
bounding_box.translate(-shift(0), -shift(1));
|
||||
expolygon.translate(-shift.x(), -shift.y());
|
||||
bounding_box.translate(-shift.x(), -shift.y());
|
||||
|
||||
Pointfs pts = _generate(
|
||||
coord_t(ceil(coordf_t(bounding_box.min(0)) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.min(1)) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.max(0)) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.max(1)) / distance_between_lines)));
|
||||
coord_t(ceil(coordf_t(bounding_box.min.x()) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.min.y()) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines)));
|
||||
|
||||
Polylines polylines;
|
||||
if (pts.size() >= 2) {
|
||||
|
|
@ -38,39 +39,24 @@ void FillPlanePath::_fill_surface_single(
|
|||
polylines.push_back(Polyline());
|
||||
Polyline &polyline = polylines.back();
|
||||
polyline.points.reserve(pts.size());
|
||||
for (Pointfs::iterator it = pts.begin(); it != pts.end(); ++ it)
|
||||
for (const Vec2d &pt : pts)
|
||||
polyline.points.push_back(Point(
|
||||
coord_t(floor((*it)(0) * distance_between_lines + 0.5)),
|
||||
coord_t(floor((*it)(1) * distance_between_lines + 0.5))));
|
||||
coord_t(floor(pt.x() * distance_between_lines + 0.5)),
|
||||
coord_t(floor(pt.y() * distance_between_lines + 0.5))));
|
||||
// intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
|
||||
polylines = intersection_pl(polylines, to_polygons(expolygon));
|
||||
|
||||
/*
|
||||
if (1) {
|
||||
require "Slic3r/SVG.pm";
|
||||
print "Writing fill.svg\n";
|
||||
Slic3r::SVG::output("fill.svg",
|
||||
no_arrows => 1,
|
||||
polygons => \@$expolygon,
|
||||
green_polygons => [ $bounding_box->polygon ],
|
||||
polylines => [ $polyline ],
|
||||
red_polylines => \@paths,
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
polylines = intersection_pl(std::move(polylines), to_polygons(expolygon));
|
||||
Polylines chained;
|
||||
if (params.dont_connect() || params.density > 0.5 || polylines.size() <= 1)
|
||||
chained = chain_polylines(std::move(polylines));
|
||||
else
|
||||
connect_infill(std::move(polylines), expolygon, chained, this->spacing, params);
|
||||
// paths must be repositioned and rotated back
|
||||
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) {
|
||||
it->translate(shift(0), shift(1));
|
||||
it->rotate(direction.first);
|
||||
for (Polyline &pl : chained) {
|
||||
pl.translate(shift.x(), shift.y());
|
||||
pl.rotate(direction.first);
|
||||
}
|
||||
append(polylines_out, std::move(chained));
|
||||
}
|
||||
|
||||
// Move the polylines to the output, avoid a deep copy.
|
||||
size_t j = polylines_out.size();
|
||||
polylines_out.resize(j + polylines.size(), Polyline());
|
||||
for (size_t i = 0; i < polylines.size(); ++ i)
|
||||
std::swap(polylines_out[j ++], polylines[i]);
|
||||
}
|
||||
|
||||
// Follow an Archimedean spiral, in polar coordinates: r=a+b\theta
|
||||
|
|
@ -85,13 +71,13 @@ Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t m
|
|||
coordf_t r = 1;
|
||||
Pointfs out;
|
||||
//FIXME Vojtech: If used as a solid infill, there is a gap left at the center.
|
||||
out.push_back(Vec2d(0, 0));
|
||||
out.push_back(Vec2d(1, 0));
|
||||
out.emplace_back(0, 0);
|
||||
out.emplace_back(1, 0);
|
||||
while (r < rmax) {
|
||||
// Discretization angle to achieve a discretization error lower than RESOLUTION.
|
||||
theta += 2. * acos(1. - RESOLUTION / r);
|
||||
r = a + b * theta;
|
||||
out.push_back(Vec2d(r * cos(theta), r * sin(theta)));
|
||||
out.emplace_back(r * cos(theta), r * sin(theta));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
@ -128,15 +114,12 @@ static inline Point hilbert_n_to_xy(const size_t n)
|
|||
++ ndigits;
|
||||
}
|
||||
}
|
||||
int state = (ndigits & 1) ? 4 : 0;
|
||||
// int dirstate = (ndigits & 1) ? 0 : 4;
|
||||
int state = (ndigits & 1) ? 4 : 0;
|
||||
coord_t x = 0;
|
||||
coord_t y = 0;
|
||||
for (int i = (int)ndigits - 1; i >= 0; -- i) {
|
||||
int digit = (n >> (i * 2)) & 3;
|
||||
state += digit;
|
||||
// if (digit != 3)
|
||||
// dirstate = state; // lowest non-3 digit
|
||||
x |= digit_to_x[state] << i;
|
||||
y |= digit_to_y[state] << i;
|
||||
state = next_state[state];
|
||||
|
|
@ -162,7 +145,7 @@ Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x,
|
|||
line.reserve(sz2);
|
||||
for (size_t i = 0; i < sz2; ++ i) {
|
||||
Point p = hilbert_n_to_xy(i);
|
||||
line.push_back(Vec2d(p(0) + min_x, p(1) + min_y));
|
||||
line.emplace_back(p.x() + min_x, p.y() + min_y);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
|
@ -175,27 +158,27 @@ Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_
|
|||
coordf_t r = 0;
|
||||
coordf_t r_inc = sqrt(2.);
|
||||
Pointfs out;
|
||||
out.push_back(Vec2d(0, 0));
|
||||
out.emplace_back(0., 0.);
|
||||
while (r < rmax) {
|
||||
r += r_inc;
|
||||
coordf_t rx = r / sqrt(2.);
|
||||
coordf_t r2 = r + rx;
|
||||
out.push_back(Vec2d( r, 0.));
|
||||
out.push_back(Vec2d( r2, rx));
|
||||
out.push_back(Vec2d( rx, rx));
|
||||
out.push_back(Vec2d( rx, r2));
|
||||
out.push_back(Vec2d(0., r));
|
||||
out.push_back(Vec2d(-rx, r2));
|
||||
out.push_back(Vec2d(-rx, rx));
|
||||
out.push_back(Vec2d(-r2, rx));
|
||||
out.push_back(Vec2d(-r, 0.));
|
||||
out.push_back(Vec2d(-r2, -rx));
|
||||
out.push_back(Vec2d(-rx, -rx));
|
||||
out.push_back(Vec2d(-rx, -r2));
|
||||
out.push_back(Vec2d(0., -r));
|
||||
out.push_back(Vec2d( rx, -r2));
|
||||
out.push_back(Vec2d( rx, -rx));
|
||||
out.push_back(Vec2d( r2+r_inc, -rx));
|
||||
out.emplace_back( r, 0.);
|
||||
out.emplace_back( r2, rx);
|
||||
out.emplace_back( rx, rx);
|
||||
out.emplace_back( rx, r2);
|
||||
out.emplace_back( 0., r);
|
||||
out.emplace_back(-rx, r2);
|
||||
out.emplace_back(-rx, rx);
|
||||
out.emplace_back(-r2, rx);
|
||||
out.emplace_back(- r, 0.);
|
||||
out.emplace_back(-r2, -rx);
|
||||
out.emplace_back(-rx, -rx);
|
||||
out.emplace_back(-rx, -r2);
|
||||
out.emplace_back( 0., -r);
|
||||
out.emplace_back( rx, -r2);
|
||||
out.emplace_back( rx, -rx);
|
||||
out.emplace_back( r2+r_inc, -rx);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,17 +16,17 @@ namespace Slic3r {
|
|||
class FillPlanePath : public Fill
|
||||
{
|
||||
public:
|
||||
virtual ~FillPlanePath() {}
|
||||
~FillPlanePath() override = default;
|
||||
|
||||
protected:
|
||||
virtual void _fill_surface_single(
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
virtual bool _centered() const = 0;
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) = 0;
|
||||
};
|
||||
|
|
@ -34,34 +34,34 @@ protected:
|
|||
class FillArchimedeanChords : public FillPlanePath
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillArchimedeanChords(*this); };
|
||||
virtual ~FillArchimedeanChords() {}
|
||||
Fill* clone() const override { return new FillArchimedeanChords(*this); };
|
||||
~FillArchimedeanChords() override = default;
|
||||
|
||||
protected:
|
||||
virtual bool _centered() const { return true; }
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
|
||||
bool _centered() const override { return true; }
|
||||
Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) override;
|
||||
};
|
||||
|
||||
class FillHilbertCurve : public FillPlanePath
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillHilbertCurve(*this); };
|
||||
virtual ~FillHilbertCurve() {}
|
||||
Fill* clone() const override { return new FillHilbertCurve(*this); };
|
||||
~FillHilbertCurve() override = default;
|
||||
|
||||
protected:
|
||||
virtual bool _centered() const { return false; }
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
|
||||
bool _centered() const override { return false; }
|
||||
Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) override;
|
||||
};
|
||||
|
||||
class FillOctagramSpiral : public FillPlanePath
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillOctagramSpiral(*this); };
|
||||
virtual ~FillOctagramSpiral() {}
|
||||
Fill* clone() const override { return new FillOctagramSpiral(*this); };
|
||||
~FillOctagramSpiral() override = default;
|
||||
|
||||
protected:
|
||||
virtual bool _centered() const { return true; }
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
|
||||
bool _centered() const override { return true; }
|
||||
Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) override;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,68 +12,92 @@ class Surface;
|
|||
class FillRectilinear : public Fill
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillRectilinear(*this); };
|
||||
virtual ~FillRectilinear() {}
|
||||
Fill* clone() const override { return new FillRectilinear(*this); };
|
||||
~FillRectilinear() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
// Fill by single directional lines, interconnect the lines along perimeters.
|
||||
bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out);
|
||||
|
||||
coord_t _min_spacing;
|
||||
coord_t _line_spacing;
|
||||
// distance threshold for allowing the horizontal infill lines to be connected into a continuous path
|
||||
coord_t _diagonal_distance;
|
||||
// only for line infill
|
||||
coord_t _line_oscillation;
|
||||
|
||||
// Enabled for the grid infill, disabled for the rectilinear and line infill.
|
||||
virtual bool _horizontal_lines() const { return false; }
|
||||
|
||||
virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const
|
||||
{ return Line(Point(x, y_min), Point(x, y_max)); }
|
||||
|
||||
virtual bool _can_connect(coord_t dist_X, coord_t dist_Y) {
|
||||
return dist_X <= this->_diagonal_distance
|
||||
&& dist_Y <= this->_diagonal_distance;
|
||||
}
|
||||
// Fill by multiple sweeps of differing directions.
|
||||
struct SweepParams {
|
||||
float angle_base;
|
||||
float pattern_shift;
|
||||
};
|
||||
bool fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &sweep_params, Polylines &polylines_out);
|
||||
};
|
||||
|
||||
class FillLine : public FillRectilinear
|
||||
class FillAlignedRectilinear : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
virtual ~FillLine() {}
|
||||
Fill* clone() const override { return new FillAlignedRectilinear(*this); };
|
||||
~FillAlignedRectilinear() override = default;
|
||||
|
||||
protected:
|
||||
virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const {
|
||||
coord_t osc = (i & 1) ? this->_line_oscillation : 0;
|
||||
return Line(Point(x - osc, y_min), Point(x + osc, y_max));
|
||||
}
|
||||
// Always generate infill at the same angle.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
};
|
||||
|
||||
virtual bool _can_connect(coord_t dist_X, coord_t dist_Y)
|
||||
{
|
||||
coord_t TOLERANCE = 10 * SCALED_EPSILON;
|
||||
return (dist_X >= (this->_line_spacing - this->_line_oscillation) - TOLERANCE)
|
||||
&& (dist_X <= (this->_line_spacing + this->_line_oscillation) + TOLERANCE)
|
||||
&& (dist_Y <= this->_diagonal_distance);
|
||||
}
|
||||
class FillMonotonic : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillMonotonic(*this); };
|
||||
~FillMonotonic() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
bool no_sort() const override { return true; }
|
||||
};
|
||||
|
||||
class FillGrid : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
virtual ~FillGrid() {}
|
||||
Fill* clone() const override { return new FillGrid(*this); };
|
||||
~FillGrid() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
// Flag for Slic3r::Fill::Rectilinear to fill both directions.
|
||||
virtual bool _horizontal_lines() const { return true; }
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
class FillTriangles : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillTriangles(*this); };
|
||||
~FillTriangles() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
class FillStars : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillStars(*this); };
|
||||
~FillStars() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
class FillCubic : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillCubic(*this); };
|
||||
~FillCubic() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillRectilinear_hpp_
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,83 +0,0 @@
|
|||
#ifndef slic3r_FillRectilinear2_hpp_
|
||||
#define slic3r_FillRectilinear2_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Surface;
|
||||
|
||||
class FillRectilinear2 : public Fill
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillRectilinear2(*this); };
|
||||
virtual ~FillRectilinear2() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out);
|
||||
};
|
||||
|
||||
class FillMonotonic : public FillRectilinear2
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillMonotonic(*this); };
|
||||
virtual ~FillMonotonic() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
virtual bool no_sort() const { return true; }
|
||||
};
|
||||
|
||||
class FillGrid2 : public FillRectilinear2
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillGrid2(*this); };
|
||||
virtual ~FillGrid2() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
};
|
||||
|
||||
class FillTriangles : public FillRectilinear2
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillTriangles(*this); };
|
||||
virtual ~FillTriangles() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
};
|
||||
|
||||
class FillStars : public FillRectilinear2
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillStars(*this); };
|
||||
virtual ~FillStars() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
};
|
||||
|
||||
class FillCubic : public FillRectilinear2
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillCubic(*this); };
|
||||
virtual ~FillCubic() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
};
|
||||
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillRectilinear2_hpp_
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/SLA/RasterBase.hpp"
|
||||
#include "libslic3r/miniz_extension.hpp"
|
||||
#include "libslic3r/PNGRead.hpp"
|
||||
#include "libslic3r/PNGReadWrite.hpp"
|
||||
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
|
|
|||
|
|
@ -169,6 +169,10 @@ namespace Slic3r {
|
|||
|
||||
// subdivide the retraction in segments
|
||||
if (!wipe_path.empty()) {
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
// add tag for processor
|
||||
gcode += ";" + GCodeProcessor::Wipe_Start_Tag + "\n";
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
for (const Line& line : wipe_path.lines()) {
|
||||
double segment_length = line.length();
|
||||
/* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
|
||||
|
|
@ -183,6 +187,10 @@ namespace Slic3r {
|
|||
"wipe and retract"
|
||||
);
|
||||
}
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
// add tag for processor
|
||||
gcode += ";" + GCodeProcessor::Wipe_End_Tag + "\n";
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
gcodegen.set_last_pos(wipe_path.points.back());
|
||||
}
|
||||
|
||||
|
|
@ -1764,7 +1772,7 @@ void GCode::process_layer(
|
|||
std::string gcode;
|
||||
|
||||
// add tag for processor
|
||||
gcode += "; " + GCodeProcessor::Layer_Change_Tag + "\n";
|
||||
gcode += ";" + GCodeProcessor::Layer_Change_Tag + "\n";
|
||||
// export layer z
|
||||
char buf[64];
|
||||
sprintf(buf, ";Z:%g\n", print_z);
|
||||
|
|
@ -2240,7 +2248,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
const EdgeGrid::Grid* edge_grid_ptr = (lower_layer_edge_grid && *lower_layer_edge_grid)
|
||||
? lower_layer_edge_grid->get()
|
||||
: nullptr;
|
||||
Point seam = m_seam_placer.get_seam(m_layer->id(), seam_position, loop,
|
||||
Point seam = m_seam_placer.get_seam(*m_layer, seam_position, loop,
|
||||
last_pos, EXTRUDER_CONFIG(nozzle_diameter),
|
||||
(m_layer == NULL ? nullptr : m_layer->object()),
|
||||
was_clockwise, edge_grid_ptr);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
|
|||
namespace Slic3r {
|
||||
|
||||
const std::string GCodeProcessor::Extrusion_Role_Tag = "TYPE:";
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
const std::string GCodeProcessor::Wipe_Start_Tag = "WIPE_START";
|
||||
const std::string GCodeProcessor::Wipe_End_Tag = "WIPE_END";
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
const std::string GCodeProcessor::Height_Tag = "HEIGHT:";
|
||||
const std::string GCodeProcessor::Layer_Change_Tag = "LAYER_CHANGE";
|
||||
const std::string GCodeProcessor::Color_Change_Tag = "COLOR_CHANGE";
|
||||
|
|
@ -35,6 +39,11 @@ const std::string GCodeProcessor::First_Line_M73_Placeholder_Tag = "; _
|
|||
const std::string GCodeProcessor::Last_Line_M73_Placeholder_Tag = "; _GP_LAST_LINE_M73_PLACEHOLDER";
|
||||
const std::string GCodeProcessor::Estimated_Printing_Time_Placeholder_Tag = "; _GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER";
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
const float GCodeProcessor::Wipe_Width = 0.05f;
|
||||
const float GCodeProcessor::Wipe_Height = 0.05f;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
const std::string GCodeProcessor::Width_Tag = "WIDTH:";
|
||||
const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "MM3_PER_MM:";
|
||||
|
|
@ -390,13 +399,17 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
};
|
||||
|
||||
// check for temporary lines
|
||||
auto is_temporary_decoration = [](const std::string& gcode_line) {
|
||||
auto is_temporary_decoration = [](const std::string_view gcode_line) {
|
||||
// remove trailing '\n'
|
||||
std::string line = gcode_line.substr(0, gcode_line.length() - 1);
|
||||
if (line == "; " + Layer_Change_Tag)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
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.
|
||||
|
|
@ -488,7 +501,8 @@ const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProces
|
|||
{ EProducer::Cura, "Cura_SteamEngine" },
|
||||
{ EProducer::Simplify3D, "Simplify3D" },
|
||||
{ EProducer::CraftWare, "CraftWare" },
|
||||
{ EProducer::ideaMaker, "ideaMaker" }
|
||||
{ EProducer::ideaMaker, "ideaMaker" },
|
||||
{ EProducer::KissSlicer, "KISSlicer" }
|
||||
};
|
||||
|
||||
unsigned int GCodeProcessor::s_result_id = 0;
|
||||
|
|
@ -591,9 +605,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
}
|
||||
}
|
||||
|
||||
// ensure at least one (default) color is defined
|
||||
std::string default_color = "#FF8000";
|
||||
m_result.extruder_colors = std::vector<std::string>(1, default_color);
|
||||
const ConfigOptionStrings* extruder_colour = config.option<ConfigOptionStrings>("extruder_colour");
|
||||
if (extruder_colour != nullptr) {
|
||||
// takes colors from config
|
||||
|
|
@ -608,7 +619,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// replace missing values with default
|
||||
std::string default_color = "#FF8000";
|
||||
for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) {
|
||||
if (m_result.extruder_colors[i].empty())
|
||||
m_result.extruder_colors[i] = default_color;
|
||||
|
|
@ -725,6 +738,9 @@ void GCodeProcessor::reset()
|
|||
m_end_position = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
m_origin = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
m_cached_position.reset();
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
m_wiping = false;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
m_feedrate = 0.0f;
|
||||
m_width = 0.0f;
|
||||
|
|
@ -806,6 +822,16 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
|||
process_gcode_line(line);
|
||||
});
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
// update width/height of wipe moves
|
||||
for (MoveVertex& move : m_result.moves) {
|
||||
if (move.type == EMoveType::Wipe) {
|
||||
move.width = Wipe_Width;
|
||||
move.height = Wipe_Height;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
// process the time blocks
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
||||
TimeMachine& machine = m_time_processor.machines[i];
|
||||
|
|
@ -1027,6 +1053,20 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
return;
|
||||
}
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
// wipe start tag
|
||||
if (starts_with(comment, Wipe_Start_Tag)) {
|
||||
m_wiping = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// wipe end tag
|
||||
if (starts_with(comment, Wipe_End_Tag)) {
|
||||
m_wiping = false;
|
||||
return;
|
||||
}
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
if ((!m_producers_enabled || m_producer == EProducer::PrusaSlicer) &&
|
||||
starts_with(comment, Height_Tag)) {
|
||||
// height tag
|
||||
|
|
@ -1104,11 +1144,14 @@ bool GCodeProcessor::process_producers_tags(const std::string_view comment)
|
|||
{
|
||||
switch (m_producer)
|
||||
{
|
||||
case EProducer::Slic3rPE:
|
||||
case EProducer::Slic3r:
|
||||
case EProducer::PrusaSlicer: { return process_prusaslicer_tags(comment); }
|
||||
case EProducer::Cura: { return process_cura_tags(comment); }
|
||||
case EProducer::Simplify3D: { return process_simplify3d_tags(comment); }
|
||||
case EProducer::CraftWare: { return process_craftware_tags(comment); }
|
||||
case EProducer::ideaMaker: { return process_ideamaker_tags(comment); }
|
||||
case EProducer::KissSlicer: { return process_kissslicer_tags(comment); }
|
||||
default: { return false; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1178,6 +1221,14 @@ bool GCodeProcessor::process_cura_tags(const std::string_view comment)
|
|||
return true;
|
||||
}
|
||||
|
||||
// layer
|
||||
tag = "LAYER:";
|
||||
pos = comment.find(tag);
|
||||
if (pos != comment.npos) {
|
||||
++m_layer_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1262,9 +1313,8 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment)
|
|||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// geometry
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// ; tool
|
||||
std::string tag = " tool";
|
||||
pos = comment.find(tag);
|
||||
|
|
@ -1289,6 +1339,19 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment)
|
|||
}
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
// ; layer
|
||||
std::string tag = " layer";
|
||||
pos = comment.find(tag);
|
||||
if (pos == 0) {
|
||||
// skip lines "; layer end"
|
||||
const std::string_view data = comment.substr(pos + tag.length());
|
||||
size_t end_start = data.find("end");
|
||||
if (end_start == data.npos)
|
||||
++m_layer_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1329,6 +1392,13 @@ bool GCodeProcessor::process_craftware_tags(const std::string_view comment)
|
|||
return true;
|
||||
}
|
||||
|
||||
// layer
|
||||
pos = comment.find(" Layer #");
|
||||
if (pos == 0) {
|
||||
++m_layer_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1360,9 +1430,8 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string_view comment)
|
|||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// geometry
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// width
|
||||
tag = "WIDTH:";
|
||||
pos = comment.find(tag);
|
||||
|
|
@ -1382,6 +1451,120 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string_view comment)
|
|||
}
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
// layer
|
||||
pos = comment.find("LAYER:");
|
||||
if (pos == 0) {
|
||||
++m_layer_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GCodeProcessor::process_kissslicer_tags(const std::string_view comment)
|
||||
{
|
||||
// extrusion roles
|
||||
|
||||
// ; 'Raft Path'
|
||||
size_t pos = comment.find(" 'Raft Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erSkirt;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Support Interface Path'
|
||||
pos = comment.find(" 'Support Interface Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erSupportMaterialInterface;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Travel/Ironing Path'
|
||||
pos = comment.find(" 'Travel/Ironing Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erIroning;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Support (may Stack) Path'
|
||||
pos = comment.find(" 'Support (may Stack) Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erSupportMaterial;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Perimeter Path'
|
||||
pos = comment.find(" 'Perimeter Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erExternalPerimeter;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Pillar Path'
|
||||
pos = comment.find(" 'Pillar Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Destring/Wipe/Jump Path'
|
||||
pos = comment.find(" 'Destring/Wipe/Jump Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Prime Pillar Path'
|
||||
pos = comment.find(" 'Prime Pillar Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Loop Path'
|
||||
pos = comment.find(" 'Loop Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Crown Path'
|
||||
pos = comment.find(" 'Crown Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Solid Path'
|
||||
pos = comment.find(" 'Solid Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Stacked Sparse Infill Path'
|
||||
pos = comment.find(" 'Stacked Sparse Infill Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erInternalInfill;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Sparse Infill Path'
|
||||
pos = comment.find(" 'Sparse Infill Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erSolidInfill;
|
||||
return true;
|
||||
}
|
||||
|
||||
// geometry
|
||||
|
||||
// layer
|
||||
pos = comment.find(" BEGIN_LAYER_");
|
||||
if (pos == 0) {
|
||||
++m_layer_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1423,7 +1606,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
auto move_type = [this](const AxisCoords& delta_pos) {
|
||||
EMoveType type = EMoveType::Noop;
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
if (m_wiping)
|
||||
type = EMoveType::Wipe;
|
||||
else if (delta_pos[E] < 0.0f)
|
||||
#else
|
||||
if (delta_pos[E] < 0.0f)
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
type = (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) ? EMoveType::Travel : EMoveType::Retract;
|
||||
else if (delta_pos[E] > 0.0f) {
|
||||
if (delta_pos[X] == 0.0f && delta_pos[Y] == 0.0f)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ namespace Slic3r {
|
|||
Pause_Print,
|
||||
Custom_GCode,
|
||||
Travel,
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
Wipe,
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
Extrude,
|
||||
Count
|
||||
};
|
||||
|
|
@ -69,6 +72,10 @@ namespace Slic3r {
|
|||
{
|
||||
public:
|
||||
static const std::string Extrusion_Role_Tag;
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
static const std::string Wipe_Start_Tag;
|
||||
static const std::string Wipe_End_Tag;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
static const std::string Height_Tag;
|
||||
static const std::string Layer_Change_Tag;
|
||||
static const std::string Color_Change_Tag;
|
||||
|
|
@ -78,6 +85,11 @@ namespace Slic3r {
|
|||
static const std::string Last_Line_M73_Placeholder_Tag;
|
||||
static const std::string Estimated_Printing_Time_Placeholder_Tag;
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
static const float Wipe_Width;
|
||||
static const float Wipe_Height;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
static const std::string Width_Tag;
|
||||
static const std::string Mm3_Per_Mm_Tag;
|
||||
|
|
@ -390,6 +402,9 @@ namespace Slic3r {
|
|||
AxisCoords m_end_position; // mm
|
||||
AxisCoords m_origin; // mm
|
||||
CachedPosition m_cached_position;
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
bool m_wiping;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
float m_feedrate; // mm/s
|
||||
float m_width; // mm
|
||||
|
|
@ -414,7 +429,8 @@ namespace Slic3r {
|
|||
Cura,
|
||||
Simplify3D,
|
||||
CraftWare,
|
||||
ideaMaker
|
||||
ideaMaker,
|
||||
KissSlicer
|
||||
};
|
||||
|
||||
static const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> Producers;
|
||||
|
|
@ -471,6 +487,7 @@ namespace Slic3r {
|
|||
bool process_simplify3d_tags(const std::string_view comment);
|
||||
bool process_craftware_tags(const std::string_view comment);
|
||||
bool process_ideamaker_tags(const std::string_view comment);
|
||||
bool process_kissslicer_tags(const std::string_view comment);
|
||||
|
||||
bool detect_producer(const std::string_view comment);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "libslic3r/EdgeGrid.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/SVG.hpp"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -191,24 +192,98 @@ void SeamPlacer::init(const Print& print)
|
|||
{
|
||||
m_enforcers.clear();
|
||||
m_blockers.clear();
|
||||
//m_last_seam_position.clear();
|
||||
m_seam_history.clear();
|
||||
m_po_list.clear();
|
||||
|
||||
for (const PrintObject* po : print.objects()) {
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::ENFORCER, m_enforcers);
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::BLOCKER, m_blockers);
|
||||
}
|
||||
const std::vector<double>& nozzle_dmrs = print.config().nozzle_diameter.values;
|
||||
float max_nozzle_dmr = *std::max_element(nozzle_dmrs.begin(), nozzle_dmrs.end());
|
||||
for (ExPolygons& explgs : m_enforcers)
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||
for (ExPolygons& explgs : m_blockers)
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||
const std::vector<double>& nozzle_dmrs = print.config().nozzle_diameter.values;
|
||||
float max_nozzle_dmr = *std::max_element(nozzle_dmrs.begin(), nozzle_dmrs.end());
|
||||
|
||||
|
||||
std::vector<ExPolygons> temp_enf;
|
||||
std::vector<ExPolygons> temp_blk;
|
||||
|
||||
for (const PrintObject* po : print.objects()) {
|
||||
temp_enf.clear();
|
||||
temp_blk.clear();
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::ENFORCER, temp_enf);
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::BLOCKER, temp_blk);
|
||||
|
||||
// Offset the triangles out slightly.
|
||||
for (auto* custom_per_object : {&temp_enf, &temp_blk})
|
||||
for (ExPolygons& explgs : *custom_per_object)
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||
|
||||
// FIXME: Offsetting should be done somehow cheaper, but following does not work
|
||||
// for (auto* custom_per_object : {&temp_enf, &temp_blk}) {
|
||||
// for (ExPolygons& plgs : *custom_per_object) {
|
||||
// for (ExPolygon& plg : plgs) {
|
||||
// auto out = Slic3r::offset_ex(plg, scale_(max_nozzle_dmr));
|
||||
// plg = out.empty() ? ExPolygon() : out.front();
|
||||
// assert(out.empty() || out.size() == 1);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// Remember this PrintObject and initialize a store of enforcers and blockers for it.
|
||||
m_po_list.push_back(po);
|
||||
size_t po_idx = m_po_list.size() - 1;
|
||||
m_enforcers.emplace_back(std::vector<CustomTrianglesPerLayer>(temp_enf.size()));
|
||||
m_blockers.emplace_back(std::vector<CustomTrianglesPerLayer>(temp_blk.size()));
|
||||
|
||||
// A helper class to store data to build the AABB tree from.
|
||||
class CustomTriangleRef {
|
||||
public:
|
||||
CustomTriangleRef(size_t idx,
|
||||
Point&& centroid,
|
||||
BoundingBox&& bb)
|
||||
: m_idx{idx}, m_centroid{centroid},
|
||||
m_bbox{AlignedBoxType(bb.min, bb.max)}
|
||||
{}
|
||||
size_t idx() const { return m_idx; }
|
||||
const Point& centroid() const { return m_centroid; }
|
||||
const TreeType::BoundingBox& bbox() const { return m_bbox; }
|
||||
|
||||
private:
|
||||
size_t m_idx;
|
||||
Point m_centroid;
|
||||
AlignedBoxType m_bbox;
|
||||
};
|
||||
|
||||
// A lambda to extract the ExPolygons and save them into the member AABB tree.
|
||||
// Will be called for enforcers and blockers separately.
|
||||
auto add_custom = [](std::vector<ExPolygons>& src, std::vector<CustomTrianglesPerLayer>& dest) {
|
||||
// Go layer by layer, and append all the ExPolygons into the AABB tree.
|
||||
size_t layer_idx = 0;
|
||||
for (ExPolygons& expolys_on_layer : src) {
|
||||
CustomTrianglesPerLayer& layer_data = dest[layer_idx];
|
||||
std::vector<CustomTriangleRef> triangles_data;
|
||||
layer_data.polys.reserve(expolys_on_layer.size());
|
||||
triangles_data.reserve(expolys_on_layer.size());
|
||||
|
||||
for (ExPolygon& expoly : expolys_on_layer) {
|
||||
if (expoly.empty())
|
||||
continue;
|
||||
layer_data.polys.emplace_back(std::move(expoly));
|
||||
triangles_data.emplace_back(layer_data.polys.size() - 1,
|
||||
layer_data.polys.back().centroid(),
|
||||
layer_data.polys.back().bounding_box());
|
||||
}
|
||||
// All polygons are saved, build the AABB tree for them.
|
||||
layer_data.tree.build(std::move(triangles_data));
|
||||
++layer_idx;
|
||||
}
|
||||
};
|
||||
|
||||
add_custom(temp_enf, m_enforcers.at(po_idx));
|
||||
add_custom(temp_blk, m_blockers.at(po_idx));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_position,
|
||||
Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
const ExtrusionLoop& loop, Point last_pos, coordf_t nozzle_dmr,
|
||||
const PrintObject* po, bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid)
|
||||
{
|
||||
|
|
@ -216,7 +291,28 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
|
|||
BoundingBox polygon_bb = polygon.bounding_box();
|
||||
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
||||
|
||||
if (this->is_custom_seam_on_layer(layer_idx)) {
|
||||
size_t po_idx = std::find(m_po_list.begin(), m_po_list.end(), po) - m_po_list.begin();
|
||||
|
||||
// Find current layer in respective PrintObject. Cache the result so the
|
||||
// lookup is only done once per layer, not for each loop.
|
||||
const Layer* layer_po = nullptr;
|
||||
if (po == m_last_po && layer.print_z == m_last_print_z)
|
||||
layer_po = m_last_layer_po;
|
||||
else {
|
||||
layer_po = po->get_layer_at_printz(layer.print_z);
|
||||
m_last_po = po;
|
||||
m_last_print_z = layer.print_z;
|
||||
m_last_layer_po = layer_po;
|
||||
}
|
||||
if (! layer_po)
|
||||
return last_pos;
|
||||
|
||||
// Index of this layer in the respective PrintObject.
|
||||
size_t layer_idx = layer_po->id() - po->layers().front()->id(); // raft layers
|
||||
|
||||
assert(layer_idx < po->layer_count());
|
||||
|
||||
if (this->is_custom_seam_on_layer(layer_idx, po_idx)) {
|
||||
// Seam enf/blockers can begin and end in between the original vertices.
|
||||
// Let add extra points in between and update the leghths.
|
||||
polygon.densify(MINIMAL_POLYGON_SIDE);
|
||||
|
|
@ -229,11 +325,10 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
|
|||
if (seam_position == spAligned) {
|
||||
// Seam is aligned to the seam at the preceding layer.
|
||||
if (po != nullptr) {
|
||||
std::optional<Point> pos = m_seam_history.get_last_seam(po, layer_idx, polygon_bb);
|
||||
std::optional<Point> pos = m_seam_history.get_last_seam(m_po_list[po_idx], layer_idx, polygon_bb);
|
||||
if (pos.has_value()) {
|
||||
//last_pos = m_last_seam_position[po];
|
||||
last_pos = *pos;
|
||||
last_pos_weight = is_custom_enforcer_on_layer(layer_idx) ? 0.f : 1.f;
|
||||
last_pos_weight = is_custom_enforcer_on_layer(layer_idx, po_idx) ? 0.f : 1.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -313,12 +408,12 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
|
|||
|
||||
// Custom seam. Huge (negative) constant penalty is applied inside
|
||||
// blockers (enforcers) to rule out points that should not win.
|
||||
this->apply_custom_seam(polygon, penalties, lengths, layer_idx, seam_position);
|
||||
this->apply_custom_seam(polygon, po_idx, penalties, lengths, layer_idx, seam_position);
|
||||
|
||||
// Find a point with a minimum penalty.
|
||||
size_t idx_min = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
|
||||
|
||||
if (seam_position != spAligned || ! is_custom_enforcer_on_layer(layer_idx)) {
|
||||
if (seam_position != spAligned || ! is_custom_enforcer_on_layer(layer_idx, po_idx)) {
|
||||
// Very likely the weight of idx_min is very close to the weight of last_pos_proj_idx.
|
||||
// In that case use last_pos_proj_idx instead.
|
||||
float penalty_aligned = penalties[last_pos_proj_idx];
|
||||
|
|
@ -363,29 +458,45 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
|
|||
return polygon.points[idx_min];
|
||||
|
||||
} else { // spRandom
|
||||
if (loop.loop_role() == elrContourInternalPerimeter && loop.role() != erExternalPerimeter) {
|
||||
// This loop does not contain any other loop. Set a random position.
|
||||
// The other loops will get a seam close to the random point chosen
|
||||
// on the innermost contour.
|
||||
//FIXME This works correctly for inner contours first only.
|
||||
last_pos = this->get_random_seam(layer_idx, polygon);
|
||||
}
|
||||
if (loop.role() == erExternalPerimeter && is_custom_seam_on_layer(layer_idx)) {
|
||||
// There is a possibility that the loop will be influenced by custom
|
||||
// seam enforcer/blocker. In this case do not inherit the seam
|
||||
// from internal loops (which may conflict with the custom selection
|
||||
// and generate another random one.
|
||||
bool saw_custom = false;
|
||||
Point candidate = this->get_random_seam(layer_idx, polygon, &saw_custom);
|
||||
if (saw_custom)
|
||||
last_pos = candidate;
|
||||
if (po->print()->default_region_config().external_perimeters_first) {
|
||||
if (loop.role() == erExternalPerimeter)
|
||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
else {
|
||||
// Internal perimeters will just use last_pos.
|
||||
}
|
||||
} else {
|
||||
if (loop.loop_role() == elrContourInternalPerimeter && loop.role() != erExternalPerimeter) {
|
||||
// This loop does not contain any other loop. Set a random position.
|
||||
// The other loops will get a seam close to the random point chosen
|
||||
// on the innermost contour.
|
||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
m_last_loop_was_external = false;
|
||||
}
|
||||
if (loop.role() == erExternalPerimeter) {
|
||||
if (m_last_loop_was_external) {
|
||||
// There was no internal perimeter before this one.
|
||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
} else {
|
||||
if (is_custom_seam_on_layer(layer_idx, po_idx)) {
|
||||
// There is a possibility that the loop will be influenced by custom
|
||||
// seam enforcer/blocker. In this case do not inherit the seam
|
||||
// from internal loops (which may conflict with the custom selection
|
||||
// and generate another random one.
|
||||
bool saw_custom = false;
|
||||
Point candidate = this->get_random_seam(layer_idx, polygon, po_idx, &saw_custom);
|
||||
if (saw_custom)
|
||||
last_pos = candidate;
|
||||
}
|
||||
}
|
||||
m_last_loop_was_external = true;
|
||||
}
|
||||
}
|
||||
return last_pos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon,
|
||||
Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon, size_t po_idx,
|
||||
bool* saw_custom) const
|
||||
{
|
||||
// Parametrize the polygon by its length.
|
||||
|
|
@ -394,7 +505,7 @@ Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon,
|
|||
// Which of the points are inside enforcers/blockers?
|
||||
std::vector<size_t> enforcers_idxs;
|
||||
std::vector<size_t> blockers_idxs;
|
||||
this->get_enforcers_and_blockers(layer_idx, polygon, enforcers_idxs, blockers_idxs);
|
||||
this->get_enforcers_and_blockers(layer_idx, polygon, po_idx, enforcers_idxs, blockers_idxs);
|
||||
|
||||
bool has_enforcers = ! enforcers_idxs.empty();
|
||||
bool has_blockers = ! blockers_idxs.empty();
|
||||
|
|
@ -444,32 +555,44 @@ Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon,
|
|||
|
||||
void SeamPlacer::get_enforcers_and_blockers(size_t layer_id,
|
||||
const Polygon& polygon,
|
||||
size_t po_idx,
|
||||
std::vector<size_t>& enforcers_idxs,
|
||||
std::vector<size_t>& blockers_idxs) const
|
||||
{
|
||||
enforcers_idxs.clear();
|
||||
blockers_idxs.clear();
|
||||
|
||||
// FIXME: This is quadratic and it should be improved, maybe by building
|
||||
// an AABB tree (or at least utilize bounding boxes).
|
||||
for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||
auto is_inside = [](const Point& pt,
|
||||
const CustomTrianglesPerLayer& custom_data) -> bool {
|
||||
assert(! custom_data.polys.empty());
|
||||
// Now ask the AABB tree which polygon we should check and check it.
|
||||
size_t candidate = AABBTreeIndirect::get_candidate_idx(custom_data.tree, pt);
|
||||
if (candidate != size_t(-1)
|
||||
&& custom_data.polys[candidate].contains(pt))
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
if (! m_enforcers.empty()) {
|
||||
assert(layer_id < m_enforcers.size());
|
||||
for (const ExPolygon& explg : m_enforcers[layer_id]) {
|
||||
if (explg.contains(polygon.points[i]))
|
||||
enforcers_idxs.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (! m_blockers.empty()) {
|
||||
assert(layer_id < m_blockers.size());
|
||||
for (const ExPolygon& explg : m_blockers[layer_id]) {
|
||||
if (explg.contains(polygon.points[i]))
|
||||
blockers_idxs.push_back(i);
|
||||
if (! m_enforcers[po_idx].empty()) {
|
||||
const CustomTrianglesPerLayer& enforcers = m_enforcers[po_idx][layer_id];
|
||||
if (! enforcers.polys.empty()) {
|
||||
for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||
if (is_inside(polygon.points[i], enforcers))
|
||||
enforcers_idxs.emplace_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! m_blockers[po_idx].empty()) {
|
||||
const CustomTrianglesPerLayer& blockers = m_blockers[po_idx][layer_id];
|
||||
if (! blockers.polys.empty()) {
|
||||
for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||
if (is_inside(polygon.points[i], blockers))
|
||||
blockers_idxs.emplace_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -543,17 +666,17 @@ static std::vector<size_t> find_enforcer_centers(const Polygon& polygon,
|
|||
|
||||
|
||||
|
||||
void SeamPlacer::apply_custom_seam(const Polygon& polygon,
|
||||
void SeamPlacer::apply_custom_seam(const Polygon& polygon, size_t po_idx,
|
||||
std::vector<float>& penalties,
|
||||
const std::vector<float>& lengths,
|
||||
int layer_id, SeamPosition seam_position) const
|
||||
{
|
||||
if (! is_custom_seam_on_layer(layer_id))
|
||||
if (! is_custom_seam_on_layer(layer_id, po_idx))
|
||||
return;
|
||||
|
||||
std::vector<size_t> enforcers_idxs;
|
||||
std::vector<size_t> blockers_idxs;
|
||||
this->get_enforcers_and_blockers(layer_id, polygon, enforcers_idxs, blockers_idxs);
|
||||
this->get_enforcers_and_blockers(layer_id, polygon, po_idx, enforcers_idxs, blockers_idxs);
|
||||
|
||||
for (size_t i : enforcers_idxs) {
|
||||
assert(i < penalties.size());
|
||||
|
|
|
|||
|
|
@ -3,15 +3,17 @@
|
|||
|
||||
#include <optional>
|
||||
|
||||
#include "libslic3r/ExPolygon.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/AABBTreeIndirect.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PrintObject;
|
||||
class ExtrusionLoop;
|
||||
class Print;
|
||||
class Layer;
|
||||
namespace EdgeGrid { class Grid; }
|
||||
|
||||
|
||||
|
|
@ -39,14 +41,31 @@ class SeamPlacer {
|
|||
public:
|
||||
void init(const Print& print);
|
||||
|
||||
Point get_seam(const size_t layer_idx, const SeamPosition seam_position,
|
||||
Point get_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
const ExtrusionLoop& loop, Point last_pos,
|
||||
coordf_t nozzle_diameter, const PrintObject* po,
|
||||
bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid);
|
||||
|
||||
using TreeType = AABBTreeIndirect::Tree<2, coord_t>;
|
||||
using AlignedBoxType = Eigen::AlignedBox<TreeType::CoordType, TreeType::NumDimensions>;
|
||||
|
||||
private:
|
||||
std::vector<ExPolygons> m_enforcers;
|
||||
std::vector<ExPolygons> m_blockers;
|
||||
|
||||
struct CustomTrianglesPerLayer {
|
||||
Polygons polys;
|
||||
TreeType tree;
|
||||
};
|
||||
|
||||
// Just a cache to save some lookups.
|
||||
const Layer* m_last_layer_po = nullptr;
|
||||
coordf_t m_last_print_z = -1.;
|
||||
const PrintObject* m_last_po = nullptr;
|
||||
|
||||
bool m_last_loop_was_external = true;
|
||||
|
||||
std::vector<std::vector<CustomTrianglesPerLayer>> m_enforcers;
|
||||
std::vector<std::vector<CustomTrianglesPerLayer>> m_blockers;
|
||||
std::vector<const PrintObject*> m_po_list;
|
||||
|
||||
//std::map<const PrintObject*, Point> m_last_seam_position;
|
||||
SeamHistory m_seam_history;
|
||||
|
|
@ -54,32 +73,33 @@ private:
|
|||
// Get indices of points inside enforcers and blockers.
|
||||
void get_enforcers_and_blockers(size_t layer_id,
|
||||
const Polygon& polygon,
|
||||
size_t po_id,
|
||||
std::vector<size_t>& enforcers_idxs,
|
||||
std::vector<size_t>& blockers_idxs) const;
|
||||
|
||||
// Apply penalties to points inside enforcers/blockers.
|
||||
void apply_custom_seam(const Polygon& polygon,
|
||||
void apply_custom_seam(const Polygon& polygon, size_t po_id,
|
||||
std::vector<float>& penalties,
|
||||
const std::vector<float>& lengths,
|
||||
int layer_id, SeamPosition seam_position) const;
|
||||
|
||||
// Return random point of a polygon. The distribution will be uniform
|
||||
// along the contour and account for enforcers and blockers.
|
||||
Point get_random_seam(size_t layer_idx, const Polygon& polygon,
|
||||
Point get_random_seam(size_t layer_idx, const Polygon& polygon, size_t po_id,
|
||||
bool* saw_custom = nullptr) const;
|
||||
|
||||
// Is there any enforcer/blocker on this layer?
|
||||
bool is_custom_seam_on_layer(size_t layer_id) const {
|
||||
return is_custom_enforcer_on_layer(layer_id)
|
||||
|| is_custom_blocker_on_layer(layer_id);
|
||||
bool is_custom_seam_on_layer(size_t layer_id, size_t po_idx) const {
|
||||
return is_custom_enforcer_on_layer(layer_id, po_idx)
|
||||
|| is_custom_blocker_on_layer(layer_id, po_idx);
|
||||
}
|
||||
|
||||
bool is_custom_enforcer_on_layer(size_t layer_id) const {
|
||||
return (! m_enforcers.empty() && ! m_enforcers[layer_id].empty());
|
||||
bool is_custom_enforcer_on_layer(size_t layer_id, size_t po_idx) const {
|
||||
return (! m_enforcers.at(po_idx).empty() && ! m_enforcers.at(po_idx)[layer_id].polys.empty());
|
||||
}
|
||||
|
||||
bool is_custom_blocker_on_layer(size_t layer_id) const {
|
||||
return (! m_blockers.empty() && ! m_blockers[layer_id].empty());
|
||||
bool is_custom_blocker_on_layer(size_t layer_id, size_t po_idx) const {
|
||||
return (! m_blockers.at(po_idx).empty() && ! m_blockers.at(po_idx)[layer_id].polys.empty());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -338,19 +338,19 @@ double rad2deg_dir(double angle)
|
|||
return rad2deg(angle);
|
||||
}
|
||||
|
||||
Point circle_taubin_newton(const Points::const_iterator& input_begin, const Points::const_iterator& input_end, size_t cycles)
|
||||
Point circle_center_taubin_newton(const Points::const_iterator& input_begin, const Points::const_iterator& input_end, size_t cycles)
|
||||
{
|
||||
Vec2ds tmp;
|
||||
tmp.reserve(std::distance(input_begin, input_end));
|
||||
std::transform(input_begin, input_end, std::back_inserter(tmp), [] (const Point& in) { return unscale(in); } );
|
||||
Vec2d center = circle_taubin_newton(tmp.cbegin(), tmp.end(), cycles);
|
||||
Vec2d center = circle_center_taubin_newton(tmp.cbegin(), tmp.end(), cycles);
|
||||
return Point::new_scale(center.x(), center.y());
|
||||
}
|
||||
|
||||
/// Adapted from work in "Circular and Linear Regression: Fitting circles and lines by least squares", pg 126
|
||||
/// Returns a point corresponding to the center of a circle for which all of the points from input_begin to input_end
|
||||
/// lie on.
|
||||
Vec2d circle_taubin_newton(const Vec2ds::const_iterator& input_begin, const Vec2ds::const_iterator& input_end, size_t cycles)
|
||||
Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_begin, const Vec2ds::const_iterator& input_end, size_t cycles)
|
||||
{
|
||||
// calculate the centroid of the data set
|
||||
const Vec2d sum = std::accumulate(input_begin, input_end, Vec2d(0,0));
|
||||
|
|
|
|||
|
|
@ -201,6 +201,57 @@ inline double ray_point_distance(const Line &iline, const Point &ipt)
|
|||
}
|
||||
|
||||
// Based on Liang-Barsky function by Daniel White @ http://www.skytopia.com/project/articles/compsci/clipping.html
|
||||
template<typename T>
|
||||
inline bool liang_barsky_line_clipping_interval(
|
||||
// Start and end points of the source line, result will be stored there as well.
|
||||
const Eigen::Matrix<T, 2, 1, Eigen::DontAlign> &x0,
|
||||
const Eigen::Matrix<T, 2, 1, Eigen::DontAlign> &v,
|
||||
// Bounding box to clip with.
|
||||
const BoundingBoxBase<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &bbox,
|
||||
std::pair<double, double> &out_interval)
|
||||
{
|
||||
double t0 = 0.0;
|
||||
double t1 = 1.0;
|
||||
// Traverse through left, right, bottom, top edges.
|
||||
auto clip_side = [&x0, &v, &bbox, &t0, &t1](double p, double q) -> bool {
|
||||
if (p == 0) {
|
||||
if (q < 0)
|
||||
// Line parallel to the bounding box edge is fully outside of the bounding box.
|
||||
return false;
|
||||
// else don't clip
|
||||
} else {
|
||||
double r = q / p;
|
||||
if (p < 0) {
|
||||
if (r > t1)
|
||||
// Fully clipped.
|
||||
return false;
|
||||
if (r > t0)
|
||||
// Partially clipped.
|
||||
t0 = r;
|
||||
} else {
|
||||
assert(p > 0);
|
||||
if (r < t0)
|
||||
// Fully clipped.
|
||||
return false;
|
||||
if (r < t1)
|
||||
// Partially clipped.
|
||||
t1 = r;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (clip_side(- v.x(), - bbox.min.x() + x0.x()) &&
|
||||
clip_side( v.x(), bbox.max.x() - x0.x()) &&
|
||||
clip_side(- v.y(), - bbox.min.y() + x0.y()) &&
|
||||
clip_side( v.y(), bbox.max.y() - x0.y())) {
|
||||
out_interval.first = t0;
|
||||
out_interval.second = t1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool liang_barsky_line_clipping(
|
||||
// Start and end points of the source line, result will be stored there as well.
|
||||
|
|
@ -210,49 +261,12 @@ inline bool liang_barsky_line_clipping(
|
|||
const BoundingBoxBase<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &bbox)
|
||||
{
|
||||
Eigen::Matrix<T, 2, 1, Eigen::DontAlign> v = x1 - x0;
|
||||
double t0 = 0.0;
|
||||
double t1 = 1.0;
|
||||
|
||||
// Traverse through left, right, bottom, top edges.
|
||||
for (int edge = 0; edge < 4; ++ edge)
|
||||
{
|
||||
double p, q;
|
||||
switch (edge) {
|
||||
case 0: p = - v.x(); q = - bbox.min.x() + x0.x(); break;
|
||||
case 1: p = v.x(); q = bbox.max.x() - x0.x(); break;
|
||||
case 2: p = - v.y(); q = - bbox.min.y() + x0.y(); break;
|
||||
default: p = v.y(); q = bbox.max.y() - x0.y(); break;
|
||||
}
|
||||
|
||||
if (p == 0) {
|
||||
if (q < 0)
|
||||
// Line parallel to the bounding box edge is fully outside of the bounding box.
|
||||
return false;
|
||||
// else don't clip
|
||||
} else {
|
||||
double r = q / p;
|
||||
if (p < 0) {
|
||||
if (r > t1)
|
||||
// Fully clipped.
|
||||
return false;
|
||||
if (r > t0)
|
||||
// Partially clipped.
|
||||
t0 = r;
|
||||
} else {
|
||||
assert(p > 0);
|
||||
if (r < t0)
|
||||
// Fully clipped.
|
||||
return false;
|
||||
if (r < t1)
|
||||
// Partially clipped.
|
||||
t1 = r;
|
||||
}
|
||||
}
|
||||
std::pair<double, double> interval;
|
||||
if (liang_barsky_line_clipping_interval(x0, v, bbox, interval)) {
|
||||
// Clipped successfully.
|
||||
x1 = x0 + interval.second * v;
|
||||
x0 += interval.first * v;
|
||||
}
|
||||
|
||||
// Clipped successfully.
|
||||
x1 = x0 + t1 * v;
|
||||
x0 += t0 * v;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -273,6 +287,35 @@ bool liang_barsky_line_clipping(
|
|||
return liang_barsky_line_clipping(x0clip, x1clip, bbox);
|
||||
}
|
||||
|
||||
// Ugly named variant, that accepts the squared line
|
||||
// Don't call me with a nearly zero length vector!
|
||||
template<typename T>
|
||||
int ray_circle_intersections_r2_lv2_c(T r2, T a, T b, T lv2, T c, std::pair<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>, Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &out)
|
||||
{
|
||||
T x0 = - a * c / lv2;
|
||||
T y0 = - b * c / lv2;
|
||||
T d = r2 - c * c / lv2;
|
||||
if (d < T(0))
|
||||
return 0;
|
||||
T mult = sqrt(d / lv2);
|
||||
out.first.x() = x0 + b * mult;
|
||||
out.first.y() = y0 - a * mult;
|
||||
out.second.x() = x0 - b * mult;
|
||||
out.second.y() = y0 + a * mult;
|
||||
return mult == T(0) ? 1 : 2;
|
||||
}
|
||||
template<typename T>
|
||||
int ray_circle_intersections(T r, T a, T b, T c, std::pair<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>, Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &out)
|
||||
{
|
||||
T lv2 = a * a + b * b;
|
||||
if (lv2 < T(SCALED_EPSILON * SCALED_EPSILON)) {
|
||||
//FIXME what is the correct epsilon?
|
||||
// What if the line touches the circle?
|
||||
return false;
|
||||
}
|
||||
return ray_circle_intersections_r2_lv2_c2(r * r, a, b, a * a + b * b, c, out);
|
||||
}
|
||||
|
||||
Pointf3s convex_hull(Pointf3s points);
|
||||
Polygon convex_hull(Points points);
|
||||
Polygon convex_hull(const Polygons &polygons);
|
||||
|
|
@ -298,12 +341,12 @@ template<typename T> T angle_to_0_2PI(T angle)
|
|||
}
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Points as an arc.
|
||||
Point circle_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Point circle_taubin_newton(const Points& input, size_t cycles = 20) { return circle_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
Point circle_center_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Point circle_center_taubin_newton(const Points& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Pointfs as an arc.
|
||||
Vec2d circle_taubin_newton(const Vec2ds::const_iterator& input_start, const Vec2ds::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Vec2d circle_taubin_newton(const Vec2ds& input, size_t cycles = 20) { return circle_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_start, const Vec2ds::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
|
||||
void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval);
|
||||
|
||||
|
|
|
|||
|
|
@ -100,6 +100,13 @@ bool Line::clip_with_bbox(const BoundingBox &bbox)
|
|||
return result;
|
||||
}
|
||||
|
||||
void Line::extend(double offset)
|
||||
{
|
||||
Vector offset_vector = (offset * this->vector().cast<double>().normalized()).cast<coord_t>();
|
||||
this->a -= offset_vector;
|
||||
this->b += offset_vector;
|
||||
}
|
||||
|
||||
Vec3d Linef3::intersect_plane(double z) const
|
||||
{
|
||||
auto v = (this->b - this->a).cast<double>();
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace line_alg {
|
|||
template<class L, class T, int N>
|
||||
double distance_to_squared(const L &line, const Vec<N, T> &point)
|
||||
{
|
||||
const Vec<N, double> v = line.vector().template cast<double>();
|
||||
const Vec<N, double> v = (line.b - line.a).template cast<double>();
|
||||
const Vec<N, double> va = (point - line.a).template cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
if (l2 == 0.0)
|
||||
|
|
@ -54,7 +54,8 @@ public:
|
|||
Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}
|
||||
explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; }
|
||||
void scale(double factor) { this->a *= factor; this->b *= factor; }
|
||||
void translate(double x, double y) { Vector v(x, y); this->a += v; this->b += v; }
|
||||
void translate(const Point &v) { this->a += v; this->b += v; }
|
||||
void translate(double x, double y) { this->translate(Point(x, y)); }
|
||||
void rotate(double angle, const Point ¢er) { this->a.rotate(angle, center); this->b.rotate(angle, center); }
|
||||
void reverse() { std::swap(this->a, this->b); }
|
||||
double length() const { return (b - a).cast<double>().norm(); }
|
||||
|
|
@ -75,6 +76,8 @@ public:
|
|||
double ccw(const Point& point) const { return point.ccw(*this); }
|
||||
// Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box.
|
||||
bool clip_with_bbox(const BoundingBox &bbox);
|
||||
// Extend the line from both sides by an offset.
|
||||
void extend(double offset);
|
||||
|
||||
static inline double distance_to_squared(const Point &point, const Point &a, const Point &b) { return line_alg::distance_to_squared(Line{a, b}, Vec<2, coord_t>{point}); }
|
||||
static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); }
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ Slic3r::arrangement::ArrangePolygon get_arrange_poly(const Model &model)
|
|||
std::copy(pts.begin(), pts.end(), std::back_inserter(apts));
|
||||
}
|
||||
|
||||
apts = Geometry::convex_hull(apts);
|
||||
apts = std::move(Geometry::convex_hull(apts).points);
|
||||
return ap;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ class MultiPoint
|
|||
public:
|
||||
Points points;
|
||||
|
||||
operator Points() const { return this->points; }
|
||||
|
||||
MultiPoint() {}
|
||||
MultiPoint(const MultiPoint &other) : points(other.points) {}
|
||||
MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {}
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
#include "PNGRead.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <cstdio>
|
||||
#include <png.h>
|
||||
|
||||
namespace Slic3r { namespace png {
|
||||
|
||||
struct PNGDescr {
|
||||
png_struct *png = nullptr; png_info *info = nullptr;
|
||||
|
||||
PNGDescr() = default;
|
||||
PNGDescr(const PNGDescr&) = delete;
|
||||
PNGDescr(PNGDescr&&) = delete;
|
||||
PNGDescr& operator=(const PNGDescr&) = delete;
|
||||
PNGDescr& operator=(PNGDescr&&) = delete;
|
||||
|
||||
~PNGDescr()
|
||||
{
|
||||
if (png && info) png_destroy_info_struct(png, &info);
|
||||
if (png) png_destroy_read_struct( &png, nullptr, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
bool is_png(const ReadBuf &rb)
|
||||
{
|
||||
static const constexpr int PNG_SIG_BYTES = 8;
|
||||
|
||||
#if PNG_LIBPNG_VER_MINOR <= 2
|
||||
// Earlier libpng versions had png_sig_cmp(png_bytep, ...) which is not
|
||||
// a const pointer. It is not possible to cast away the const qualifier from
|
||||
// the input buffer so... yes... life is challenging...
|
||||
png_byte buf[PNG_SIG_BYTES];
|
||||
auto inbuf = static_cast<const std::uint8_t *>(rb.buf);
|
||||
std::copy(inbuf, inbuf + PNG_SIG_BYTES, buf);
|
||||
#else
|
||||
auto buf = static_cast<png_const_bytep>(rb.buf);
|
||||
#endif
|
||||
|
||||
return rb.sz >= PNG_SIG_BYTES && !png_sig_cmp(buf, 0, PNG_SIG_BYTES);
|
||||
}
|
||||
|
||||
// Buffer read callback for libpng. It provides an allocated output buffer and
|
||||
// the amount of data it desires to read from the input.
|
||||
void png_read_callback(png_struct *png_ptr,
|
||||
png_bytep outBytes,
|
||||
png_size_t byteCountToRead)
|
||||
{
|
||||
// Retrieve our input buffer through the png_ptr
|
||||
auto reader = static_cast<IStream *>(png_get_io_ptr(png_ptr));
|
||||
|
||||
if (!reader || !reader->is_ok()) return;
|
||||
|
||||
reader->read(static_cast<std::uint8_t *>(outBytes), byteCountToRead);
|
||||
}
|
||||
|
||||
bool decode_png(IStream &in_buf, ImageGreyscale &out_img)
|
||||
{
|
||||
static const constexpr int PNG_SIG_BYTES = 8;
|
||||
|
||||
std::vector<png_byte> sig(PNG_SIG_BYTES, 0);
|
||||
in_buf.read(sig.data(), PNG_SIG_BYTES);
|
||||
if (!png_check_sig(sig.data(), PNG_SIG_BYTES))
|
||||
return false;
|
||||
|
||||
PNGDescr dsc;
|
||||
dsc.png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr,
|
||||
nullptr);
|
||||
|
||||
if(!dsc.png) return false;
|
||||
|
||||
dsc.info = png_create_info_struct(dsc.png);
|
||||
if(!dsc.info) return false;
|
||||
|
||||
png_set_read_fn(dsc.png, static_cast<void *>(&in_buf), png_read_callback);
|
||||
|
||||
// Tell that we have already read the first bytes to check the signature
|
||||
png_set_sig_bytes(dsc.png, PNG_SIG_BYTES);
|
||||
|
||||
png_read_info(dsc.png, dsc.info);
|
||||
|
||||
out_img.cols = png_get_image_width(dsc.png, dsc.info);
|
||||
out_img.rows = png_get_image_height(dsc.png, dsc.info);
|
||||
size_t color_type = png_get_color_type(dsc.png, dsc.info);
|
||||
size_t bit_depth = png_get_bit_depth(dsc.png, dsc.info);
|
||||
|
||||
if (color_type != PNG_COLOR_TYPE_GRAY || bit_depth != 8)
|
||||
return false;
|
||||
|
||||
out_img.buf.resize(out_img.rows * out_img.cols);
|
||||
|
||||
auto readbuf = static_cast<png_bytep>(out_img.buf.data());
|
||||
for (size_t r = 0; r < out_img.rows; ++r)
|
||||
png_read_row(dsc.png, readbuf + r * out_img.cols, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::png
|
||||
225
src/libslic3r/PNGReadWrite.cpp
Normal file
225
src/libslic3r/PNGReadWrite.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
#include "PNGReadWrite.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <cstdio>
|
||||
#include <png.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
|
||||
namespace Slic3r { namespace png {
|
||||
|
||||
struct PNGDescr {
|
||||
png_struct *png = nullptr; png_info *info = nullptr;
|
||||
|
||||
PNGDescr() = default;
|
||||
PNGDescr(const PNGDescr&) = delete;
|
||||
PNGDescr(PNGDescr&&) = delete;
|
||||
PNGDescr& operator=(const PNGDescr&) = delete;
|
||||
PNGDescr& operator=(PNGDescr&&) = delete;
|
||||
|
||||
~PNGDescr()
|
||||
{
|
||||
if (png && info) png_destroy_info_struct(png, &info);
|
||||
if (png) png_destroy_read_struct( &png, nullptr, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
bool is_png(const ReadBuf &rb)
|
||||
{
|
||||
static const constexpr int PNG_SIG_BYTES = 8;
|
||||
|
||||
#if PNG_LIBPNG_VER_MINOR <= 2
|
||||
// Earlier libpng versions had png_sig_cmp(png_bytep, ...) which is not
|
||||
// a const pointer. It is not possible to cast away the const qualifier from
|
||||
// the input buffer so... yes... life is challenging...
|
||||
png_byte buf[PNG_SIG_BYTES];
|
||||
auto inbuf = static_cast<const std::uint8_t *>(rb.buf);
|
||||
std::copy(inbuf, inbuf + PNG_SIG_BYTES, buf);
|
||||
#else
|
||||
auto buf = static_cast<png_const_bytep>(rb.buf);
|
||||
#endif
|
||||
|
||||
return rb.sz >= PNG_SIG_BYTES && !png_sig_cmp(buf, 0, PNG_SIG_BYTES);
|
||||
}
|
||||
|
||||
// Buffer read callback for libpng. It provides an allocated output buffer and
|
||||
// the amount of data it desires to read from the input.
|
||||
static void png_read_callback(png_struct *png_ptr,
|
||||
png_bytep outBytes,
|
||||
png_size_t byteCountToRead)
|
||||
{
|
||||
// Retrieve our input buffer through the png_ptr
|
||||
auto reader = static_cast<IStream *>(png_get_io_ptr(png_ptr));
|
||||
|
||||
if (!reader || !reader->is_ok()) return;
|
||||
|
||||
reader->read(static_cast<std::uint8_t *>(outBytes), byteCountToRead);
|
||||
}
|
||||
|
||||
bool decode_png(IStream &in_buf, ImageGreyscale &out_img)
|
||||
{
|
||||
static const constexpr int PNG_SIG_BYTES = 8;
|
||||
|
||||
std::vector<png_byte> sig(PNG_SIG_BYTES, 0);
|
||||
in_buf.read(sig.data(), PNG_SIG_BYTES);
|
||||
if (!png_check_sig(sig.data(), PNG_SIG_BYTES))
|
||||
return false;
|
||||
|
||||
PNGDescr dsc;
|
||||
dsc.png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr,
|
||||
nullptr);
|
||||
|
||||
if(!dsc.png) return false;
|
||||
|
||||
dsc.info = png_create_info_struct(dsc.png);
|
||||
if(!dsc.info) return false;
|
||||
|
||||
png_set_read_fn(dsc.png, static_cast<void *>(&in_buf), png_read_callback);
|
||||
|
||||
// Tell that we have already read the first bytes to check the signature
|
||||
png_set_sig_bytes(dsc.png, PNG_SIG_BYTES);
|
||||
|
||||
png_read_info(dsc.png, dsc.info);
|
||||
|
||||
out_img.cols = png_get_image_width(dsc.png, dsc.info);
|
||||
out_img.rows = png_get_image_height(dsc.png, dsc.info);
|
||||
size_t color_type = png_get_color_type(dsc.png, dsc.info);
|
||||
size_t bit_depth = png_get_bit_depth(dsc.png, dsc.info);
|
||||
|
||||
if (color_type != PNG_COLOR_TYPE_GRAY || bit_depth != 8)
|
||||
return false;
|
||||
|
||||
out_img.buf.resize(out_img.rows * out_img.cols);
|
||||
|
||||
auto readbuf = static_cast<png_bytep>(out_img.buf.data());
|
||||
for (size_t r = 0; r < out_img.rows; ++r)
|
||||
png_read_row(dsc.png, readbuf + r * out_img.cols, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Down to earth function to store a packed RGB image to file. Mostly useful for debugging purposes.
|
||||
// Based on https://www.lemoda.net/c/write-png/
|
||||
bool write_rgb_to_file(const char *file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// Forward declaration due to the gotos.
|
||||
png_structp png_ptr = nullptr;
|
||||
png_infop info_ptr = nullptr;
|
||||
png_byte **row_pointers = nullptr;
|
||||
|
||||
FILE *fp = boost::nowide::fopen(file_name_utf8, "wb");
|
||||
if (! fp) {
|
||||
BOOST_LOG_TRIVIAL(error) << "write_png_file: File could not be opened for writing: " << file_name_utf8;
|
||||
goto fopen_failed;
|
||||
}
|
||||
|
||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||
if (! png_ptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "write_png_file: png_create_write_struct() failed";
|
||||
goto png_create_write_struct_failed;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (! info_ptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "write_png_file: png_create_info_struct() failed";
|
||||
goto png_create_info_struct_failed;
|
||||
}
|
||||
|
||||
// Set up error handling.
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
BOOST_LOG_TRIVIAL(error) << "write_png_file: setjmp() failed";
|
||||
goto png_failure;
|
||||
}
|
||||
|
||||
// Set image attributes.
|
||||
png_set_IHDR(png_ptr,
|
||||
info_ptr,
|
||||
png_uint_32(width),
|
||||
png_uint_32(height),
|
||||
8, // depth
|
||||
PNG_COLOR_TYPE_RGB,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
// Initialize rows of PNG.
|
||||
row_pointers = reinterpret_cast<png_byte**>(::png_malloc(png_ptr, height * sizeof(png_byte*)));
|
||||
for (size_t y = 0; y < height; ++ y) {
|
||||
auto row = reinterpret_cast<png_byte*>(::png_malloc(png_ptr, sizeof(uint8_t) * width * 3));
|
||||
row_pointers[y] = row;
|
||||
memcpy(row, data_rgb + width * y * 3, sizeof(uint8_t) * width * 3);
|
||||
}
|
||||
|
||||
// Write the image data to "fp".
|
||||
png_init_io(png_ptr, fp);
|
||||
png_set_rows(png_ptr, info_ptr, row_pointers);
|
||||
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, nullptr);
|
||||
|
||||
for (size_t y = 0; y < height; ++ y)
|
||||
png_free(png_ptr, row_pointers[y]);
|
||||
png_free(png_ptr, row_pointers);
|
||||
|
||||
result = true;
|
||||
|
||||
png_failure:
|
||||
png_create_info_struct_failed:
|
||||
::png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
png_create_write_struct_failed:
|
||||
::fclose(fp);
|
||||
fopen_failed:
|
||||
return result;
|
||||
}
|
||||
|
||||
bool write_rgb_to_file(const std::string &file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb)
|
||||
{
|
||||
return write_rgb_to_file(file_name_utf8.c_str(), width, height, data_rgb);
|
||||
}
|
||||
|
||||
bool write_rgb_to_file(const std::string &file_name_utf8, size_t width, size_t height, const std::vector<uint8_t> &data_rgb)
|
||||
{
|
||||
assert(width * height * 3 == data_rgb.size());
|
||||
return write_rgb_to_file(file_name_utf8.c_str(), width, height, data_rgb.data());
|
||||
}
|
||||
|
||||
// Scaled variants are mostly useful for debugging purposes, for example to export images of low resolution distance fileds.
|
||||
// Scaling is done by multiplying rows and columns without any smoothing to emphasise the original pixels.
|
||||
bool write_rgb_to_file_scaled(const char *file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb, size_t scale)
|
||||
{
|
||||
if (scale <= 1)
|
||||
return write_rgb_to_file(file_name_utf8, width, height, data_rgb);
|
||||
else {
|
||||
std::vector<uint8_t> scaled(width * height * 3 * scale * scale);
|
||||
uint8_t *dst = scaled.data();
|
||||
for (size_t r = 0; r < height; ++ r) {
|
||||
for (size_t repr = 0; repr < scale; ++ repr) {
|
||||
const uint8_t *row = data_rgb + width * 3 * r;
|
||||
for (size_t c = 0; c < width; ++ c) {
|
||||
for (size_t repc = 0; repc < scale; ++ repc) {
|
||||
*dst ++ = row[0];
|
||||
*dst ++ = row[1];
|
||||
*dst ++ = row[2];
|
||||
}
|
||||
row += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
return write_rgb_to_file(file_name_utf8, width * scale, height * scale, scaled.data());
|
||||
}
|
||||
}
|
||||
|
||||
bool write_rgb_to_file_scaled(const std::string &file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb, size_t scale)
|
||||
{
|
||||
return write_rgb_to_file_scaled(file_name_utf8.c_str(), width, height, data_rgb, scale);
|
||||
}
|
||||
|
||||
bool write_rgb_to_file_scaled(const std::string &file_name_utf8, size_t width, size_t height, const std::vector<uint8_t> &data_rgb, size_t scale)
|
||||
{
|
||||
assert(width * height * 3 == data_rgb.size());
|
||||
return write_rgb_to_file_scaled(file_name_utf8.c_str(), width, height, data_rgb.data(), scale);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::png
|
||||
|
|
@ -65,6 +65,18 @@ template<class Img> bool decode_png(const ReadBuf &in_buf, Img &out_img)
|
|||
|
||||
// TODO: std::istream of FILE* could be similarly adapted in case its needed...
|
||||
|
||||
|
||||
|
||||
// Down to earth function to store a packed RGB image to file. Mostly useful for debugging purposes.
|
||||
bool write_rgb_to_file(const char *file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb);
|
||||
bool write_rgb_to_file(const std::string &file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb);
|
||||
bool write_rgb_to_file(const std::string &file_name_utf8, size_t width, size_t height, const std::vector<uint8_t> &data_rgb);
|
||||
// Scaled variants are mostly useful for debugging purposes, for example to export images of low resolution distance fileds.
|
||||
// Scaling is done by multiplying rows and columns without any smoothing to emphasise the original pixels.
|
||||
bool write_rgb_to_file_scaled(const char *file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb, size_t scale);
|
||||
bool write_rgb_to_file_scaled(const std::string &file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb, size_t scale);
|
||||
bool write_rgb_to_file_scaled(const std::string &file_name_utf8, size_t width, size_t height, const std::vector<uint8_t> &data_rgb, size_t scale);
|
||||
|
||||
}} // namespace Slic3r::png
|
||||
|
||||
#endif // PNGREAD_HPP
|
||||
|
|
@ -158,7 +158,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||
// get non-overhang paths by intersecting this loop with the grown lower slices
|
||||
extrusion_paths_append(
|
||||
paths,
|
||||
intersection_pl(loop.polygon, perimeter_generator.lower_slices_polygons()),
|
||||
intersection_pl((Polygons)loop.polygon, perimeter_generator.lower_slices_polygons()),
|
||||
role,
|
||||
is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(),
|
||||
is_external ? perimeter_generator.ext_perimeter_flow.width : perimeter_generator.perimeter_flow.width,
|
||||
|
|
@ -169,7 +169,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||
// the loop centerline and original lower slices is >= half nozzle diameter
|
||||
extrusion_paths_append(
|
||||
paths,
|
||||
diff_pl(loop.polygon, perimeter_generator.lower_slices_polygons()),
|
||||
diff_pl((Polygons)loop.polygon, perimeter_generator.lower_slices_polygons()),
|
||||
erOverhangPerimeter,
|
||||
perimeter_generator.mm3_per_mm_overhang(),
|
||||
perimeter_generator.overhang_flow.width,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "Line.hpp"
|
||||
#include "MultiPoint.hpp"
|
||||
#include "Int128.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -176,6 +177,19 @@ Point Point::projection_onto(const Line &line) const
|
|||
return ((line.a - *this).cast<double>().squaredNorm() < (line.b - *this).cast<double>().squaredNorm()) ? line.a : line.b;
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const Points &pts)
|
||||
{
|
||||
return BoundingBox(pts);
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const std::vector<Points> &pts)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
for (const Points &p : pts)
|
||||
bbox.merge(get_extents(p));
|
||||
return bbox;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf)
|
||||
{
|
||||
return stm << pointf(0) << "," << pointf(1);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
class BoundingBox;
|
||||
class Line;
|
||||
class MultiPoint;
|
||||
class Point;
|
||||
|
|
@ -55,23 +56,20 @@ typedef Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign> Transform3d
|
|||
|
||||
inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0) || (lhs(0) == rhs(0) && lhs(1) < rhs(1)); }
|
||||
|
||||
inline int32_t cross2(const Vec2i32 &v1, const Vec2i32 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
|
||||
// One likely does not want to perform the cross product with a 32bit accumulator.
|
||||
//inline int32_t cross2(const Vec2i32 &v1, const Vec2i32 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
|
||||
inline int64_t cross2(const Vec2i64 &v1, const Vec2i64 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
|
||||
inline float cross2(const Vec2f &v1, const Vec2f &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
|
||||
inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
|
||||
|
||||
template<class T, int N> Eigen::Matrix<T, 2, 1, Eigen::DontAlign>
|
||||
to_2d(const Eigen::Matrix<T, N, 1, Eigen::DontAlign> &ptN) { return {ptN(0), ptN(1)}; }
|
||||
template<typename T, int Options>
|
||||
inline Eigen::Matrix<T, 2, 1, Eigen::DontAlign> perp(const Eigen::MatrixBase<Eigen::Matrix<T, 2, 1, Options>> &v) { return Eigen::Matrix<T, 2, 1, Eigen::DontAlign>(- v.y(), v.x()); }
|
||||
|
||||
//inline Vec2i32 to_2d(const Vec3i32 &pt3) { return Vec2i32(pt3(0), pt3(1)); }
|
||||
//inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); }
|
||||
//inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); }
|
||||
//inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
|
||||
template<class T, int N, int Options>
|
||||
Eigen::Matrix<T, 2, 1, Eigen::DontAlign> to_2d(const Eigen::MatrixBase<Eigen::Matrix<T, N, 1, Options>> &ptN) { return { ptN(0), ptN(1) }; }
|
||||
|
||||
inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); }
|
||||
inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); }
|
||||
inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(int64_t(v(0)), int64_t(v(1)), int64_t(z)); }
|
||||
inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); }
|
||||
template<class T, int Options>
|
||||
Eigen::Matrix<T, 3, 1, Eigen::DontAlign> to_3d(const Eigen::MatrixBase<Eigen::Matrix<T, 2, 1, Options>> & pt, const T z) { return { pt(0), pt(1), z }; }
|
||||
|
||||
inline Vec2d unscale(coord_t x, coord_t y) { return Vec2d(unscale<double>(x), unscale<double>(y)); }
|
||||
inline Vec2d unscale(const Vec2crd &pt) { return Vec2d(unscale<double>(pt(0)), unscale<double>(pt(1))); }
|
||||
|
|
@ -132,6 +130,7 @@ public:
|
|||
|
||||
void rotate(double angle, const Point ¢er);
|
||||
Point rotated(double angle) const { Point res(*this); res.rotate(angle); return res; }
|
||||
Point rotated(double cos_a, double sin_a) const { Point res(*this); res.rotate(cos_a, sin_a); return res; }
|
||||
Point rotated(double angle, const Point ¢er) const { Point res(*this); res.rotate(angle, center); return res; }
|
||||
int nearest_point_index(const Points &points) const;
|
||||
int nearest_point_index(const PointConstPtrs &points) const;
|
||||
|
|
@ -174,6 +173,15 @@ inline bool is_approx(const Vec3d &p1, const Vec3d &p2, double epsilon = EPSILON
|
|||
return d.x() < epsilon && d.y() < epsilon && d.z() < epsilon;
|
||||
}
|
||||
|
||||
inline Point lerp(const Point &a, const Point &b, double t)
|
||||
{
|
||||
assert((t >= -EPSILON) && (t <= 1. + EPSILON));
|
||||
return ((1. - t) * a.cast<double>() + t * b.cast<double>()).cast<coord_t>();
|
||||
}
|
||||
|
||||
extern BoundingBox get_extents(const Points &pts);
|
||||
extern BoundingBox get_extents(const std::vector<Points> &pts);
|
||||
|
||||
namespace int128 {
|
||||
// Exact orientation predicate,
|
||||
// returns +1: CCW, 0: collinear, -1: CW.
|
||||
|
|
@ -291,6 +299,33 @@ public:
|
|||
std::make_pair(nullptr, std::numeric_limits<double>::max());
|
||||
}
|
||||
|
||||
// Returns all pairs of values and squared distances.
|
||||
std::vector<std::pair<const ValueType*, double>> find_all(const Vec2crd &pt) {
|
||||
// Iterate over 4 closest grid cells around pt,
|
||||
// Round pt to a closest grid_cell corner.
|
||||
Vec2crd grid_corner((pt(0)+(m_grid_resolution>>1))>>m_grid_log2, (pt(1)+(m_grid_resolution>>1))>>m_grid_log2);
|
||||
// For four neighbors of grid_corner:
|
||||
std::vector<std::pair<const ValueType*, double>> out;
|
||||
const double r2 = double(m_search_radius) * m_search_radius;
|
||||
for (coord_t neighbor_y = -1; neighbor_y < 1; ++ neighbor_y) {
|
||||
for (coord_t neighbor_x = -1; neighbor_x < 1; ++ neighbor_x) {
|
||||
// Range of fragment starts around grid_corner, close to pt.
|
||||
auto range = m_map.equal_range(Vec2crd(grid_corner(0) + neighbor_x, grid_corner(1) + neighbor_y));
|
||||
// Find the map entry closest to pt.
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
const ValueType &value = it->second;
|
||||
const Vec2crd *pt2 = m_point_accessor(value);
|
||||
if (pt2 != nullptr) {
|
||||
const double d2 = (pt - *pt2).cast<double>().squaredNorm();
|
||||
if (d2 <= r2)
|
||||
out.emplace_back(&value, d2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef typename std::unordered_multimap<Vec2crd, ValueType, PointHash> map_type;
|
||||
PointAccessor m_point_accessor;
|
||||
|
|
|
|||
|
|
@ -298,11 +298,6 @@ void Polygon::densify(float min_length, std::vector<float>* lengths_ptr)
|
|||
assert(points.size() == lengths.size() - 1);
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const Points &points)
|
||||
{
|
||||
return BoundingBox(points);
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const Polygon &poly)
|
||||
{
|
||||
return poly.bounding_box();
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@ typedef std::vector<Polygon> Polygons;
|
|||
class Polygon : public MultiPoint
|
||||
{
|
||||
public:
|
||||
operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; }
|
||||
operator Polyline() const { return this->split_at_first_point(); }
|
||||
explicit operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; }
|
||||
explicit operator Polyline() const { return this->split_at_first_point(); }
|
||||
Point& operator[](Points::size_type idx) { return this->points[idx]; }
|
||||
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
|
||||
|
||||
Polygon() {}
|
||||
Polygon() = default;
|
||||
virtual ~Polygon() = default;
|
||||
explicit Polygon(const Points &points) : MultiPoint(points) {}
|
||||
Polygon(std::initializer_list<Point> points) : MultiPoint(points) {}
|
||||
|
|
@ -74,7 +74,6 @@ public:
|
|||
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
|
||||
inline bool operator!=(const Polygon &lhs, const Polygon &rhs) { return lhs.points != rhs.points; }
|
||||
|
||||
extern BoundingBox get_extents(const Points &points);
|
||||
extern BoundingBox get_extents(const Polygon &poly);
|
||||
extern BoundingBox get_extents(const Polygons &polygons);
|
||||
extern BoundingBox get_extents_rotated(const Polygon &poly, double angle);
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ BoundingBox get_extents(const Polylines &polylines)
|
|||
if (! polylines.empty()) {
|
||||
bb = polylines.front().bounding_box();
|
||||
for (size_t i = 1; i < polylines.size(); ++ i)
|
||||
bb.merge(polylines[i]);
|
||||
bb.merge(polylines[i].points);
|
||||
}
|
||||
return bb;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
operator Polylines() const;
|
||||
operator Line() const;
|
||||
explicit operator Polylines() const;
|
||||
explicit operator Line() const;
|
||||
const Point& last_point() const override { return this->points.back(); }
|
||||
|
||||
const Point& leftmost_point() const;
|
||||
|
|
|
|||
|
|
@ -427,7 +427,7 @@ const std::vector<std::string>& Preset::print_options()
|
|||
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
|
||||
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
|
||||
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
|
||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects",
|
||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects",
|
||||
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
|
||||
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "single_extruder_multi_material_priming",
|
||||
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits"
|
||||
|
|
@ -439,7 +439,7 @@ const std::vector<std::string>& Preset::filament_options()
|
|||
{
|
||||
static std::vector<std::string> s_opts {
|
||||
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
|
||||
"extrusion_multiplier", "filament_density", "filament_cost", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time",
|
||||
"extrusion_multiplier", "filament_density", "filament_cost", "filament_spool_weight", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time",
|
||||
"filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves",
|
||||
"filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower",
|
||||
"temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed",
|
||||
|
|
@ -1840,8 +1840,11 @@ namespace PresetUtils {
|
|||
{
|
||||
std::string out;
|
||||
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
|
||||
if (pm != nullptr && !pm->bed_model.empty())
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model;
|
||||
if (pm != nullptr && !pm->bed_model.empty()) {
|
||||
out = Slic3r::data_dir() + "/vendor/" + preset.vendor->id + "/" + pm->bed_model;
|
||||
if (!boost::filesystem::exists(boost::filesystem::path(out)))
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -1849,8 +1852,11 @@ namespace PresetUtils {
|
|||
{
|
||||
std::string out;
|
||||
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
|
||||
if (pm != nullptr && !pm->bed_texture.empty())
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture;
|
||||
if (pm != nullptr && !pm->bed_texture.empty()) {
|
||||
out = Slic3r::data_dir() + "/vendor/" + preset.vendor->id + "/" + pm->bed_texture;
|
||||
if (!boost::filesystem::exists(boost::filesystem::path(out)))
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
} // namespace PresetUtils
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||
"filament_density",
|
||||
"filament_notes",
|
||||
"filament_cost",
|
||||
"filament_spool_weight",
|
||||
"first_layer_acceleration",
|
||||
"first_layer_bed_temperature",
|
||||
"first_layer_speed",
|
||||
|
|
@ -1220,9 +1221,9 @@ static inline bool sequential_print_horizontal_clearance_valid(const Print &prin
|
|||
// instance.shift is a position of a centered object, while model object may not be centered.
|
||||
// Conver the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
|
||||
convex_hull.translate(instance.shift - print_object->center_offset());
|
||||
if (! intersection(convex_hulls_other, convex_hull).empty())
|
||||
if (! intersection(convex_hulls_other, (Polygons)convex_hull).empty())
|
||||
return false;
|
||||
polygons_append(convex_hulls_other, convex_hull);
|
||||
convex_hulls_other.emplace_back(std::move(convex_hull));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -470,12 +470,14 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
def->enum_values.push_back("rectilinear");
|
||||
def->enum_values.push_back("monotonic");
|
||||
def->enum_values.push_back("alignedrectilinear");
|
||||
def->enum_values.push_back("concentric");
|
||||
def->enum_values.push_back("hilbertcurve");
|
||||
def->enum_values.push_back("archimedeanchords");
|
||||
def->enum_values.push_back("octagramspiral");
|
||||
def->enum_labels.push_back(L("Rectilinear"));
|
||||
def->enum_labels.push_back(L("Monotonic"));
|
||||
def->enum_labels.push_back(L("Aligned Rectilinear"));
|
||||
def->enum_labels.push_back(L("Concentric"));
|
||||
def->enum_labels.push_back(L("Hilbert Curve"));
|
||||
def->enum_labels.push_back(L("Archimedean Chords"));
|
||||
|
|
@ -493,7 +495,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values = def_top_fill_pattern->enum_values;
|
||||
def->enum_labels = def_top_fill_pattern->enum_labels;
|
||||
def->aliases = def_top_fill_pattern->aliases;
|
||||
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
|
||||
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipMonotonic));
|
||||
|
||||
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
|
||||
def->label = L("External perimeters");
|
||||
|
|
@ -816,6 +818,16 @@ void PrintConfigDef::init_fff_params()
|
|||
def->min = 0;
|
||||
def->set_default_value(new ConfigOptionFloats { 0. });
|
||||
|
||||
def = this->add("filament_spool_weight", coFloats);
|
||||
def->label = L("Spool weight");
|
||||
def->tooltip = L("Enter weight of the empty filament spool. "
|
||||
"One may weigh a partially consumed filament spool before printing and one may compare the measured weight "
|
||||
"with the calculated weight of the filament with the spool to find out whether the amount "
|
||||
"of filament on the spool is sufficient to finish the print.");
|
||||
def->sidetext = L("g");
|
||||
def->min = 0;
|
||||
def->set_default_value(new ConfigOptionFloats { 0. });
|
||||
|
||||
def = this->add("filament_settings_id", coStrings);
|
||||
def->set_default_value(new ConfigOptionStrings { "" });
|
||||
def->cli = ConfigOptionDef::nocli;
|
||||
|
|
@ -881,6 +893,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->tooltip = L("Fill pattern for general low-density infill.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
def->enum_values.push_back("rectilinear");
|
||||
def->enum_values.push_back("alignedrectilinear");
|
||||
def->enum_values.push_back("grid");
|
||||
def->enum_values.push_back("triangles");
|
||||
def->enum_values.push_back("stars");
|
||||
|
|
@ -896,6 +909,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.push_back("adaptivecubic");
|
||||
def->enum_values.push_back("supportcubic");
|
||||
def->enum_labels.push_back(L("Rectilinear"));
|
||||
def->enum_labels.push_back(L("Aligned Rectilinear"));
|
||||
def->enum_labels.push_back(L("Grid"));
|
||||
def->enum_labels.push_back(L("Triangles"));
|
||||
def->enum_labels.push_back(L("Stars"));
|
||||
|
|
@ -1060,6 +1074,55 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(1));
|
||||
|
||||
auto def_infill_anchor_min = def = this->add("infill_anchor", coFloatOrPercent);
|
||||
def->label = L("Length of the infill anchor");
|
||||
def->category = L("Advanced");
|
||||
def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. "
|
||||
"If expressed as percentage (example: 15%) it is calculated over infill extrusion width. "
|
||||
"PrusaSlicer tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment "
|
||||
"shorter than infill_anchor_max is found, the infill line is connected to a perimeter segment at just one side "
|
||||
"and the length of the perimeter segment taken is limited to this parameter, but no longer than anchor_length_max. "
|
||||
"Set this parameter to zero to disable anchoring perimeters connected to a single infill line.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->ratio_over = "infill_extrusion_width";
|
||||
def->gui_type = "f_enum_open";
|
||||
def->enum_values.push_back("0");
|
||||
def->enum_values.push_back("1");
|
||||
def->enum_values.push_back("2");
|
||||
def->enum_values.push_back("5");
|
||||
def->enum_values.push_back("10");
|
||||
def->enum_values.push_back("1000");
|
||||
def->enum_labels.push_back(L("0 (no open anchors)"));
|
||||
def->enum_labels.push_back("1 mm");
|
||||
def->enum_labels.push_back("2 mm");
|
||||
def->enum_labels.push_back("5 mm");
|
||||
def->enum_labels.push_back("10 mm");
|
||||
def->enum_labels.push_back(L("1000 (unlimited)"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(600, true));
|
||||
|
||||
def = this->add("infill_anchor_max", coFloatOrPercent);
|
||||
def->label = L("Maximum length of the infill anchor");
|
||||
def->category = def_infill_anchor_min->category;
|
||||
def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. "
|
||||
"If expressed as percentage (example: 15%) it is calculated over infill extrusion width. "
|
||||
"PrusaSlicer tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment "
|
||||
"shorter than this parameter is found, the infill line is connected to a perimeter segment at just one side "
|
||||
"and the length of the perimeter segment taken is limited to infill_anchor, but no longer than this parameter. "
|
||||
"Set this parameter to zero to disable anchoring.");
|
||||
def->sidetext = def_infill_anchor_min->sidetext;
|
||||
def->ratio_over = def_infill_anchor_min->ratio_over;
|
||||
def->gui_type = def_infill_anchor_min->gui_type;
|
||||
def->enum_values = def_infill_anchor_min->enum_values;
|
||||
def->enum_labels.push_back(L("0 (not anchored)"));
|
||||
def->enum_labels.push_back("1 mm");
|
||||
def->enum_labels.push_back("2 mm");
|
||||
def->enum_labels.push_back("5 mm");
|
||||
def->enum_labels.push_back("10 mm");
|
||||
def->enum_labels.push_back(L("1000 (unlimited)"));
|
||||
def->mode = def_infill_anchor_min->mode;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(50, false));
|
||||
|
||||
def = this->add("infill_extruder", coInt);
|
||||
def->label = L("Infill extruder");
|
||||
def->category = L("Extruders");
|
||||
|
|
@ -1527,8 +1590,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def = this->add("perimeter_acceleration", coFloat);
|
||||
def->label = L("Perimeters");
|
||||
def->tooltip = L("This is the acceleration your printer will use for perimeters. "
|
||||
"A high value like 9000 usually gives good results if your hardware is up to the job. "
|
||||
"Set zero to disable acceleration control for perimeters.");
|
||||
"Set zero to disable acceleration control for perimeters.");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ enum AuthorizationType {
|
|||
};
|
||||
|
||||
enum InfillPattern : int {
|
||||
ipRectilinear, ipMonotonic, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||
ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipCount,
|
||||
};
|
||||
|
||||
|
|
@ -145,6 +145,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<InfillPattern>::g
|
|||
if (keys_map.empty()) {
|
||||
keys_map["rectilinear"] = ipRectilinear;
|
||||
keys_map["monotonic"] = ipMonotonic;
|
||||
keys_map["alignedrectilinear"] = ipAlignedRectilinear;
|
||||
keys_map["grid"] = ipGrid;
|
||||
keys_map["triangles"] = ipTriangles;
|
||||
keys_map["stars"] = ipStars;
|
||||
|
|
@ -530,6 +531,8 @@ public:
|
|||
ConfigOptionPercent fill_density;
|
||||
ConfigOptionEnum<InfillPattern> fill_pattern;
|
||||
ConfigOptionFloat gap_fill_speed;
|
||||
ConfigOptionFloatOrPercent infill_anchor;
|
||||
ConfigOptionFloatOrPercent infill_anchor_max;
|
||||
ConfigOptionInt infill_extruder;
|
||||
ConfigOptionFloatOrPercent infill_extrusion_width;
|
||||
ConfigOptionInt infill_every_layers;
|
||||
|
|
@ -581,6 +584,8 @@ protected:
|
|||
OPT_PTR(fill_density);
|
||||
OPT_PTR(fill_pattern);
|
||||
OPT_PTR(gap_fill_speed);
|
||||
OPT_PTR(infill_anchor);
|
||||
OPT_PTR(infill_anchor_max);
|
||||
OPT_PTR(infill_extruder);
|
||||
OPT_PTR(infill_extrusion_width);
|
||||
OPT_PTR(infill_every_layers);
|
||||
|
|
@ -681,6 +686,7 @@ public:
|
|||
ConfigOptionStrings filament_type;
|
||||
ConfigOptionBools filament_soluble;
|
||||
ConfigOptionFloats filament_cost;
|
||||
ConfigOptionFloats filament_spool_weight;
|
||||
ConfigOptionFloats filament_max_volumetric_speed;
|
||||
ConfigOptionFloats filament_loading_speed;
|
||||
ConfigOptionFloats filament_loading_speed_start;
|
||||
|
|
@ -757,6 +763,7 @@ protected:
|
|||
OPT_PTR(filament_type);
|
||||
OPT_PTR(filament_soluble);
|
||||
OPT_PTR(filament_cost);
|
||||
OPT_PTR(filament_spool_weight);
|
||||
OPT_PTR(filament_max_volumetric_speed);
|
||||
OPT_PTR(filament_loading_speed);
|
||||
OPT_PTR(filament_loading_speed_start);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
#include "Slicing.hpp"
|
||||
#include "Tesselate.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "AABBTreeIndirect.hpp"
|
||||
#include "Fill/FillAdaptive.hpp"
|
||||
#include "Format/STL.hpp"
|
||||
|
||||
|
|
@ -590,7 +589,8 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||
|| opt_key == "external_fill_link_max_length"
|
||||
|| opt_key == "fill_angle"
|
||||
|| opt_key == "fill_pattern"
|
||||
|| opt_key == "fill_link_max_length"
|
||||
|| opt_key == "infill_anchor"
|
||||
|| opt_key == "infill_anchor_max"
|
||||
|| opt_key == "top_infill_extrusion_width"
|
||||
|| opt_key == "first_layer_extrusion_width") {
|
||||
steps.emplace_back(posInfill);
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@ bool add_cavity(Contour3D &pad, ExPolygon &top_poly, const PadConfig3D &cfg,
|
|||
|
||||
if (inner_base.empty() || middle_base.empty()) { logerr(); return false; }
|
||||
|
||||
ExPolygons pdiff = diff_ex(top_poly, middle_base.contour);
|
||||
ExPolygons pdiff = diff_ex((Polygons)top_poly, (Polygons)middle_base.contour);
|
||||
|
||||
if (pdiff.size() != 1) { logerr(); return false; }
|
||||
|
||||
|
|
|
|||
|
|
@ -350,6 +350,7 @@ struct SLAPrintStatistics
|
|||
size_t fast_layers_count;
|
||||
double total_cost;
|
||||
double total_weight;
|
||||
std::vector<double> layers_times;
|
||||
|
||||
// Config with the filled in print statistics.
|
||||
DynamicConfig config() const;
|
||||
|
|
@ -366,6 +367,7 @@ struct SLAPrintStatistics
|
|||
fast_layers_count = 0;
|
||||
total_cost = 0.;
|
||||
total_weight = 0.;
|
||||
layers_times.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -671,6 +671,8 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
double models_volume(0.0);
|
||||
|
||||
double estim_time(0.0);
|
||||
std::vector<double> layers_times;
|
||||
layers_times.reserve(printer_input.size());
|
||||
|
||||
size_t slow_layers = 0;
|
||||
size_t fast_layers = 0;
|
||||
|
|
@ -688,7 +690,7 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
|
||||
// write vars
|
||||
&mutex, &models_volume, &supports_volume, &estim_time, &slow_layers,
|
||||
&fast_layers, &fade_layer_time](size_t sliced_layer_cnt)
|
||||
&fast_layers, &fade_layer_time, &layers_times](size_t sliced_layer_cnt)
|
||||
{
|
||||
PrintLayer &layer = m_print->m_printer_input[sliced_layer_cnt];
|
||||
|
||||
|
|
@ -775,20 +777,21 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
else
|
||||
slow_layers++;
|
||||
|
||||
|
||||
// Calculation of the printing time
|
||||
|
||||
|
||||
double layer_times = 0.0;
|
||||
if (sliced_layer_cnt < 3)
|
||||
estim_time += init_exp_time;
|
||||
else if (fade_layer_time > exp_time)
|
||||
{
|
||||
layer_times += init_exp_time;
|
||||
else if (fade_layer_time > exp_time) {
|
||||
fade_layer_time -= delta_fade_time;
|
||||
estim_time += fade_layer_time;
|
||||
layer_times += fade_layer_time;
|
||||
}
|
||||
else
|
||||
estim_time += exp_time;
|
||||
|
||||
estim_time += tilt_time;
|
||||
layer_times += exp_time;
|
||||
layer_times += tilt_time;
|
||||
|
||||
layers_times.push_back(layer_times);
|
||||
estim_time += layer_times;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -804,8 +807,10 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
// A layers count o the highest object
|
||||
if (printer_input.size() == 0)
|
||||
print_statistics.estimated_print_time = std::nan("");
|
||||
else
|
||||
else {
|
||||
print_statistics.estimated_print_time = estim_time;
|
||||
print_statistics.layers_times = layers_times;
|
||||
}
|
||||
|
||||
print_statistics.fast_layers_count = fast_layers;
|
||||
print_statistics.slow_layers_count = slow_layers;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
|
||||
#define COORD(x) (unscale<float>((x))*10)
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool SVG::open(const char* afilename)
|
||||
|
|
@ -33,8 +31,9 @@ bool SVG::open(const char* afilename, const BoundingBox &bbox, const coord_t bbo
|
|||
this->f = boost::nowide::fopen(afilename, "w");
|
||||
if (f == NULL)
|
||||
return false;
|
||||
float w = COORD(bbox.max(0) - bbox.min(0) + 2 * bbox_offset);
|
||||
float h = COORD(bbox.max(1) - bbox.min(1) + 2 * bbox_offset);
|
||||
float w = to_svg_coord(bbox.max(0) - bbox.min(0) + 2 * bbox_offset);
|
||||
float h = to_svg_coord(bbox.max(1) - bbox.min(1) + 2 * bbox_offset);
|
||||
this->height = h;
|
||||
fprintf(this->f,
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
|
||||
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
|
||||
|
|
@ -47,12 +46,11 @@ bool SVG::open(const char* afilename, const BoundingBox &bbox, const coord_t bbo
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Line &line, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const Line &line, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
fprintf(this->f,
|
||||
" <line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke: %s; stroke-width: %f\"",
|
||||
COORD(line.a(0) - origin(0)), COORD(line.a(1) - origin(1)), COORD(line.b(0) - origin(0)), COORD(line.b(1) - origin(1)), stroke.c_str(), (stroke_width == 0) ? 1.f : COORD(stroke_width));
|
||||
to_svg_x(line.a(0) - origin(0)), to_svg_y(line.a(1) - origin(1)), to_svg_x(line.b(0) - origin(0)), to_svg_y(line.b(1) - origin(1)), stroke.c_str(), (stroke_width == 0) ? 1.f : to_svg_coord(stroke_width));
|
||||
if (this->arrows)
|
||||
fprintf(this->f, " marker-end=\"url(#endArrow)\"");
|
||||
fprintf(this->f, "/>\n");
|
||||
|
|
@ -67,34 +65,31 @@ void SVG::draw(const ThickLine &line, const std::string &fill, const std::string
|
|||
coordf_t db = coordf_t(0.5)*line.b_width/len;
|
||||
fprintf(this->f,
|
||||
" <polygon points=\"%f,%f %f,%f %f,%f %f,%f\" style=\"fill:%s; stroke: %s; stroke-width: %f\"/>\n",
|
||||
COORD(line.a(0)-da*perp(0)-origin(0)),
|
||||
COORD(line.a(1)-da*perp(1)-origin(1)),
|
||||
COORD(line.b(0)-db*perp(0)-origin(0)),
|
||||
COORD(line.b(1)-db*perp(1)-origin(1)),
|
||||
COORD(line.b(0)+db*perp(0)-origin(0)),
|
||||
COORD(line.b(1)+db*perp(1)-origin(1)),
|
||||
COORD(line.a(0)+da*perp(0)-origin(0)),
|
||||
COORD(line.a(1)+da*perp(1)-origin(1)),
|
||||
to_svg_x(line.a(0)-da*perp(0)-origin(0)),
|
||||
to_svg_y(line.a(1)-da*perp(1)-origin(1)),
|
||||
to_svg_x(line.b(0)-db*perp(0)-origin(0)),
|
||||
to_svg_y(line.b(1)-db*perp(1)-origin(1)),
|
||||
to_svg_x(line.b(0)+db*perp(0)-origin(0)),
|
||||
to_svg_y(line.b(1)+db*perp(1)-origin(1)),
|
||||
to_svg_x(line.a(0)+da*perp(0)-origin(0)),
|
||||
to_svg_y(line.a(1)+da*perp(1)-origin(1)),
|
||||
fill.c_str(), stroke.c_str(),
|
||||
(stroke_width == 0) ? 1.f : COORD(stroke_width));
|
||||
(stroke_width == 0) ? 1.f : to_svg_coord(stroke_width));
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Lines &lines, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const Lines &lines, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it)
|
||||
this->draw(*it, stroke, stroke_width);
|
||||
for (const Line &l : lines)
|
||||
this->draw(l, stroke, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const IntersectionLines &lines, std::string stroke)
|
||||
void SVG::draw(const IntersectionLines &lines, std::string stroke)
|
||||
{
|
||||
for (IntersectionLines::const_iterator it = lines.begin(); it != lines.end(); ++it)
|
||||
this->draw((Line)*it, stroke);
|
||||
for (const IntersectionLine &il : lines)
|
||||
this->draw((Line)il, stroke);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ExPolygon &expolygon, std::string fill, const float fill_opacity)
|
||||
void SVG::draw(const ExPolygon &expolygon, std::string fill, const float fill_opacity)
|
||||
{
|
||||
this->fill = fill;
|
||||
|
||||
|
|
@ -106,8 +101,7 @@ SVG::draw(const ExPolygon &expolygon, std::string fill, const float fill_opacity
|
|||
this->path(d, true, 0, fill_opacity);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const ExPolygon &expolygon, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const ExPolygon &expolygon, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
{
|
||||
draw_outline(expolygon.contour, stroke_outer, stroke_width);
|
||||
for (Polygons::const_iterator it = expolygon.holes.begin(); it != expolygon.holes.end(); ++ it) {
|
||||
|
|
@ -115,83 +109,71 @@ SVG::draw_outline(const ExPolygon &expolygon, std::string stroke_outer, std::str
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ExPolygons &expolygons, std::string fill, const float fill_opacity)
|
||||
void SVG::draw(const ExPolygons &expolygons, std::string fill, const float fill_opacity)
|
||||
{
|
||||
for (ExPolygons::const_iterator it = expolygons.begin(); it != expolygons.end(); ++it)
|
||||
this->draw(*it, fill, fill_opacity);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const ExPolygons &expolygons, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const ExPolygons &expolygons, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
{
|
||||
for (ExPolygons::const_iterator it = expolygons.begin(); it != expolygons.end(); ++ it)
|
||||
draw_outline(*it, stroke_outer, stroke_holes, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Surface &surface, std::string fill, const float fill_opacity)
|
||||
void SVG::draw(const Surface &surface, std::string fill, const float fill_opacity)
|
||||
{
|
||||
draw(surface.expolygon, fill, fill_opacity);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const Surface &surface, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const Surface &surface, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
{
|
||||
draw_outline(surface.expolygon, stroke_outer, stroke_holes, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Surfaces &surfaces, std::string fill, const float fill_opacity)
|
||||
void SVG::draw(const Surfaces &surfaces, std::string fill, const float fill_opacity)
|
||||
{
|
||||
for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it)
|
||||
this->draw(*it, fill, fill_opacity);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const Surfaces &surfaces, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const Surfaces &surfaces, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
{
|
||||
for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++ it)
|
||||
draw_outline(*it, stroke_outer, stroke_holes, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const SurfacesPtr &surfaces, std::string fill, const float fill_opacity)
|
||||
void SVG::draw(const SurfacesPtr &surfaces, std::string fill, const float fill_opacity)
|
||||
{
|
||||
for (SurfacesPtr::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it)
|
||||
this->draw(*(*it), fill, fill_opacity);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const SurfacesPtr &surfaces, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const SurfacesPtr &surfaces, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
{
|
||||
for (SurfacesPtr::const_iterator it = surfaces.begin(); it != surfaces.end(); ++ it)
|
||||
draw_outline(*(*it), stroke_outer, stroke_holes, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Polygon &polygon, std::string fill)
|
||||
void SVG::draw(const Polygon &polygon, std::string fill)
|
||||
{
|
||||
this->fill = fill;
|
||||
this->path(this->get_path_d(polygon, true), !fill.empty(), 0, 1.f);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Polygons &polygons, std::string fill)
|
||||
void SVG::draw(const Polygons &polygons, std::string fill)
|
||||
{
|
||||
for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it)
|
||||
this->draw(*it, fill);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Polyline &polyline, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const Polyline &polyline, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
this->stroke = stroke;
|
||||
this->path(this->get_path_d(polyline, false), false, stroke_width, 1.f);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Polylines &polylines, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const Polylines &polylines, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
|
||||
this->draw(*it, stroke, stroke_width);
|
||||
|
|
@ -203,73 +185,64 @@ void SVG::draw(const ThickLines &thicklines, const std::string &fill, const std:
|
|||
this->draw(*it, fill, stroke, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ThickPolylines &polylines, const std::string &stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const ThickPolylines &polylines, const std::string &stroke, coordf_t stroke_width)
|
||||
{
|
||||
for (ThickPolylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
|
||||
this->draw((Polyline)*it, stroke, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coordf_t stroke_width)
|
||||
{
|
||||
for (ThickPolylines::const_iterator it = thickpolylines.begin(); it != thickpolylines.end(); ++ it)
|
||||
draw(it->thicklines(), fill, stroke, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Point &point, std::string fill, coord_t iradius)
|
||||
void SVG::draw(const Point &point, std::string fill, coord_t iradius)
|
||||
{
|
||||
float radius = (iradius == 0) ? 3.f : COORD(iradius);
|
||||
float radius = (iradius == 0) ? 3.f : to_svg_coord(iradius);
|
||||
std::ostringstream svg;
|
||||
svg << " <circle cx=\"" << COORD(point(0) - origin(0)) << "\" cy=\"" << COORD(point(1) - origin(1))
|
||||
svg << " <circle cx=\"" << to_svg_x(point(0) - origin(0)) << "\" cy=\"" << to_svg_y(point(1) - origin(1))
|
||||
<< "\" r=\"" << radius << "\" "
|
||||
<< "style=\"stroke: none; fill: " << fill << "\" />";
|
||||
|
||||
fprintf(this->f, "%s\n", svg.str().c_str());
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Points &points, std::string fill, coord_t radius)
|
||||
void SVG::draw(const Points &points, std::string fill, coord_t radius)
|
||||
{
|
||||
for (Points::const_iterator it = points.begin(); it != points.end(); ++it)
|
||||
this->draw(*it, fill, radius);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ClipperLib::Path &polygon, double scale, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const ClipperLib::Path &polygon, double scale, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
this->stroke = stroke;
|
||||
this->path(this->get_path_d(polygon, scale, true), false, stroke_width, 1.f);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ClipperLib::Paths &polygons, double scale, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const ClipperLib::Paths &polygons, double scale, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
for (ClipperLib::Paths::const_iterator it = polygons.begin(); it != polygons.end(); ++ it)
|
||||
draw(*it, scale, stroke, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const Polygon &polygon, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const Polygon &polygon, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
this->stroke = stroke;
|
||||
this->path(this->get_path_d(polygon, true), false, stroke_width, 1.f);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const Polygons &polygons, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const Polygons &polygons, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++ it)
|
||||
draw_outline(*it, stroke, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::path(const std::string &d, bool fill, coordf_t stroke_width, const float fill_opacity)
|
||||
void SVG::path(const std::string &d, bool fill, coordf_t stroke_width, const float fill_opacity)
|
||||
{
|
||||
float lineWidth = 0.f;
|
||||
if (! fill)
|
||||
lineWidth = (stroke_width == 0) ? 2.f : COORD(stroke_width);
|
||||
lineWidth = (stroke_width == 0) ? 2.f : to_svg_coord(stroke_width);
|
||||
|
||||
fprintf(
|
||||
this->f,
|
||||
|
|
@ -283,27 +256,25 @@ SVG::path(const std::string &d, bool fill, coordf_t stroke_width, const float fi
|
|||
);
|
||||
}
|
||||
|
||||
std::string
|
||||
SVG::get_path_d(const MultiPoint &mp, bool closed) const
|
||||
std::string SVG::get_path_d(const MultiPoint &mp, bool closed) const
|
||||
{
|
||||
std::ostringstream d;
|
||||
d << "M ";
|
||||
for (Points::const_iterator p = mp.points.begin(); p != mp.points.end(); ++p) {
|
||||
d << COORD((*p)(0) - origin(0)) << " ";
|
||||
d << COORD((*p)(1) - origin(1)) << " ";
|
||||
d << to_svg_x((*p)(0) - origin(0)) << " ";
|
||||
d << to_svg_y((*p)(1) - origin(1)) << " ";
|
||||
}
|
||||
if (closed) d << "z";
|
||||
return d.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
SVG::get_path_d(const ClipperLib::Path &path, double scale, bool closed) const
|
||||
std::string SVG::get_path_d(const ClipperLib::Path &path, double scale, bool closed) const
|
||||
{
|
||||
std::ostringstream d;
|
||||
d << "M ";
|
||||
for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) {
|
||||
d << COORD(scale * p->X - origin(0)) << " ";
|
||||
d << COORD(scale * p->Y - origin(1)) << " ";
|
||||
d << to_svg_x(scale * p->X - origin(0)) << " ";
|
||||
d << to_svg_y(scale * p->Y - origin(1)) << " ";
|
||||
}
|
||||
if (closed) d << "z";
|
||||
return d.str();
|
||||
|
|
@ -313,8 +284,8 @@ void SVG::draw_text(const Point &pt, const char *text, const char *color)
|
|||
{
|
||||
fprintf(this->f,
|
||||
"<text x=\"%f\" y=\"%f\" font-family=\"sans-serif\" font-size=\"20px\" fill=\"%s\">%s</text>",
|
||||
COORD(pt(0)-origin(0)),
|
||||
COORD(pt(1)-origin(1)),
|
||||
to_svg_x(pt(0)-origin(0)),
|
||||
to_svg_y(pt(1)-origin(1)),
|
||||
color, text);
|
||||
}
|
||||
|
||||
|
|
@ -322,18 +293,17 @@ void SVG::draw_legend(const Point &pt, const char *text, const char *color)
|
|||
{
|
||||
fprintf(this->f,
|
||||
"<circle cx=\"%f\" cy=\"%f\" r=\"10\" fill=\"%s\"/>",
|
||||
COORD(pt(0)-origin(0)),
|
||||
COORD(pt(1)-origin(1)),
|
||||
to_svg_x(pt(0)-origin(0)),
|
||||
to_svg_y(pt(1)-origin(1)),
|
||||
color);
|
||||
fprintf(this->f,
|
||||
"<text x=\"%f\" y=\"%f\" font-family=\"sans-serif\" font-size=\"10px\" fill=\"%s\">%s</text>",
|
||||
COORD(pt(0)-origin(0)) + 20.f,
|
||||
COORD(pt(1)-origin(1)),
|
||||
to_svg_x(pt(0)-origin(0)) + 20.f,
|
||||
to_svg_y(pt(1)-origin(1)),
|
||||
"black", text);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::Close()
|
||||
void SVG::Close()
|
||||
{
|
||||
fprintf(this->f, "</svg>\n");
|
||||
fclose(this->f);
|
||||
|
|
|
|||
|
|
@ -16,27 +16,28 @@ public:
|
|||
bool arrows;
|
||||
std::string fill, stroke;
|
||||
Point origin;
|
||||
bool flipY;
|
||||
float height;
|
||||
bool flipY;
|
||||
|
||||
SVG(const char* afilename) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(afilename), flipY(false)
|
||||
{ open(filename); }
|
||||
SVG(const char* afilename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool aflipY = false) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(afilename), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(aflipY)
|
||||
{ open(filename, bbox, bbox_offset, aflipY); }
|
||||
SVG(const char* afilename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = true) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(afilename), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(flipY)
|
||||
{ open(filename, bbox, bbox_offset, flipY); }
|
||||
SVG(const std::string &filename) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(filename), flipY(false)
|
||||
{ open(filename); }
|
||||
SVG(const std::string &filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool aflipY = false) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(filename), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(aflipY)
|
||||
{ open(filename, bbox, bbox_offset, aflipY); }
|
||||
SVG(const std::string &filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = true) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(filename), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(flipY)
|
||||
{ open(filename, bbox, bbox_offset, flipY); }
|
||||
~SVG() { if (f != NULL) Close(); }
|
||||
|
||||
bool open(const char* filename);
|
||||
bool open(const char* filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = false);
|
||||
bool open(const char* filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = true);
|
||||
bool open(const std::string &filename)
|
||||
{ return open(filename.c_str()); }
|
||||
bool open(const std::string &filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = false)
|
||||
bool open(const std::string &filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = true)
|
||||
{ return open(filename.c_str(), bbox, bbox_offset, flipY); }
|
||||
|
||||
void draw(const Line &line, std::string stroke = "black", coordf_t stroke_width = 0);
|
||||
|
|
@ -127,6 +128,11 @@ public:
|
|||
};
|
||||
|
||||
static void export_expolygons(const char *path, const std::vector<std::pair<Slic3r::ExPolygons, ExPolygonAttributes>> &expolygons_with_attributes);
|
||||
|
||||
private:
|
||||
static float to_svg_coord(float x) throw() { return unscale<float>(x) * 10.f; }
|
||||
static float to_svg_x(float x) throw() { return to_svg_coord(x); }
|
||||
float to_svg_y(float x) const throw() { return flipY ? this->height - to_svg_coord(x) : to_svg_coord(x); }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2324,7 +2324,6 @@ static inline void fill_expolygons_generate_paths(
|
|||
{
|
||||
FillParams fill_params;
|
||||
fill_params.density = density;
|
||||
fill_params.complete = true;
|
||||
fill_params.dont_adjust = true;
|
||||
for (const ExPolygon &expoly : expolygons) {
|
||||
Surface surface(stInternal, expoly);
|
||||
|
|
@ -2351,7 +2350,6 @@ static inline void fill_expolygons_generate_paths(
|
|||
{
|
||||
FillParams fill_params;
|
||||
fill_params.density = density;
|
||||
fill_params.complete = true;
|
||||
fill_params.dont_adjust = true;
|
||||
for (ExPolygon &expoly : expolygons) {
|
||||
Surface surface(stInternal, std::move(expoly));
|
||||
|
|
@ -2515,7 +2513,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
|
|||
Polygon &contour = (i_contour == 0) ? it_contact_expoly->contour : it_contact_expoly->holes[i_contour - 1];
|
||||
const Point *seg_current_pt = nullptr;
|
||||
coordf_t seg_current_t = 0.;
|
||||
if (! intersection_pl(contour.split_at_first_point(), overhang_with_margin).empty()) {
|
||||
if (! intersection_pl((Polylines)contour.split_at_first_point(), overhang_with_margin).empty()) {
|
||||
// The contour is below the overhang at least to some extent.
|
||||
//FIXME ideally one would place the circles below the overhang only.
|
||||
// Walk around the contour and place circles so their centers are not closer than circle_distance from each other.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#ifndef _prusaslicer_technologies_h_
|
||||
#define _prusaslicer_technologies_h_
|
||||
|
||||
//============
|
||||
//=============
|
||||
// debug techs
|
||||
//============
|
||||
//=============
|
||||
|
||||
// Shows camera target in the 3D scene
|
||||
#define ENABLE_SHOW_CAMERA_TARGET 0
|
||||
|
|
@ -23,20 +23,24 @@
|
|||
#define DISABLE_INSTANCES_SYNCH 0
|
||||
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
||||
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING 0
|
||||
// Enable G-Code viewer statistics imgui dialog
|
||||
#define ENABLE_GCODE_VIEWER_STATISTICS 0
|
||||
// Enable G-Code viewer comparison between toolpaths height and width detected from gcode and calculated at gcode generation
|
||||
#define ENABLE_GCODE_VIEWER_DATA_CHECKING 0
|
||||
|
||||
|
||||
//================
|
||||
//=================
|
||||
// 2.2.0.rc1 techs
|
||||
//================
|
||||
//=================
|
||||
#define ENABLE_2_2_0_RC1 1
|
||||
|
||||
// Enable hack to remove crash when closing on OSX 10.9.5
|
||||
#define ENABLE_HACK_CLOSING_ON_OSX_10_9_5 (1 && ENABLE_2_2_0_RC1)
|
||||
|
||||
|
||||
//===================
|
||||
//====================
|
||||
// 2.3.0.alpha1 techs
|
||||
//===================
|
||||
//====================
|
||||
#define ENABLE_2_3_0_ALPHA1 1
|
||||
|
||||
// Enable rendering of objects using environment map
|
||||
|
|
@ -51,26 +55,32 @@
|
|||
// Enable built-in DPI changed event handler of wxWidgets 3.1.3
|
||||
#define ENABLE_WX_3_1_3_DPI_CHANGED_EVENT (1 && ENABLE_2_3_0_ALPHA1)
|
||||
|
||||
// Enable G-Code viewer
|
||||
#define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1)
|
||||
#define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER)
|
||||
#define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER)
|
||||
|
||||
|
||||
//===================
|
||||
//====================
|
||||
// 2.3.0.alpha3 techs
|
||||
//===================
|
||||
//====================
|
||||
#define ENABLE_2_3_0_ALPHA3 1
|
||||
|
||||
#define ENABLE_CTRL_M_ON_WINDOWS (0 && ENABLE_2_3_0_ALPHA3)
|
||||
#define ENABLE_CTRL_M_ON_WINDOWS (1 && ENABLE_2_3_0_ALPHA3)
|
||||
|
||||
|
||||
//===================
|
||||
//====================
|
||||
// 2.3.0.alpha4 techs
|
||||
//===================
|
||||
//====================
|
||||
#define ENABLE_2_3_0_ALPHA4 1
|
||||
|
||||
#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS (1 && ENABLE_GCODE_VIEWER && ENABLE_2_3_0_ALPHA4)
|
||||
#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS (1 && ENABLE_2_3_0_ALPHA4)
|
||||
#define ENABLE_SHOW_OPTION_POINT_LAYERS (1 && ENABLE_2_3_0_ALPHA4)
|
||||
|
||||
|
||||
//===================
|
||||
// 2.3.0.beta1 techs
|
||||
//===================
|
||||
#define ENABLE_2_3_0_BETA1 1
|
||||
|
||||
#define ENABLE_SHOW_WIPE_MOVES (1 && ENABLE_2_3_0_BETA1)
|
||||
#define ENABLE_DRAG_AND_DROP_FIX (1 && ENABLE_2_3_0_BETA1)
|
||||
#define ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN (1 && ENABLE_2_3_0_BETA1)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
|
|||
|
|
@ -103,12 +103,6 @@ enum Axis {
|
|||
NUM_AXES_WITH_UNKNOWN,
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline void append_to(std::vector<T> &dst, const std::vector<T> &src)
|
||||
{
|
||||
dst.insert(dst.end(), src.begin(), src.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void append(std::vector<T>& dest, const std::vector<T>& src)
|
||||
{
|
||||
|
|
@ -131,6 +125,30 @@ inline void append(std::vector<T>& dest, std::vector<T>&& src)
|
|||
src.shrink_to_fit();
|
||||
}
|
||||
|
||||
// Append the source in reverse.
|
||||
template <typename T>
|
||||
inline void append_reversed(std::vector<T>& dest, const std::vector<T>& src)
|
||||
{
|
||||
if (dest.empty())
|
||||
dest = src;
|
||||
else
|
||||
dest.insert(dest.end(), src.rbegin(), src.rend());
|
||||
}
|
||||
|
||||
// Append the source in reverse.
|
||||
template <typename T>
|
||||
inline void append_reversed(std::vector<T>& dest, std::vector<T>&& src)
|
||||
{
|
||||
if (dest.empty())
|
||||
dest = std::move(src);
|
||||
else {
|
||||
dest.reserve(dest.size() + src.size());
|
||||
std::move(std::rbegin(src), std::rend(src), std::back_inserter(dest));
|
||||
}
|
||||
src.clear();
|
||||
src.shrink_to_fit();
|
||||
}
|
||||
|
||||
// Casting an std::vector<> from one type to another type without warnings about a loss of accuracy.
|
||||
template<typename T_TO, typename T_FROM>
|
||||
std::vector<T_TO> cast(const std::vector<T_FROM> &src)
|
||||
|
|
|
|||
|
|
@ -432,7 +432,7 @@ CopyFileResult copy_file_inner(const std::string& from, const std::string& to, s
|
|||
boost::system::error_code ec;
|
||||
boost::filesystem::permissions(target, perms, ec);
|
||||
if (ec)
|
||||
BOOST_LOG_TRIVIAL(error) << "boost::filesystem::permisions before copy error message (this could be irrelevant message based on file system): " << ec.message();
|
||||
BOOST_LOG_TRIVIAL(debug) << "boost::filesystem::permisions before copy error message (this could be irrelevant message based on file system): " << ec.message();
|
||||
ec.clear();
|
||||
boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec);
|
||||
if (ec) {
|
||||
|
|
@ -442,7 +442,7 @@ CopyFileResult copy_file_inner(const std::string& from, const std::string& to, s
|
|||
ec.clear();
|
||||
boost::filesystem::permissions(target, perms, ec);
|
||||
if (ec)
|
||||
BOOST_LOG_TRIVIAL(error) << "boost::filesystem::permisions after copy error message (this could be irrelevant message based on file system): " << ec.message();
|
||||
BOOST_LOG_TRIVIAL(debug) << "boost::filesystem::permisions after copy error message (this could be irrelevant message based on file system): " << ec.message();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue