WIP Lightning Infill, roughly integrated, untested, disabled with

HAS_LIGHTNING_INFILL
This commit is contained in:
Vojtech Bubnik 2021-12-01 18:11:41 +01:00
parent e8697d2fc2
commit 481def3205
14 changed files with 221 additions and 126 deletions

View File

@ -67,8 +67,8 @@ add_library(libslic3r STATIC
Fill/FillPlanePath.hpp
Fill/FillLine.cpp
Fill/FillLine.hpp
Fill/FillRectilinear.cpp
Fill/FillRectilinear.hpp
Fill/FillLightning.cpp
Fill/FillLightning.hpp
Fill/Lightning/DistanceField.cpp
Fill/Lightning/DistanceField.hpp
Fill/Lightning/Generator.cpp
@ -77,6 +77,8 @@ add_library(libslic3r STATIC
Fill/Lightning/Layer.hpp
Fill/Lightning/TreeNode.cpp
Fill/Lightning/TreeNode.hpp
Fill/FillRectilinear.cpp
Fill/FillRectilinear.hpp
Flow.cpp
Flow.hpp
format.hpp

View File

@ -19,6 +19,7 @@
#include "FillLine.hpp"
#include "FillRectilinear.hpp"
#include "FillAdaptive.hpp"
#include "FillLightning.hpp"
// #define INFILL_DEBUG_OUTPUT
@ -45,6 +46,9 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipAdaptiveCubic: return new FillAdaptive::Filler();
case ipSupportCubic: return new FillAdaptive::Filler();
case ipSupportBase: return new FillSupportBase();
#if HAS_LIGHTNING_INFILL
case ipLightning: return new FillLightning::Filler();
#endif // HAS_LIGHTNING_INFILL
default: throw Slic3r::InvalidArgument("unknown type");
}
}

View File

@ -0,0 +1,28 @@
#include "../Print.hpp"
#include "FillLightning.hpp"
#include "Lightning/Generator.hpp"
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <numeric>
namespace Slic3r::FillLightning {
Polylines Filler::fill_surface(const Surface *surface, const FillParams &params)
{
const Layer &layer = generator->getTreesForLayer(this->layer_id);
return layer.convertToLines(to_polygons(surface->expolygon), generator->infilll_extrusion_width());
}
void GeneratorDeleter::operator()(Generator *p) {
delete p;
}
GeneratorPtr build_generator(const PrintObject &print_object)
{
return GeneratorPtr(new Generator(print_object));
}
} // namespace Slic3r::FillAdaptive

View File

@ -0,0 +1,36 @@
#ifndef slic3r_FillLightning_hpp_
#define slic3r_FillLightning_hpp_
#include "FillBase.hpp"
namespace Slic3r {
class PrintObject;
namespace FillLightning {
class Generator;
// To keep the definition of Octree opaque, we have to define a custom deleter.
struct GeneratorDeleter { void operator()(Generator *p); };
using GeneratorPtr = std::unique_ptr<Generator, GeneratorDeleter>;
GeneratorPtr build_generator(const PrintObject &print_object);
class Filler : public Slic3r::Fill
{
public:
~Filler() override = default;
Generator *generator { nullptr };
protected:
Fill* clone() const override { return new Filler(*this); }
// Perform the fill.
Polylines fill_surface(const Surface *surface, const FillParams &params) override;
// Let the G-code export reoder the infill lines.
bool no_sort() const override { return false; }
};
} // namespace FillAdaptive
} // namespace Slic3r
#endif // slic3r_FillLightning_hpp_

View File

@ -4,12 +4,12 @@
#include "DistanceField.hpp" //Class we're implementing.
#include "../FillRectilinear.hpp"
namespace Slic3r
namespace Slic3r::FillLightning
{
constexpr coord_t radius_per_cell_size = 6; // The cell-size should be small compared to the radius, but not so small as to be inefficient.
LightningDistanceField::LightningDistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang) :
DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang) :
m_cell_size(radius / radius_per_cell_size),
m_supporting_radius(radius)
{
@ -44,7 +44,7 @@ LightningDistanceField::LightningDistanceField(const coord_t& radius, const Poly
}
}
void LightningDistanceField::update(const Point& to_node, const Point& added_leaf)
void DistanceField::update(const Point& to_node, const Point& added_leaf)
{
Vec2d v = (added_leaf - to_node).cast<double>();
auto l2 = v.squaredNorm();
@ -95,4 +95,4 @@ void LightningDistanceField::update(const Point& to_node, const Point& added_lea
}
}
} // namespace Slic3r
} // namespace Slic3r::FillLightning

View File

@ -4,7 +4,7 @@
#ifndef LIGHTNING_DISTANCE_FIELD_H
#define LIGHTNING_DISTANCE_FIELD_H
namespace Slic3r
namespace Slic3r::FillLightning
{
/*!
@ -15,7 +15,7 @@ namespace Slic3r
* maintains how far it is removed from the edge, which is used to determine
* how it gets supported by Lightning Infill.
*/
class LightningDistanceField
class DistanceField
{
public:
/*!
@ -26,7 +26,7 @@ public:
* \param current_overhang The overhang that needs to be supported on this
* layer.
*/
LightningDistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang);
DistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang);
/*!
* Gets the next unsupported location to be supported by a new branch.
@ -92,6 +92,6 @@ protected:
std::unordered_map<Point, std::list<UnsupportedCell>::iterator, PointHash> m_unsupported_points_grid;
};
} // namespace Slic3r
} // namespace Slic3r::FillLightning
#endif //LIGHTNING_DISTANCE_FIELD_H

View File

@ -20,12 +20,12 @@
* - Make a pass with Arachne over the output. Somehow.
* - Generate all to-be-supported points at once instead of sequentially: See branch interlocking_gen PolygonUtils::spreadDots (Or work with sparse grids.)
* - Lots of magic values ... to many to parameterize. But are they the best?
* - Move more complex computations from LightningGenerator constructor to elsewhere.
* - Move more complex computations from Generator constructor to elsewhere.
*/
using namespace Slic3r;
namespace Slic3r::FillLightning {
LightningGenerator::LightningGenerator(const PrintObject &print_object)
Generator::Generator(const PrintObject &print_object)
{
const PrintConfig &print_config = print_object.print()->config();
const PrintObjectConfig &object_config = print_object.config();
@ -51,7 +51,7 @@ LightningGenerator::LightningGenerator(const PrintObject &print_object)
generateTrees(print_object);
}
void LightningGenerator::generateInitialInternalOverhangs(const PrintObject &print_object)
void Generator::generateInitialInternalOverhangs(const PrintObject &print_object)
{
m_overhang_per_layer.resize(print_object.layers().size());
const float infill_wall_offset = - m_infill_extrusion_width;
@ -73,13 +73,13 @@ void LightningGenerator::generateInitialInternalOverhangs(const PrintObject &pri
}
}
const LightningLayer& LightningGenerator::getTreesForLayer(const size_t& layer_id) const
const Layer& Generator::getTreesForLayer(const size_t& layer_id) const
{
assert(layer_id < m_lightning_layers.size());
return m_lightning_layers[layer_id];
}
void LightningGenerator::generateTrees(const PrintObject &print_object)
void Generator::generateTrees(const PrintObject &print_object)
{
m_lightning_layers.resize(print_object.layers().size());
const coord_t infill_wall_offset = - m_infill_extrusion_width;
@ -101,11 +101,11 @@ void LightningGenerator::generateTrees(const PrintObject &print_object)
// For-each layer from top to bottom:
for (int layer_id = top_layer_id; layer_id >= 0; layer_id--)
{
LightningLayer& current_lightning_layer = m_lightning_layers[layer_id];
Layer& current_lightning_layer = m_lightning_layers[layer_id];
Polygons& current_outlines = infill_outlines[layer_id];
// register all trees propagated from the previous layer as to-be-reconnected
std::vector<LightningTreeNodeSPtr> to_be_reconnected_tree_roots = current_lightning_layer.tree_roots;
std::vector<NodeSPtr> to_be_reconnected_tree_roots = current_lightning_layer.tree_roots;
current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, outlines_locator, m_supporting_radius, m_wall_supporting_radius);
current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, outlines_locator, m_supporting_radius, m_wall_supporting_radius);
@ -118,8 +118,10 @@ void LightningGenerator::generateTrees(const PrintObject &print_object)
outlines_locator.set_bbox(get_extents(below_outlines).inflated(SCALED_EPSILON));
outlines_locator.create(below_outlines, locator_cell_size);
std::vector<LightningTreeNodeSPtr>& lower_trees = m_lightning_layers[layer_id - 1].tree_roots;
std::vector<NodeSPtr>& lower_trees = m_lightning_layers[layer_id - 1].tree_roots;
for (auto& tree : current_lightning_layer.tree_roots)
tree->propagateToNextLayer(lower_trees, below_outlines, outlines_locator, m_prune_length, m_straightening_max_distance, locator_cell_size / 2);
}
}
} // namespace Slic3r::FillLightning

View File

@ -14,6 +14,9 @@ namespace Slic3r
{
class PrintObject;
namespace FillLightning
{
/*!
* Generates the Lightning Infill pattern.
*
@ -31,7 +34,7 @@ class PrintObject;
* Printing of Hollowed Objects" by Tricard, Claux and Lefebvre:
* https://www.researchgate.net/publication/333808588_Ribbed_Support_Vaults_for_3D_Printing_of_Hollowed_Objects
*/
class LightningGenerator // "Just like Nicola used to make!"
class Generator // "Just like Nicola used to make!"
{
public:
/*!
@ -42,7 +45,7 @@ public:
* already be calculated at this point.
* \param mesh The mesh to generate infill for.
*/
LightningGenerator(const PrintObject &print_object);
Generator(const PrintObject &print_object);
/*!
* Get a tree of paths generated for a certain layer of the mesh.
@ -53,7 +56,9 @@ public:
* \return A tree structure representing paths to print to create the
* Lightning Infill pattern.
*/
const LightningLayer& getTreesForLayer(const size_t& layer_id) const;
const Layer& getTreesForLayer(const size_t& layer_id) const;
float infilll_extrusion_width() const { return m_infill_extrusion_width; }
protected:
/*!
@ -118,9 +123,10 @@ protected:
*
* This is generated by \ref generateTrees.
*/
std::vector<LightningLayer> m_lightning_layers;
std::vector<Layer> m_lightning_layers;
};
} // namespace FillLightning
} // namespace Slic3r
#endif // LIGHTNING_GENERATOR_H

View File

@ -10,9 +10,9 @@
#include "../../Geometry.hpp"
using namespace Slic3r;
namespace Slic3r::FillLightning {
coord_t LightningLayer::getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location)
coord_t Layer::getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location)
{
return coord_t((boundary_loc - unsupported_location).cast<double>().norm());
}
@ -23,16 +23,16 @@ Point GroundingLocation::p() const
return tree_node ? tree_node->getLocation() : *boundary_location;
}
void LightningLayer::fillLocator(SparseLightningTreeNodeGrid &tree_node_locator)
void Layer::fillLocator(SparseNodeGrid &tree_node_locator)
{
std::function<void(LightningTreeNodeSPtr)> add_node_to_locator_func = [&tree_node_locator](LightningTreeNodeSPtr node) {
std::function<void(NodeSPtr)> add_node_to_locator_func = [&tree_node_locator](NodeSPtr node) {
tree_node_locator.insert(std::make_pair(Point(node->getLocation().x() / locator_cell_size, node->getLocation().y() / locator_cell_size), node));
};
for (auto& tree : tree_roots)
tree->visitNodes(add_node_to_locator_func);
}
void LightningLayer::generateNewTrees
void Layer::generateNewTrees
(
const Polygons& current_overhang,
const Polygons& current_outlines,
@ -41,9 +41,9 @@ void LightningLayer::generateNewTrees
const coord_t wall_supporting_radius
)
{
LightningDistanceField distance_field(supporting_radius, current_outlines, current_overhang);
DistanceField distance_field(supporting_radius, current_outlines, current_overhang);
SparseLightningTreeNodeGrid tree_node_locator;
SparseNodeGrid tree_node_locator;
fillLocator(tree_node_locator);
// Until no more points need to be added to support all:
@ -53,8 +53,8 @@ void LightningLayer::generateNewTrees
GroundingLocation grounding_loc = getBestGroundingLocation(
unsupported_location, current_outlines, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator);
LightningTreeNodeSPtr new_parent;
LightningTreeNodeSPtr new_child;
NodeSPtr new_parent;
NodeSPtr new_child;
this->attach(unsupported_location, grounding_loc, new_child, new_parent);
tree_node_locator.insert(std::make_pair(Point(new_child->getLocation().x() / locator_cell_size, new_child->getLocation().y() / locator_cell_size), new_child));
if (new_parent)
@ -93,15 +93,15 @@ static bool polygonCollidesWithLineSegment(const Point from, const Point to, con
return visitor.intersect;
}
GroundingLocation LightningLayer::getBestGroundingLocation
GroundingLocation Layer::getBestGroundingLocation
(
const Point& unsupported_location,
const Polygons& current_outlines,
const EdgeGrid::Grid& outline_locator,
const coord_t supporting_radius,
const coord_t wall_supporting_radius,
const SparseLightningTreeNodeGrid& tree_node_locator,
const LightningTreeNodeSPtr& exclude_tree
const SparseNodeGrid& tree_node_locator,
const NodeSPtr& exclude_tree
)
{
// Closest point on current_outlines to unsupported_location:
@ -123,7 +123,7 @@ GroundingLocation LightningLayer::getBestGroundingLocation
const auto within_dist = coord_t((node_location - unsupported_location).cast<double>().norm());
LightningTreeNodeSPtr sub_tree{ nullptr };
NodeSPtr sub_tree{ nullptr };
coord_t current_dist = getWeightedDistance(node_location, unsupported_location);
if (current_dist >= wall_supporting_radius) { // Only reconnect tree roots to other trees if they are not already close to the outlines.
const coord_t search_radius = std::min(current_dist, within_dist);
@ -154,15 +154,15 @@ GroundingLocation LightningLayer::getBestGroundingLocation
GroundingLocation{ sub_tree, std::optional<Point>() };
}
bool LightningLayer::attach(
bool Layer::attach(
const Point& unsupported_location,
const GroundingLocation& grounding_loc,
LightningTreeNodeSPtr& new_child,
LightningTreeNodeSPtr& new_root)
NodeSPtr& new_child,
NodeSPtr& new_root)
{
// Update trees & distance fields.
if (grounding_loc.boundary_location) {
new_root = LightningTreeNode::create(grounding_loc.p(), std::make_optional(grounding_loc.p()));
new_root = Node::create(grounding_loc.p(), std::make_optional(grounding_loc.p()));
new_child = new_root->addChild(unsupported_location);
tree_roots.push_back(new_root);
return true;
@ -172,9 +172,9 @@ bool LightningLayer::attach(
}
}
void LightningLayer::reconnectRoots
void Layer::reconnectRoots
(
std::vector<LightningTreeNodeSPtr>& to_be_reconnected_tree_roots,
std::vector<NodeSPtr>& to_be_reconnected_tree_roots,
const Polygons& current_outlines,
const EdgeGrid::Grid& outline_locator,
const coord_t supporting_radius,
@ -183,7 +183,7 @@ void LightningLayer::reconnectRoots
{
constexpr coord_t tree_connecting_ignore_offset = 100;
SparseLightningTreeNodeGrid tree_node_locator;
SparseNodeGrid tree_node_locator;
fillLocator(tree_node_locator);
const coord_t within_max_dist = outline_locator.resolution() * 2;
@ -198,8 +198,8 @@ void LightningLayer::reconnectRoots
{
Point new_root_pt;
// Find an intersection of the line segment from root_ptr->getLocation() to ground_loc, at within_max_dist from ground_loc.
if (lineSegmentPolygonsIntersection(root_ptr->getLocation(), ground_loc, current_outlines, outline_locator, new_root_pt, within_max_dist)) {
auto new_root = LightningTreeNode::create(new_root_pt, new_root_pt);
if (lineSegmentPolygonsIntersection(root_ptr->getLocation(), ground_loc, outline_locator, new_root_pt, within_max_dist)) {
auto new_root = Node::create(new_root_pt, new_root_pt);
root_ptr->addChild(new_root);
new_root->reroot();
@ -228,7 +228,7 @@ void LightningLayer::reconnectRoots
if (ground.boundary_location.value() == root_ptr->getLocation())
continue; // Already on the boundary.
auto new_root = LightningTreeNode::create(ground.p(), ground.p());
auto new_root = Node::create(ground.p(), ground.p());
auto attach_ptr = root_ptr->closestNode(new_root->getLocation());
attach_ptr->reroot();
@ -375,14 +375,13 @@ static unsigned int moveInside(const Polygons& polygons, Point& from, int distan
}
// Returns 'added someting'.
Polygons LightningLayer::convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const
Polylines Layer::convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const
{
Polygons result_lines;
if (tree_roots.empty())
return result_lines;
return {};
for (const auto& tree : tree_roots)
{
Polygons result_lines;
for (const auto& tree : tree_roots) {
// If even the furthest location in the tree is inside the polygon, the entire tree must be inside of the polygon.
// (Don't take the root as that may be on the edge and cause rounding errors to register as 'outside'.)
constexpr coord_t epsilon = 5;
@ -393,13 +392,12 @@ Polygons LightningLayer::convertToLines(const Polygons& limit_to_outline, const
}
// TODO: allow for polylines!
Polygons split_lines;
for (Polygon &line : result_lines)
{
if (line.size() <= 1) continue;
Polylines split_lines;
for (Polygon &line : result_lines) {
if (line.size() <= 1)
continue;
Point last = line[0];
for (size_t point_idx = 1; point_idx < line.size(); point_idx++)
{
for (size_t point_idx = 1; point_idx < line.size(); point_idx++) {
Point here = line[point_idx];
split_lines.push_back({ last, here });
last = here;
@ -408,3 +406,5 @@ Polygons LightningLayer::convertToLines(const Polygons& limit_to_outline, const
return split_lines;
}
} // namespace Slic3r::Lightning

View File

@ -12,16 +12,16 @@
#include <list>
#include <unordered_map>
namespace Slic3r
namespace Slic3r::FillLightning
{
class LightningTreeNode;
using LightningTreeNodeSPtr = std::shared_ptr<LightningTreeNode>;
using SparseLightningTreeNodeGrid = std::unordered_multimap<Point, std::weak_ptr<LightningTreeNode>, PointHash>;
class Node;
using NodeSPtr = std::shared_ptr<Node>;
using SparseNodeGrid = std::unordered_multimap<Point, std::weak_ptr<Node>, PointHash>;
struct GroundingLocation
{
LightningTreeNodeSPtr tree_node; //!< not null if the gounding location is on a tree
NodeSPtr tree_node; //!< not null if the gounding location is on a tree
std::optional<Point> boundary_location; //!< in case the gounding location is on the boundary
Point p() const;
};
@ -31,10 +31,10 @@ struct GroundingLocation
*
* Contains the trees to be printed and propagated to the next layer below.
*/
class LightningLayer
class Layer
{
public:
std::vector<LightningTreeNodeSPtr> tree_roots;
std::vector<NodeSPtr> tree_roots;
void generateNewTrees
(
@ -55,8 +55,8 @@ public:
const EdgeGrid::Grid& outline_locator,
const coord_t supporting_radius,
const coord_t wall_supporting_radius,
const SparseLightningTreeNodeGrid& tree_node_locator,
const LightningTreeNodeSPtr& exclude_tree = nullptr
const SparseNodeGrid& tree_node_locator,
const NodeSPtr& exclude_tree = nullptr
);
/*!
@ -64,24 +64,24 @@ public:
* \param[out] new_root The new root node if one had been made
* \return Whether a new root was added
*/
bool attach(const Point& unsupported_location, const GroundingLocation& ground, LightningTreeNodeSPtr& new_child, LightningTreeNodeSPtr& new_root);
bool attach(const Point& unsupported_location, const GroundingLocation& ground, NodeSPtr& new_child, NodeSPtr& new_root);
void reconnectRoots
(
std::vector<LightningTreeNodeSPtr>& to_be_reconnected_tree_roots,
std::vector<NodeSPtr>& to_be_reconnected_tree_roots,
const Polygons& current_outlines,
const EdgeGrid::Grid& outline_locator,
const coord_t supporting_radius,
const coord_t wall_supporting_radius
);
Polygons convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const;
Polylines convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const;
coord_t getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location);
void fillLocator(SparseLightningTreeNodeGrid& tree_node_locator);
void fillLocator(SparseNodeGrid& tree_node_locator);
};
} // namespace Slic3r
} // namespace Slic3r::FillLightning
#endif // LIGHTNING_LAYER_H

View File

@ -5,9 +5,9 @@
#include "../../Geometry.hpp"
using namespace Slic3r;
namespace Slic3r::FillLightning {
coord_t LightningTreeNode::getWeightedDistance(const Point& unsupported_location, const coord_t& supporting_radius) const
coord_t Node::getWeightedDistance(const Point& unsupported_location, const coord_t& supporting_radius) const
{
constexpr coord_t min_valence_for_boost = 0;
constexpr coord_t max_valence_for_boost = 4;
@ -19,7 +19,7 @@ coord_t LightningTreeNode::getWeightedDistance(const Point& unsupported_location
return dist_here - valence_boost;
}
bool LightningTreeNode::hasOffspring(const LightningTreeNodeSPtr& to_be_checked) const
bool Node::hasOffspring(const NodeSPtr& to_be_checked) const
{
if (to_be_checked == shared_from_this())
return true;
@ -31,14 +31,14 @@ bool LightningTreeNode::hasOffspring(const LightningTreeNodeSPtr& to_be_checked)
return false;
}
LightningTreeNodeSPtr LightningTreeNode::addChild(const Point& child_loc)
NodeSPtr Node::addChild(const Point& child_loc)
{
assert(m_p != child_loc);
LightningTreeNodeSPtr child = LightningTreeNode::create(child_loc);
NodeSPtr child = Node::create(child_loc);
return addChild(child);
}
LightningTreeNodeSPtr LightningTreeNode::addChild(LightningTreeNodeSPtr& new_child)
NodeSPtr Node::addChild(NodeSPtr& new_child)
{
assert(new_child != shared_from_this());
//assert(p != new_child->p); // NOTE: No problem for now. Issue to solve later. Maybe even afetr final. Low prio.
@ -48,8 +48,8 @@ LightningTreeNodeSPtr LightningTreeNode::addChild(LightningTreeNodeSPtr& new_chi
return new_child;
}
void LightningTreeNode::propagateToNextLayer(
std::vector<LightningTreeNodeSPtr>& next_trees,
void Node::propagateToNextLayer(
std::vector<NodeSPtr>& next_trees,
const Polygons& next_outlines,
const EdgeGrid::Grid& outline_locator,
const coord_t prune_distance,
@ -65,7 +65,7 @@ void LightningTreeNode::propagateToNextLayer(
// NOTE: Depth-first, as currently implemented.
// Skips the root (because that has no root itself), but all initial nodes will have the root point anyway.
void LightningTreeNode::visitBranches(const std::function<void(const Point&, const Point&)>& visitor) const
void Node::visitBranches(const std::function<void(const Point&, const Point&)>& visitor) const
{
for (const auto& node : m_children) {
assert(node->m_parent.lock() == shared_from_this());
@ -75,7 +75,7 @@ void LightningTreeNode::visitBranches(const std::function<void(const Point&, con
}
// NOTE: Depth-first, as currently implemented.
void LightningTreeNode::visitNodes(const std::function<void(LightningTreeNodeSPtr)>& visitor)
void Node::visitNodes(const std::function<void(NodeSPtr)>& visitor)
{
visitor(shared_from_this());
for (const auto& node : m_children) {
@ -84,13 +84,13 @@ void LightningTreeNode::visitNodes(const std::function<void(LightningTreeNodeSPt
}
}
LightningTreeNode::LightningTreeNode(const Point& p, const std::optional<Point>& last_grounding_location /*= std::nullopt*/) :
Node::Node(const Point& p, const std::optional<Point>& last_grounding_location /*= std::nullopt*/) :
m_is_root(true), m_p(p), m_last_grounding_location(last_grounding_location)
{}
LightningTreeNodeSPtr LightningTreeNode::deepCopy() const
NodeSPtr Node::deepCopy() const
{
LightningTreeNodeSPtr local_root = LightningTreeNode::create(m_p);
NodeSPtr local_root = Node::create(m_p);
local_root->m_is_root = m_is_root;
if (m_is_root)
{
@ -99,14 +99,14 @@ LightningTreeNodeSPtr LightningTreeNode::deepCopy() const
local_root->m_children.reserve(m_children.size());
for (const auto& node : m_children)
{
LightningTreeNodeSPtr child = node->deepCopy();
NodeSPtr child = node->deepCopy();
child->m_parent = local_root;
local_root->m_children.push_back(child);
}
return local_root;
}
void LightningTreeNode::reroot(LightningTreeNodeSPtr new_parent /*= nullptr*/)
void Node::reroot(NodeSPtr new_parent /*= nullptr*/)
{
if (! m_is_root) {
auto old_parent = m_parent.lock();
@ -124,13 +124,13 @@ void LightningTreeNode::reroot(LightningTreeNodeSPtr new_parent /*= nullptr*/)
}
}
LightningTreeNodeSPtr LightningTreeNode::closestNode(const Point& loc)
NodeSPtr Node::closestNode(const Point& loc)
{
LightningTreeNodeSPtr result = shared_from_this();
NodeSPtr result = shared_from_this();
auto closest_dist2 = coord_t((m_p - loc).cast<double>().norm());
for (const auto& child : m_children) {
LightningTreeNodeSPtr candidate_node = child->closestNode(loc);
NodeSPtr candidate_node = child->closestNode(loc);
const auto child_dist2 = coord_t((candidate_node->m_p - loc).cast<double>().norm());
if (child_dist2 < closest_dist2) {
closest_dist2 = child_dist2;
@ -183,7 +183,7 @@ bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeG
return visitor.d2min < within_max_dist * within_max_dist;
}
bool LightningTreeNode::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector<LightningTreeNodeSPtr>& rerooted_parts)
bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector<NodeSPtr>& rerooted_parts)
{
if (outlines.empty())
return false;
@ -192,10 +192,10 @@ bool LightningTreeNode::realign(const Polygons& outlines, const EdgeGrid::Grid&
// Only keep children that have an unbroken connection to here, realign will put the rest in rerooted parts due to recursion:
Point coll;
bool reground_me = false;
m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&](const LightningTreeNodeSPtr &child) {
m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&](const NodeSPtr &child) {
bool connect_branch = child->realign(outlines, outline_locator, rerooted_parts);
// Find an intersection of the line segment from p to child->p, at maximum outline_locator.resolution() * 2 distance from p.
if (connect_branch && lineSegmentPolygonsIntersection(child->m_p, m_p, outlines, outline_locator, coll, outline_locator.resolution() * 2)) {
if (connect_branch && lineSegmentPolygonsIntersection(child->m_p, m_p, outline_locator, coll, outline_locator.resolution() * 2)) {
child->m_last_grounding_location.reset();
child->m_parent.reset();
child->m_is_root = true;
@ -223,12 +223,12 @@ bool LightningTreeNode::realign(const Polygons& outlines, const EdgeGrid::Grid&
return false;
}
void LightningTreeNode::straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist)
void Node::straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist)
{
straighten(magnitude, m_p, 0, max_remove_colinear_dist * max_remove_colinear_dist);
}
LightningTreeNode::RectilinearJunction LightningTreeNode::straighten(
Node::RectilinearJunction Node::straighten(
const coord_t magnitude,
const Point& junction_above,
const coord_t accumulated_dist,
@ -259,7 +259,7 @@ LightningTreeNode::RectilinearJunction LightningTreeNode::straighten(
constexpr coord_t close_enough = 10;
child_p = m_children.front(); //recursive call to straighten might have removed the child
const LightningTreeNodeSPtr& parent_node = m_parent.lock();
const NodeSPtr& parent_node = m_parent.lock();
if (parent_node &&
(child_p->m_p - parent_node->m_p).cast<double>().squaredNorm() < max_remove_colinear_dist2 &&
Line::distance_to_squared(m_p, parent_node->m_p, child_p->m_p) < close_enough * close_enough) {
@ -306,7 +306,7 @@ LightningTreeNode::RectilinearJunction LightningTreeNode::straighten(
}
// Prune the tree from the extremeties (leaf-nodes) until the pruning distance is reached.
coord_t LightningTreeNode::prune(const coord_t& pruning_distance)
coord_t Node::prune(const coord_t& pruning_distance)
{
if (pruning_distance <= 0)
return 0;
@ -343,7 +343,7 @@ coord_t LightningTreeNode::prune(const coord_t& pruning_distance)
return max_distance_pruned;
}
void LightningTreeNode::convertToPolylines(Polygons& output, const coord_t line_width) const
void Node::convertToPolylines(Polygons& output, const coord_t line_width) const
{
Polygons result;
output.emplace_back();
@ -352,7 +352,7 @@ void LightningTreeNode::convertToPolylines(Polygons& output, const coord_t line_
append(output, std::move(result));
}
void LightningTreeNode::convertToPolylines(size_t long_line_idx, Polygons& output) const
void Node::convertToPolylines(size_t long_line_idx, Polygons& output) const
{
if (m_children.empty()) {
output[long_line_idx].points.push_back(m_p);
@ -364,7 +364,7 @@ void LightningTreeNode::convertToPolylines(size_t long_line_idx, Polygons& outpu
for (size_t idx_offset = 1; idx_offset < m_children.size(); idx_offset++) {
size_t child_idx = (first_child_idx + idx_offset) % m_children.size();
const LightningTreeNode& child = *m_children[child_idx];
const Node& child = *m_children[child_idx];
output.emplace_back();
size_t child_line_idx = output.size() - 1;
child.convertToPolylines(child_line_idx, output);
@ -372,7 +372,7 @@ void LightningTreeNode::convertToPolylines(size_t long_line_idx, Polygons& outpu
}
}
void LightningTreeNode::removeJunctionOverlap(Polygons& result_lines, const coord_t line_width) const
void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_width) const
{
const coord_t reduction = line_width / 2; // TODO make configurable?
for (auto poly_it = result_lines.begin(); poly_it != result_lines.end(); ) {
@ -406,3 +406,5 @@ void LightningTreeNode::removeJunctionOverlap(Polygons& result_lines, const coor
++ poly_it;
}
}
} // namespace Slic3r::FillLightning

View File

@ -12,14 +12,14 @@
#include "../../EdgeGrid.hpp"
#include "../../Polygon.hpp"
namespace Slic3r
namespace Slic3r::FillLightning
{
constexpr auto locator_cell_size = scaled<coord_t>(4.);
class LightningTreeNode;
class Node;
using LightningTreeNodeSPtr = std::shared_ptr<LightningTreeNode>;
using NodeSPtr = std::shared_ptr<Node>;
// NOTE: As written, this struct will only be valid for a single layer, will have to be updated for the next.
// NOTE: Reasons for implementing this with some separate closures:
@ -35,15 +35,15 @@ using LightningTreeNodeSPtr = std::shared_ptr<LightningTreeNode>;
* a tree. The class also has some helper functions specific to Lightning Infill
* e.g. to straighten the paths around this node.
*/
class LightningTreeNode : public std::enable_shared_from_this<LightningTreeNode>
class Node : public std::enable_shared_from_this<Node>
{
public:
// Workaround for private/protected constructors and 'make_shared': https://stackoverflow.com/a/27832765
template<typename ...Arg> LightningTreeNodeSPtr static create(Arg&&...arg)
template<typename ...Arg> NodeSPtr static create(Arg&&...arg)
{
struct EnableMakeShared : public LightningTreeNode
struct EnableMakeShared : public Node
{
EnableMakeShared(Arg&&...arg) : LightningTreeNode(std::forward<Arg>(arg)...) {}
EnableMakeShared(Arg&&...arg) : Node(std::forward<Arg>(arg)...) {}
};
return std::make_shared<EnableMakeShared>(std::forward<Arg>(arg)...);
}
@ -62,19 +62,19 @@ public:
void setLocation(const Point& p) { m_p = p; }
/*!
* Construct a new ``LightningTreeNode`` instance and add it as a child of
* Construct a new ``Node`` instance and add it as a child of
* this node.
* \param p The location of the new node.
* \return A shared pointer to the new node.
*/
LightningTreeNodeSPtr addChild(const Point& p);
NodeSPtr addChild(const Point& p);
/*!
* Add an existing ``LightningTreeNode`` as a child of this node.
* Add an existing ``Node`` as a child of this node.
* \param new_child The node that must be added as a child.
* \return Always returns \p new_child.
*/
LightningTreeNodeSPtr addChild(LightningTreeNodeSPtr& new_child);
NodeSPtr addChild(NodeSPtr& new_child);
/*!
* Propagate this node's sub-tree to the next layer.
@ -96,7 +96,7 @@ public:
*/
void propagateToNextLayer
(
std::vector<LightningTreeNodeSPtr>& next_trees,
std::vector<NodeSPtr>& next_trees,
const Polygons& next_outlines,
const EdgeGrid::Grid& outline_locator,
const coord_t prune_distance,
@ -127,7 +127,7 @@ public:
* \param visitor A function to execute for every node in this node's sub-
* tree.
*/
void visitNodes(const std::function<void(LightningTreeNodeSPtr)>& visitor);
void visitNodes(const std::function<void(NodeSPtr)>& visitor);
/*!
* Get a weighted distance from an unsupported point to this node (given the current supporting radius).
@ -156,14 +156,14 @@ public:
* This is then recursively bubbled up until it reaches the (former) root, which then will become a leaf.
* \param new_parent The (new) parent-node of the root, useful for recursing or immediately attaching the node to another tree.
*/
void reroot(LightningTreeNodeSPtr new_parent = nullptr);
void reroot(NodeSPtr new_parent = nullptr);
/*!
* Retrieves the closest node to the specified location.
* \param loc The specified location.
* \result The branch that starts at the position closest to the location within this tree.
*/
LightningTreeNodeSPtr closestNode(const Point& loc);
NodeSPtr closestNode(const Point& loc);
/*!
* Returns whether the given tree node is a descendant of this node.
@ -174,10 +174,10 @@ public:
* \return ``true`` if the given node is a descendant or this node itself,
* or ``false`` if it is not in the sub-tree.
*/
bool hasOffspring(const LightningTreeNodeSPtr& to_be_checked) const;
bool hasOffspring(const NodeSPtr& to_be_checked) const;
protected:
LightningTreeNode() = delete; // Don't allow empty contruction
Node() = delete; // Don't allow empty contruction
/*!
* Construct a new node, either for insertion in a tree or as root.
@ -185,19 +185,19 @@ protected:
* Connecting other nodes to this node indicates that a line segment should
* be drawn between those two physical positions.
*/
LightningTreeNode(const Point& p, const std::optional<Point>& last_grounding_location = std::nullopt);
Node(const Point& p, const std::optional<Point>& last_grounding_location = std::nullopt);
/*!
* Copy this node and its entire sub-tree.
* \return The equivalent of this node in the copy (the root of the new sub-
* tree).
*/
LightningTreeNodeSPtr deepCopy() const;
NodeSPtr deepCopy() const;
/*! Reconnect trees from the layer above to the new outlines of the lower layer.
* \return Wether or not the root is kept (false is no, true is yes).
*/
bool realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector<LightningTreeNodeSPtr>& rerooted_parts);
bool realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector<NodeSPtr>& rerooted_parts);
struct RectilinearJunction
{
@ -261,15 +261,15 @@ protected:
bool m_is_root;
Point m_p;
std::weak_ptr<LightningTreeNode> m_parent;
std::vector<LightningTreeNodeSPtr> m_children;
std::weak_ptr<Node> m_parent;
std::vector<NodeSPtr> m_children;
std::optional<Point> m_last_grounding_location; //<! The last known grounding location, see 'getLastGroundingLocation()'.
};
bool inside(const Polygons &polygons, const Point p);
bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const Polygons& current_outlines, const EdgeGrid::Grid& outline_locator, Point& result, const coord_t within_max_dist);
bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, const coord_t within_max_dist);
} // namespace Slic3r
} // namespace Slic3r::FillLightning
#endif // LIGHTNING_TREE_NODE_H

View File

@ -107,7 +107,10 @@ static t_config_enum_values s_keys_map_InfillPattern {
{ "archimedeanchords", ipArchimedeanChords },
{ "octagramspiral", ipOctagramSpiral },
{ "adaptivecubic", ipAdaptiveCubic },
{ "supportcubic", ipSupportCubic }
{ "supportcubic", ipSupportCubic },
#if HAS_LIGHTNING_INFILL
{ "lightning", ipLightning }
#endif // HAS_LIGHTNING_INFILL
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern)
@ -1135,6 +1138,9 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("octagramspiral");
def->enum_values.push_back("adaptivecubic");
def->enum_values.push_back("supportcubic");
#if HAS_LIGHTNING_INFILL
def->enum_values.push_back("lightning");
#endif // HAS_LIGHTNING_INFILL
def->enum_labels.push_back(L("Rectilinear"));
def->enum_labels.push_back(L("Aligned Rectilinear"));
def->enum_labels.push_back(L("Grid"));
@ -1151,6 +1157,9 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Octagram Spiral"));
def->enum_labels.push_back(L("Adaptive Cubic"));
def->enum_labels.push_back(L("Support Cubic"));
#if HAS_LIGHTNING_INFILL
def->enum_labels.push_back(L("Lightning"));
#endif // HAS_LIGHTNING_INFILL
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipStars));
def = this->add("first_layer_acceleration", coFloat);

View File

@ -57,9 +57,15 @@ enum class FuzzySkinType {
All,
};
#define HAS_LIGHTNING_INFILL 0
enum InfillPattern : int {
ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase, ipCount,
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase,
#if HAS_LIGHTNING_INFILL
ipLightning,
#endif // HAS_LIGHTNING_INFILL
ipCount,
};
enum class IroningType {