mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-12 09:17:52 -06:00
A lot of comments added.
This commit is contained in:
parent
17bba23dba
commit
4f83703232
1 changed files with 86 additions and 25 deletions
|
@ -707,6 +707,19 @@ ClusteredPoints cluster(
|
||||||
std::function<bool(const SpatElement&, const SpatElement&)> pred,
|
std::function<bool(const SpatElement&, const SpatElement&)> pred,
|
||||||
unsigned max_points = 0);
|
unsigned max_points = 0);
|
||||||
|
|
||||||
|
// This class will hold the support tree meshes with some additional bookkeeping
|
||||||
|
// as well. Various parts of the support geometry are stored separately and are
|
||||||
|
// merged when the caller queries the merged mesh. The merged result is cached
|
||||||
|
// for fast subsequent delivery of the merged mesh which can be quite complex.
|
||||||
|
// An object of this class will be used as the result type during the support
|
||||||
|
// generation algorithm. Parts will be added with the appropriate methods such
|
||||||
|
// as add_head or add_pillar which forwards the constructor arguments and fills
|
||||||
|
// the IDs of these substructures. The IDs are basically indices into the arrays
|
||||||
|
// of the appropriate type (heads, pillars, etc...). One can later query e.g. a
|
||||||
|
// pillar for a specific head...
|
||||||
|
//
|
||||||
|
// The support pad is considered an auxiliary geometry and is not part of the
|
||||||
|
// merged mesh. It can be retrieved using a dedicated method (pad())
|
||||||
class SLASupportTree::Impl {
|
class SLASupportTree::Impl {
|
||||||
std::vector<Head> m_heads;
|
std::vector<Head> m_heads;
|
||||||
std::vector<Pillar> m_pillars;
|
std::vector<Pillar> m_pillars;
|
||||||
|
@ -1067,16 +1080,22 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
// Indices of those who don't touch the ground
|
// Indices of those who don't touch the ground
|
||||||
IndexSet noground_heads;
|
IndexSet noground_heads;
|
||||||
|
|
||||||
|
// Groups of the 'ground_head' indices that belong into one cluster. These
|
||||||
|
// are candidates to be connected to one pillar.
|
||||||
ClusteredPoints ground_connectors;
|
ClusteredPoints ground_connectors;
|
||||||
|
|
||||||
|
// A help function to translate ground head index to the actual coordinates.
|
||||||
auto gnd_head_pt = [&ground_heads, &head_positions] (size_t idx) {
|
auto gnd_head_pt = [&ground_heads, &head_positions] (size_t idx) {
|
||||||
return Vec3d(head_positions.row(ground_heads[idx]));
|
return Vec3d(head_positions.row(ground_heads[idx]));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This algorithm uses the Impl class as its output stream. It will be
|
||||||
|
// filled gradually with support elements (heads, pillars, bridges, ...)
|
||||||
using Result = SLASupportTree::Impl;
|
using Result = SLASupportTree::Impl;
|
||||||
|
|
||||||
Result& result = *m_impl;
|
Result& result = *m_impl;
|
||||||
|
|
||||||
|
// Let's define the individual steps of the processing. We can experiment
|
||||||
|
// later with the ordering and the dependencies between them.
|
||||||
enum Steps {
|
enum Steps {
|
||||||
BEGIN,
|
BEGIN,
|
||||||
FILTER,
|
FILTER,
|
||||||
|
@ -1092,14 +1111,15 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
//...
|
//...
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debug:
|
// t-hrow i-f c-ance-l-ed: It will be called many times so a shorthand will
|
||||||
// for(int pn = 0; pn < points.rows(); ++pn) {
|
// come in handy.
|
||||||
// std::cout << "p " << pn << " " << points.row(pn) << std::endl;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
auto& tifcl = ctl.cancelfn;
|
auto& tifcl = ctl.cancelfn;
|
||||||
|
|
||||||
|
// Filtering step: here we will discard inappropriate support points and
|
||||||
|
// decide the future of the appropriate ones. We will check if a pinhead
|
||||||
|
// is applicable and adjust its angle at each support point.
|
||||||
|
// We will also merge the support points that are just too close and can be
|
||||||
|
// considered as one.
|
||||||
auto filterfn = [tifcl] (
|
auto filterfn = [tifcl] (
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const PointSet& points,
|
const PointSet& points,
|
||||||
|
@ -1110,10 +1130,6 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
PointSet& headless_pos,
|
PointSet& headless_pos,
|
||||||
PointSet& headless_norm)
|
PointSet& headless_norm)
|
||||||
{
|
{
|
||||||
/* ******************************************************** */
|
|
||||||
/* Filtering step */
|
|
||||||
/* ******************************************************** */
|
|
||||||
|
|
||||||
// Get the points that are too close to each other and keep only the
|
// Get the points that are too close to each other and keep only the
|
||||||
// first one
|
// first one
|
||||||
auto aliases =
|
auto aliases =
|
||||||
|
@ -1214,7 +1230,8 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
headless_norm.conservativeResize(hlcount, Eigen::NoChange);
|
headless_norm.conservativeResize(hlcount, Eigen::NoChange);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to write the pinheads into the result
|
// Pinhead creation: based on the filtering results, the Head objects will
|
||||||
|
// be constructed (together with their triangle meshes).
|
||||||
auto pinheadfn = [tifcl] (
|
auto pinheadfn = [tifcl] (
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
PointSet& head_pos,
|
PointSet& head_pos,
|
||||||
|
@ -1240,8 +1257,13 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// &filtered_points, &head_positions, &result, &mesh,
|
// Further classification of the support points with pinheads. If the
|
||||||
// &gndidx, &gndheight, &nogndidx, cfg
|
// ground is directly reachable through a vertical line parallel to the Z
|
||||||
|
// axis we consider a support point as pillar candidate. If touches the
|
||||||
|
// model geometry, it will be marked as non-ground facing and further steps
|
||||||
|
// will process it. Also, the pillars will be grouped into clusters that can
|
||||||
|
// be interconnected with bridges. Elements of these groups may or may not
|
||||||
|
// be interconnected. Here we only run the clustering algorithm.
|
||||||
auto classifyfn = [tifcl] (
|
auto classifyfn = [tifcl] (
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const EigenMesh3D& mesh,
|
const EigenMesh3D& mesh,
|
||||||
|
@ -1262,6 +1284,9 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
gndidx.reserve(size_t(head_pos.rows()));
|
gndidx.reserve(size_t(head_pos.rows()));
|
||||||
nogndidx.reserve(size_t(head_pos.rows()));
|
nogndidx.reserve(size_t(head_pos.rows()));
|
||||||
|
|
||||||
|
// First we search decide which heads reach the ground and can be full
|
||||||
|
// pillars and which shall be connected to the model surface (or search
|
||||||
|
// a suitable path around the surface that leads to the ground -- TODO)
|
||||||
for(unsigned i = 0; i < head_pos.rows(); i++) {
|
for(unsigned i = 0; i < head_pos.rows(); i++) {
|
||||||
tifcl();
|
tifcl();
|
||||||
auto& head = result.head(i);
|
auto& head = result.head(i);
|
||||||
|
@ -1272,6 +1297,9 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
double t = std::numeric_limits<double>::infinity();
|
double t = std::numeric_limits<double>::infinity();
|
||||||
double hw = head.width_mm;
|
double hw = head.width_mm;
|
||||||
|
|
||||||
|
// We will try to assign a pillar to all the pinheads. If a pillar
|
||||||
|
// would pierce the model surface, we will try to adjust slightly
|
||||||
|
// the head with so that the pillar can be deployed.
|
||||||
while(!accept && head.width_mm > 0) {
|
while(!accept && head.width_mm > 0) {
|
||||||
|
|
||||||
Vec3d startpoint = head.junction_point();
|
Vec3d startpoint = head.junction_point();
|
||||||
|
@ -1283,10 +1311,9 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
double tprec = ray_mesh_intersect(startpoint, dir, mesh);
|
double tprec = ray_mesh_intersect(startpoint, dir, mesh);
|
||||||
|
|
||||||
if(std::isinf(tprec) && !std::isinf(t)) {
|
if(std::isinf(tprec) && !std::isinf(t)) {
|
||||||
// This is a damned case where the pillar melds into the model
|
// This is a damned case where the pillar melds into the
|
||||||
// but its center ray can reach the ground. We can not route
|
// model but its center ray can reach the ground. We can
|
||||||
// this to the ground nor to the model surface. We have to
|
// not route this to the ground nor to the model surface.
|
||||||
// modify the head or discard this support point.
|
|
||||||
head.width_mm = hw + (ri % 2? -1 : 1) * ri * head.r_back_mm;
|
head.width_mm = hw + (ri % 2? -1 : 1) * ri * head.r_back_mm;
|
||||||
} else {
|
} else {
|
||||||
accept = true; t = tprec;
|
accept = true; t = tprec;
|
||||||
|
@ -1305,12 +1332,23 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
ri++;
|
ri++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save the distance from a surface in the Z axis downwards. It may
|
||||||
|
// be infinity but that is telling us that it touches the ground.
|
||||||
gndheight.emplace_back(t);
|
gndheight.emplace_back(t);
|
||||||
|
|
||||||
if(accept) {
|
if(accept) {
|
||||||
if(std::isinf(t)) gndidx.emplace_back(i);
|
if(std::isinf(t)) gndidx.emplace_back(i);
|
||||||
else nogndidx.emplace_back(i);
|
else nogndidx.emplace_back(i);
|
||||||
} else {
|
} else {
|
||||||
|
// This is a serious issue. There was no way to deploy a pillar
|
||||||
|
// for the given pinhead. The whole thing has to be discarded
|
||||||
|
// leaving the model potentially unprintable.
|
||||||
|
//
|
||||||
|
// TODO: In the future this has to be solved by searching for
|
||||||
|
// a path in 3D space from this support point to a suitable
|
||||||
|
// pillar position or an existing pillar.
|
||||||
|
// As a workaround we could mark this head as "sidehead only"
|
||||||
|
// let it go trough the nearby pillar search in the next step.
|
||||||
BOOST_LOG_TRIVIAL(warning) << "A support point at "
|
BOOST_LOG_TRIVIAL(warning) << "A support point at "
|
||||||
<< head.tr.transpose()
|
<< head.tr.transpose()
|
||||||
<< " had to be discarded as there is"
|
<< " had to be discarded as there is"
|
||||||
|
@ -1319,8 +1357,8 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transform the ground facing point indices top actual coordinates.
|
||||||
PointSet gnd(gndidx.size(), 3);
|
PointSet gnd(gndidx.size(), 3);
|
||||||
|
|
||||||
for(size_t i = 0; i < gndidx.size(); i++)
|
for(size_t i = 0; i < gndidx.size(); i++)
|
||||||
gnd.row(long(i)) = head_pos.row(gndidx[i]);
|
gnd.row(long(i)) = head_pos.row(gndidx[i]);
|
||||||
|
|
||||||
|
@ -1340,7 +1378,8 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
}, 3); // max 3 heads to connect to one centroid
|
}, 3); // max 3 heads to connect to one centroid
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function for interconnecting two pillars with zig-zag bridges
|
// Helper function for interconnecting two pillars with zig-zag bridges.
|
||||||
|
// This is not an individual step.
|
||||||
auto interconnect = [&cfg](
|
auto interconnect = [&cfg](
|
||||||
const Pillar& pillar,
|
const Pillar& pillar,
|
||||||
const Pillar& nextpillar,
|
const Pillar& nextpillar,
|
||||||
|
@ -1401,6 +1440,11 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Step: Routing the ground connected pinheads, and interconnecting them
|
||||||
|
// with additional (angled) bridges. Not all of these pinheads will be
|
||||||
|
// a full pillar (ground connected). Some will connect to a nearby pillar
|
||||||
|
// using a bridge. The max number of such side-heads for a central pillar
|
||||||
|
// is limited to avoid bad weight distribution.
|
||||||
auto routing_ground_fn = [gnd_head_pt, interconnect, tifcl](
|
auto routing_ground_fn = [gnd_head_pt, interconnect, tifcl](
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const ClusteredPoints& gnd_clusters,
|
const ClusteredPoints& gnd_clusters,
|
||||||
|
@ -1505,7 +1549,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
}
|
}
|
||||||
|
|
||||||
double d = distance(jp, jn);
|
double d = distance(jp, jn);
|
||||||
if(jn(Z) <= gndlvl || d > max_len) break;
|
if(jn(Z) <= gndlvl + nearhead.r_back_mm || d > max_len) break;
|
||||||
|
|
||||||
double chkd = bridge_mesh_intersect(jp, dirv(jp, jn),
|
double chkd = bridge_mesh_intersect(jp, dirv(jp, jn),
|
||||||
pradius,
|
pradius,
|
||||||
|
@ -1644,6 +1688,11 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Step: routing the pinheads that are would connect to the model surface
|
||||||
|
// along the Z axis downwards. For now these will actually be connected with
|
||||||
|
// the model surface with a flipped pinhead. In the future here we could use
|
||||||
|
// some smart algorithms to search for a safe path to the ground or to a
|
||||||
|
// nearby pillar that can hold the supported weight.
|
||||||
auto routing_nongnd_fn = [tifcl](
|
auto routing_nongnd_fn = [tifcl](
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const std::vector<double>& gndheight,
|
const std::vector<double>& gndheight,
|
||||||
|
@ -1705,6 +1754,9 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Step: process the support points where there is not enough space for a
|
||||||
|
// full pinhead. In this case we will use a rounded sphere as a touching
|
||||||
|
// point and use a thinner bridge (let's call it a stick).
|
||||||
auto process_headless = [tifcl](
|
auto process_headless = [tifcl](
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const PointSet& headless_pts,
|
const PointSet& headless_pts,
|
||||||
|
@ -1746,16 +1798,24 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using std::ref;
|
// Now that the individual blocks are defined, lets connect the wires. We
|
||||||
using std::cref;
|
// will create an array of functions which represents a program. Place the
|
||||||
|
// step methods in the array and bind the right arguments to the methods
|
||||||
|
// This way the data dependencies will be easily traceable between
|
||||||
|
// individual steps.
|
||||||
|
// There will be empty steps as well like the begin step or the done or
|
||||||
|
// abort steps. These are slots for future initialization or cleanup.
|
||||||
|
|
||||||
|
using std::cref; // Bind inputs with cref (read-only)
|
||||||
|
using std::ref; // Bind outputs with ref (writable)
|
||||||
using std::bind;
|
using std::bind;
|
||||||
|
|
||||||
// Here we can easily track what goes in and what comes out of each step:
|
// Here we can easily track what goes in and what comes out of each step:
|
||||||
// (see the cref-s as inputs and ref-s as outputs)
|
// (see the cref-s as inputs and ref-s as outputs)
|
||||||
std::array<std::function<void()>, NUM_STEPS> program = {
|
std::array<std::function<void()>, NUM_STEPS> program = {
|
||||||
[] () {
|
[] () {
|
||||||
// Begin
|
// Begin...
|
||||||
// clear up the shared data
|
// Potentially clear up the shared data (not needed for now)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Filtering unnecessary support points
|
// Filtering unnecessary support points
|
||||||
|
@ -1798,6 +1858,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
|
|
||||||
Steps pc = BEGIN, pc_prev = BEGIN;
|
Steps pc = BEGIN, pc_prev = BEGIN;
|
||||||
|
|
||||||
|
// Let's define a simple automaton that will run our program.
|
||||||
auto progress = [&ctl, &pc, &pc_prev] () {
|
auto progress = [&ctl, &pc, &pc_prev] () {
|
||||||
static const std::array<std::string, NUM_STEPS> stepstr {
|
static const std::array<std::string, NUM_STEPS> stepstr {
|
||||||
"Starting",
|
"Starting",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue