1st installment of svg icons for toolbars and gizmos

This commit is contained in:
Enrico Turri 2019-02-26 09:56:23 +01:00
commit 0f683a6a1e
34 changed files with 1629 additions and 374 deletions

View file

@ -2730,6 +2730,17 @@ void PrintConfigDef::init_sla_params()
def->cli = "";
def->min = 0;
def->default_value = new ConfigOptionFloat(1.0);
def = this->add("pad_wall_slope", coFloat);
def->label = L("Pad wall slope");
def->category = L("Pad");
def->tooltip = L("The slope of the pad wall relative to the bed plane. "
"90 degrees means straight walls.");
def->sidetext = L("degrees");
def->cli = "";
def->min = 45;
def->max = 90;
def->default_value = new ConfigOptionFloat(45.0);
}
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)

View file

@ -1028,6 +1028,9 @@ public:
// The smoothing radius of the pad edges
ConfigOptionFloat pad_edge_radius /*= 1*/;
// The slope of the pad wall...
ConfigOptionFloat pad_wall_slope;
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
@ -1053,6 +1056,7 @@ protected:
OPT_PTR(pad_wall_height);
OPT_PTR(pad_max_merge_distance);
OPT_PTR(pad_edge_radius);
OPT_PTR(pad_wall_slope);
}
};

View file

@ -601,8 +601,8 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
const double thickness = cfg.min_wall_thickness_mm;
const double wingheight = cfg.min_wall_height_mm;
const double fullheight = wingheight + thickness;
const double tilt = PI/4;
const double wingdist = wingheight / std::tan(tilt);
const double slope = cfg.wall_slope;
const double wingdist = wingheight / std::tan(slope);
// scaled values
const coord_t s_thickness = mm(thickness);
@ -627,15 +627,22 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
auto outer_base = concaveh;
outer_base.holes.clear();
offset(outer_base, s_safety_dist + s_wingdist + s_thickness);
auto inner_base = outer_base;
offset(inner_base, -(s_thickness + s_wingdist));
ExPolygon bottom_poly = outer_base;
bottom_poly.holes.clear();
if(s_wingdist > 0) offset(bottom_poly, -s_wingdist);
// Punching a hole in the top plate for the cavity
ExPolygon top_poly;
ExPolygon middle_base;
ExPolygon inner_base;
top_poly.contour = outer_base.contour;
if(wingheight > 0) {
inner_base = outer_base;
offset(inner_base, -(s_thickness + s_wingdist + s_eradius));
middle_base = outer_base;
offset(middle_base, -s_thickness);
top_poly.holes.emplace_back(middle_base.contour);
@ -682,10 +689,10 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
thrcl,
ob, wh));
// Now that we have the rounded edge connencting the top plate with
// Now that we have the rounded edge connecting the top plate with
// the outer side walls, we can generate and merge the sidewall geometry
pool.merge(walls(ob.contour, inner_base.contour, wh, -fullheight,
(s_thickness + s_wingdist) * SCALING_FACTOR, thrcl));
pool.merge(walls(ob.contour, bottom_poly.contour, wh, -fullheight,
wingdist, thrcl));
if(wingheight > 0) {
// Generate the smoothed edge geometry
@ -700,14 +707,14 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
// Next is the cavity walls connecting to the top plate's
// artificially created hole.
pool.merge(walls(inner_base.contour, ob.contour, -wingheight,
wh, -s_safety_dist * SCALING_FACTOR, thrcl));
wh, -wingdist, thrcl));
}
// Now we need to triangulate the top and bottom plates as well as the
// cavity bottom plate which is the same as the bottom plate but it is
// elevated by the thickness.
pool.merge(triangulate_expolygon_3d(top_poly));
pool.merge(triangulate_expolygon_3d(inner_base, -fullheight, true));
pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true));
if(wingheight > 0)
pool.merge(triangulate_expolygon_3d(inner_base, -wingheight));

View file

@ -3,6 +3,7 @@
#include <vector>
#include <functional>
#include <cmath>
namespace Slic3r {
@ -27,15 +28,17 @@ struct PoolConfig {
double min_wall_height_mm = 5;
double max_merge_distance_mm = 50;
double edge_radius_mm = 1;
double wall_slope = std::atan(1.0); // Universal constant for Pi/4
ThrowOnCancel throw_on_cancel = [](){};
inline PoolConfig() {}
inline PoolConfig(double wt, double wh, double md, double er):
inline PoolConfig(double wt, double wh, double md, double er, double slope):
min_wall_thickness_mm(wt),
min_wall_height_mm(wh),
max_merge_distance_mm(md),
edge_radius_mm(er) {}
edge_radius_mm(er),
wall_slope(slope) {}
};
/// Calculate the pool for the mesh for SLA printing

View file

@ -12,6 +12,7 @@
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/Model.hpp>
#include <libnest2d/optimizers/nlopt/simplex.hpp>
#include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h>
@ -594,7 +595,7 @@ double pinhead_mesh_intersect(const Vec3d& s,
double r_back,
double width,
const EigenMesh3D& m,
unsigned samples = 8,
unsigned samples = 16,
double safety_distance = 0.001)
{
// method based on:
@ -620,9 +621,20 @@ double pinhead_mesh_intersect(const Vec3d& s,
std::vector<double> phis(samples);
for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size();
a(Z) = -(v(X)*a(X) + v(Y)*a(Y)) / v(Z);
b = a.cross(v);
// We have to address the case when the direction vector v (same as dir)
// is coincident with one of the world axes. In this case two of its
// components will be completely zero and one is 1.0. Our method becomes
// dangerous here due to division with zero. Instead, vector 'a' can be an
// element-wise rotated version of 'v'
auto chk1 = [] (double val) { return std::abs(std::abs(val) - 1) < 1e-20; };
if(chk1(v(X)) || chk1(v(Y)) || chk1(v(Z))) {
a = {v(Z), v(X), v(Y)};
b = {v(Y), v(Z), v(X)};
}
else {
a(Z) = -(v(Y)*a(Y)) / v(Z); a.normalize();
b = a.cross(v);
}
// Now a and b vectors are perpendicular to v and to each other. Together
// they define the plane where we have to iterate with the given angles
@ -647,18 +659,14 @@ double pinhead_mesh_intersect(const Vec3d& s,
s(Z) + rpscos * a(Z) + rpssin * b(Z));
// Point ps is not on mesh but can be inside or outside as well. This
// would cause many problems with ray-casting. So we query the closest
// point on the mesh to this.
// auto psq = m.signed_distance(ps);
// would cause many problems with ray-casting. To detect the position we
// will use the ray-casting result (which has an is_inside predicate).
// This is the point on the circle on the back sphere
Vec3d p(c(X) + rpbcos * a(X) + rpbsin * b(X),
c(Y) + rpbcos * a(Y) + rpbsin * b(Y),
c(Z) + rpbcos * a(Z) + rpbsin * b(Z));
// Vec3d n = (p - psq.point_on_mesh()).normalized();
// phi = m.query_ray_hit(psq.point_on_mesh() + sd*n, n);
Vec3d n = (p - ps).normalized();
auto hr = m.query_ray_hit(ps + sd*n, n);
@ -693,8 +701,18 @@ double bridge_mesh_intersect(const Vec3d& s,
Vec3d a(0, 1, 0), b;
const double& sd = safety_distance;
a(Z) = -(dir(X)*a(X) + dir(Y)*a(Y)) / dir(Z);
b = a.cross(dir);
// INFO: for explanation of the method used here, see the previous method's
// comments.
auto chk1 = [] (double val) { return std::abs(std::abs(val) - 1) < 1e-20; };
if(chk1(dir(X)) || chk1(dir(Y)) || chk1(dir(Z))) {
a = {dir(Z), dir(X), dir(Y)};
b = {dir(Y), dir(Z), dir(X)};
}
else {
a(Z) = -(dir(Y)*a(Y)) / dir(Z); a.normalize();
b = a.cross(dir);
}
// circle portions
std::vector<double> phis(samples);
@ -1156,16 +1174,16 @@ bool SLASupportTree::generate(const PointSet &points,
//...
};
// t-hrow i-f c-ance-l-ed: It will be called many times so a shorthand will
// throw if canceled: It will be called many times so a shorthand will
// come in handy.
auto& tifcl = ctl.cancelfn;
auto& thr = 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 = [thr] (
const SupportConfig& cfg,
const PointSet& points,
const EigenMesh3D& mesh,
@ -1179,9 +1197,9 @@ bool SLASupportTree::generate(const PointSet &points,
// first one
auto aliases =
cluster(points,
[tifcl](const SpatElement& p, const SpatElement& se)
[thr](const SpatElement& p, const SpatElement& se)
{
tifcl();
thr();
return distance(p.first, se.first) < D_SP;
}, 2);
@ -1192,10 +1210,10 @@ bool SLASupportTree::generate(const PointSet &points,
filt_pts.row(count++) = points.row(a.front());
}
tifcl();
thr();
// calculate the normals to the triangles belonging to filtered points
auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, tifcl);
auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, thr);
head_norm.resize(count, 3);
head_pos.resize(count, 3);
@ -1207,9 +1225,15 @@ bool SLASupportTree::generate(const PointSet &points,
// not be enough space for the pinhead. Filtering is applied for
// these reasons.
using libnest2d::opt::bound;
using libnest2d::opt::initvals;
using libnest2d::opt::SimplexOptimizer;
using libnest2d::opt::StopCriteria;
static const unsigned MAX_TRIES = 100;
int pcount = 0, hlcount = 0;
for(int i = 0; i < count; i++) {
tifcl();
thr();
auto n = nmls.row(i);
// for all normals we generate the spherical coordinates and
@ -1230,32 +1254,67 @@ bool SLASupportTree::generate(const PointSet &points,
// We saturate the polar angle to 3pi/4
polar = std::max(polar, 3*PI / 4);
// Reassemble the now corrected normal
Vec3d nn(std::cos(azimuth) * std::sin(polar),
std::sin(azimuth) * std::sin(polar),
std::cos(polar));
nn.normalize();
// save the head (pinpoint) position
Vec3d hp = filt_pts.row(i);
// the full width of the head
double w = cfg.head_width_mm +
cfg.head_back_radius_mm +
2*cfg.head_front_radius_mm;
// We should shoot a ray in the direction of the pinhead and
// see if there is enough space for it
double t = pinhead_mesh_intersect(
hp, // touching point
nn,
cfg.head_front_radius_mm, // approx the radius
cfg.head_back_radius_mm,
w,
mesh);
// Reassemble the now corrected normal
auto nn = Vec3d(std::cos(azimuth) * std::sin(polar),
std::sin(azimuth) * std::sin(polar),
std::cos(polar)).normalized();
if(t > w || std::isinf(t)) {
// check available distance
double t = pinhead_mesh_intersect(
hp, // touching point
nn, // normal
cfg.head_front_radius_mm,
cfg.head_back_radius_mm,
w,
mesh);
if(t <= w) {
// Let's try to optimize this angle, there might be a viable
// normal that doesn't collide with the model geometry and
// its very close to the default.
StopCriteria stc;
stc.max_iterations = MAX_TRIES;
stc.relative_score_difference = 1e-3;
stc.stop_score = w; // space greater than w is enough
SimplexOptimizer solver(stc);
auto oresult = solver.optimize_max(
[&mesh, &cfg, w, hp](double plr, double azm)
{
auto n = Vec3d(std::cos(azm) * std::sin(plr),
std::sin(azm) * std::sin(plr),
std::cos(plr)).normalized();
double score = pinhead_mesh_intersect(
hp, n,
cfg.head_front_radius_mm,
cfg.head_back_radius_mm,
w,
mesh);
return score;
},
initvals(polar, azimuth), // let's start with what we have
bound(3*PI/4, PI), // Must not exceed the tilt limit
bound(-PI, PI) // azimuth can be a full range search
);
t = oresult.score;
polar = std::get<0>(oresult.optimum);
azimuth = std::get<1>(oresult.optimum);
nn = Vec3d(std::cos(azimuth) * std::sin(polar),
std::sin(azimuth) * std::sin(polar),
std::cos(polar)).normalized();
}
if(t > w) {
head_pos.row(pcount) = hp;
// save the verified and corrected normal
@ -1263,6 +1322,7 @@ bool SLASupportTree::generate(const PointSet &points,
++pcount;
} else if( polar >= 3*PI/4 ) {
// Headless supports do not tilt like the headed ones so
// the normal should point almost to the ground.
headless_norm.row(hlcount) = nn;
@ -1279,7 +1339,7 @@ bool SLASupportTree::generate(const PointSet &points,
// Pinhead creation: based on the filtering results, the Head objects will
// be constructed (together with their triangle meshes).
auto pinheadfn = [tifcl] (
auto pinheadfn = [thr] (
const SupportConfig& cfg,
PointSet& head_pos,
PointSet& nmls,
@ -1292,7 +1352,7 @@ bool SLASupportTree::generate(const PointSet &points,
/* ******************************************************** */
for (int i = 0; i < head_pos.rows(); ++i) {
tifcl();
thr();
result.add_head(
cfg.head_back_radius_mm,
cfg.head_front_radius_mm,
@ -1311,7 +1371,7 @@ bool SLASupportTree::generate(const PointSet &points,
// 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 = [thr] (
const SupportConfig& cfg,
const EigenMesh3D& mesh,
PointSet& head_pos,
@ -1320,7 +1380,8 @@ bool SLASupportTree::generate(const PointSet &points,
std::vector<double>& gndheight,
ClusteredPoints& ground_clusters,
Result& result
) {
)
{
/* ******************************************************** */
/* Classification */
@ -1331,11 +1392,11 @@ bool SLASupportTree::generate(const PointSet &points,
gndidx.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
// First we 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++) {
tifcl();
thr();
auto& head = result.head(i);
Vec3d dir(0, 0, -1);
@ -1344,9 +1405,38 @@ bool SLASupportTree::generate(const PointSet &points,
double t = std::numeric_limits<double>::infinity();
double hw = head.width_mm;
{
// using libnest2d::opt::Method;
// using libnest2d::opt::bound;
// using libnest2d::opt::Optimizer;
// using libnest2d::opt::TOptimizer;
// using libnest2d::opt::StopCriteria;
// auto stopcond = [] () { return false; };
// static const unsigned max_tries = 100;
// auto objfunc =
// [&head](double polar, double azimuth, double width)
// {
// Vec3d nn(std::cos(azimuth) * std::sin(polar),
// std::sin(azimuth) * std::sin(polar),
// std::cos(polar));
// };
// StopCriteria stc;
// stc.max_iterations = max_tries;
// stc.relative_score_difference = 1e-3;
// stc.stop_condition = stopcond;
// TOptimizer<Method::L_SIMPLEX> solver(stc);
}
// 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.
// the head width so that the pillar can be deployed.
while(!accept && head.width_mm > 0) {
Vec3d startpoint = head.junction_point();
@ -1358,11 +1448,18 @@ bool SLASupportTree::generate(const PointSet &points,
double tprec = ray_mesh_intersect(startpoint, dir, mesh);
if(std::isinf(tprec) && !std::isinf(t)) {
// This is a damned case where the pillar melds into the
// This is a damned case where the pillar melts into the
// model but its center ray can reach the ground. We can
// not route this to the ground nor to the model surface.
head.width_mm = hw + (ri % 2? -1 : 1) * ri * head.r_back_mm;
} else {
if(!std::isinf(t) && !std::isinf(tprec) &&
std::abs(tprec - t) > hw)
{
// In this case the head would scratch the model body
BOOST_LOG_TRIVIAL(warning) << "Head scratch detected.";
}
accept = true; t = tprec;
auto id = head.id;
@ -1417,9 +1514,9 @@ bool SLASupportTree::generate(const PointSet &points,
ground_clusters =
cluster(
gnd,
[d_base, tifcl](const SpatElement& p, const SpatElement& s)
[d_base, thr](const SpatElement& p, const SpatElement& s)
{
tifcl();
thr();
return distance(Vec2d(p.first(X), p.first(Y)),
Vec2d(s.first(X), s.first(Y))) < d_base;
}, 3); // max 3 heads to connect to one centroid
@ -1492,7 +1589,7 @@ bool SLASupportTree::generate(const PointSet &points,
// 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, thr](
const SupportConfig& cfg,
const ClusteredPoints& gnd_clusters,
const IndexSet& gndidx,
@ -1508,7 +1605,7 @@ bool SLASupportTree::generate(const PointSet &points,
cl_centroids.reserve(gnd_clusters.size());
SpatIndex pheadindex; // spatial index for the junctions
for(auto& cl : gnd_clusters) { tifcl();
for(auto& cl : gnd_clusters) { thr();
// place all the centroid head positions into the index. We will
// query for alternative pillar positions. If a sidehead cannot
// connect to the cluster centroid, we have to search for another
@ -1519,9 +1616,9 @@ bool SLASupportTree::generate(const PointSet &points,
// get the current cluster centroid
long lcid = cluster_centroid(cl, gnd_head_pt,
[tifcl](const Vec3d& p1, const Vec3d& p2)
[thr](const Vec3d& p1, const Vec3d& p2)
{
tifcl();
thr();
return distance(Vec2d(p1(X), p1(Y)), Vec2d(p2(X), p2(Y)));
});
@ -1542,7 +1639,7 @@ bool SLASupportTree::generate(const PointSet &points,
// sidepoints with the cluster centroid (which is a ground pillar)
// or a nearby pillar if the centroid is unreachable.
size_t ci = 0;
for(auto cl : gnd_clusters) { tifcl();
for(auto cl : gnd_clusters) { thr();
auto cidx = cl_centroids[ci];
cl_centroids[ci++] = cl[cidx];
@ -1566,12 +1663,12 @@ bool SLASupportTree::generate(const PointSet &points,
// is distributed more effectively on the pillar.
auto search_nearest =
[&tifcl, &cfg, &result, &emesh, maxbridgelen, gndlvl, pradius]
[&thr, &cfg, &result, &emesh, maxbridgelen, gndlvl, pradius]
(SpatIndex& spindex, const Vec3d& jsh)
{
long nearest_id = -1;
const double max_len = maxbridgelen / 2;
while(nearest_id < 0 && !spindex.empty()) { tifcl();
while(nearest_id < 0 && !spindex.empty()) { thr();
// loop until a suitable head is not found
// if there is a pillar closer than the cluster center
// (this may happen as the clustering is not perfect)
@ -1610,7 +1707,7 @@ bool SLASupportTree::generate(const PointSet &points,
return nearest_id;
};
for(auto c : cl) { tifcl();
for(auto c : cl) { thr();
auto& sidehead = result.head(gndidx[c]);
sidehead.transform();
@ -1676,7 +1773,7 @@ bool SLASupportTree::generate(const PointSet &points,
ClusterEl ring;
while(!rem.empty()) { // loop until all the points belong to some ring
tifcl();
thr();
std::sort(rem.begin(), rem.end());
auto newring = pts_convex_hull(rem,
@ -1688,7 +1785,7 @@ bool SLASupportTree::generate(const PointSet &points,
if(!ring.empty()) {
// inner ring is now in 'newring' and outer ring is in 'ring'
SpatIndex innerring;
for(unsigned i : newring) { tifcl();
for(unsigned i : newring) { thr();
const Pillar& pill = result.head_pillar(gndidx[i]);
assert(pill.id >= 0);
innerring.insert(pill.endpoint, unsigned(pill.id));
@ -1697,7 +1794,7 @@ bool SLASupportTree::generate(const PointSet &points,
// For all pillars in the outer ring find the closest in the
// inner ring and connect them. This will create the spider web
// fashioned connections between pillars
for(unsigned i : ring) { tifcl();
for(unsigned i : ring) { thr();
const Pillar& outerpill = result.head_pillar(gndidx[i]);
auto res = innerring.nearest(outerpill.endpoint, 1);
if(res.empty()) continue;
@ -1723,7 +1820,7 @@ bool SLASupportTree::generate(const PointSet &points,
next != ring.end();
++it, ++next)
{
tifcl();
thr();
const Pillar& pillar = result.head_pillar(gndidx[*it]);
const Pillar& nextpillar = result.head_pillar(gndidx[*next]);
interconnect(pillar, nextpillar, emesh, result);
@ -1738,19 +1835,19 @@ bool SLASupportTree::generate(const PointSet &points,
}
};
// Step: routing the pinheads that are would connect to the model surface
// Step: routing the pinheads that 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 = [thr](
const SupportConfig& cfg,
const std::vector<double>& gndheight,
const IndexSet& nogndidx,
Result& result)
{
// TODO: connect these to the ground pillars if possible
for(auto idx : nogndidx) { tifcl();
for(auto idx : nogndidx) { thr();
double gh = gndheight[idx];
double base_width = cfg.head_width_mm;
@ -1807,7 +1904,7 @@ 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 = [thr](
const SupportConfig& cfg,
const PointSet& headless_pts,
const PointSet& headless_norm,
@ -1822,7 +1919,7 @@ bool SLASupportTree::generate(const PointSet &points,
// We will sink the pins into the model surface for a distance of 1/3 of
// the pin radius
for(int i = 0; i < headless_pts.rows(); i++) { tifcl();
for(int i = 0; i < headless_pts.rows(); i++) { thr();
Vec3d sph = headless_pts.row(i); // Exact support position
Vec3d n = headless_norm.row(i); // mesh outward normal
Vec3d sp = sph - n * HWIDTH_MM; // stick head start point

View file

@ -205,9 +205,11 @@ template<class Vec> double distance(const Vec& pp1, const Vec& pp2) {
return std::sqrt(p.transpose() * p);
}
PointSet normals(const PointSet& points, const EigenMesh3D& mesh,
PointSet normals(const PointSet& points,
const EigenMesh3D& mesh,
double eps,
std::function<void()> throw_on_cancel) {
std::function<void()> throw_on_cancel)
{
if(points.rows() == 0 || mesh.V().rows() == 0 || mesh.F().rows() == 0)
return {};
@ -228,7 +230,7 @@ PointSet normals(const PointSet& points, const EigenMesh3D& mesh,
const Vec3d& p3 = mesh.V().row(trindex(2));
// We should check if the point lies on an edge of the hosting triangle.
// If it does than all the other triangles using the same two points
// If it does then all the other triangles using the same two points
// have to be searched and the final normal should be some kind of
// aggregation of the participating triangle normals. We should also
// consider the cases where the support point lies right on a vertex

View file

@ -415,49 +415,73 @@ void SLAPrint::set_task(const TaskParams &params)
n_object_steps = (int)slaposCount;
if (params.single_model_object.valid()) {
// Find the print object to be processed with priority.
SLAPrintObject *print_object = nullptr;
size_t idx_print_object = 0;
for (; idx_print_object < m_objects.size(); ++idx_print_object)
for (; idx_print_object < m_objects.size(); ++ idx_print_object)
if (m_objects[idx_print_object]->model_object()->id() == params.single_model_object) {
print_object = m_objects[idx_print_object];
break;
}
assert(print_object != nullptr);
bool shall_cancel = false;
for (int istep = 0; istep < n_object_steps; ++istep)
if (! print_object->m_stepmask[istep]) {
shall_cancel = true;
// Find out whether the priority print object is being currently processed.
bool running = false;
for (int istep = 0; istep < n_object_steps; ++ istep) {
if (! print_object->m_stepmask[istep])
// Step was skipped, cancel.
break;
if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) {
// No step was skipped, and a wanted step is being processed. Don't cancel.
running = true;
break;
}
bool running = false;
if (!shall_cancel) {
for (int istep = 0; istep < n_object_steps; ++ istep)
if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) {
running = true;
break;
}
}
if (!running)
if (! running)
this->call_cancel_callback();
// Now the background process is either stopped, or it is inside one of the print object steps to be calculated anyway.
if (params.single_model_instance_only) {
// Suppress all the steps of other instances.
for (SLAPrintObject *po : m_objects)
for (int istep = 0; istep < (int)slaposCount; ++istep)
for (int istep = 0; istep < (int)slaposCount; ++ istep)
po->m_stepmask[istep] = false;
}
else if (!running) {
} else if (! running) {
// Swap the print objects, so that the selected print_object is first in the row.
// At this point the background processing must be stopped, so it is safe to shuffle print objects.
if (idx_print_object != 0)
std::swap(m_objects.front(), m_objects[idx_print_object]);
}
// and set the steps for the current object.
for (int istep = 0; istep < n_object_steps; ++ istep)
print_object->m_stepmask[istep] = true;
for (int istep = n_object_steps; istep < (int)slaposCount; ++istep)
for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep)
print_object->m_stepmask[istep] = false;
}
} else {
// Slicing all objects.
bool running = false;
for (SLAPrintObject *print_object : m_objects)
for (int istep = 0; istep < n_object_steps; ++ istep) {
if (! print_object->m_stepmask[istep]) {
// Step may have been skipped. Restart.
goto loop_end;
}
if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) {
// This step is running, and the state cannot be changed due to the this->state_mutex() being locked.
// It is safe to manipulate m_stepmask of other SLAPrintObjects and SLAPrint now.
running = true;
goto loop_end;
}
}
loop_end:
if (! running)
this->call_cancel_callback();
for (SLAPrintObject *po : m_objects) {
for (int istep = 0; istep < n_object_steps; ++ istep)
po->m_stepmask[istep] = true;
for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep)
po->m_stepmask[istep] = false;
}
}
if (params.to_object_step != -1 || params.to_print_step != -1) {
// Limit the print steps.
@ -484,7 +508,7 @@ void SLAPrint::finalize()
std::string SLAPrint::output_filename() const
{
DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders();
return this->PrintBase::output_filename(m_print_config.output_filename_format.value, "zip", &config);
return this->PrintBase::output_filename(m_print_config.output_filename_format.value, "sl1", &config);
}
namespace {
@ -727,11 +751,13 @@ void SLAPrint::process()
double wt = po.m_config.pad_wall_thickness.getFloat();
double h = po.m_config.pad_wall_height.getFloat();
double md = po.m_config.pad_max_merge_distance.getFloat();
double er = po.m_config.pad_edge_radius.getFloat();
// Radius is disabled for now...
double er = 0; // po.m_config.pad_edge_radius.getFloat();
double tilt = po.m_config.pad_wall_slope.getFloat() * PI / 180.0;
double lh = po.m_config.layer_height.getFloat();
double elevation = po.m_config.support_object_elevation.getFloat();
if(!po.m_config.supports_enable.getBool()) elevation = 0;
sla::PoolConfig pcfg(wt, h, md, er);
sla::PoolConfig pcfg(wt, h, md, er, tilt);
ExPolygons bp;
double pad_h = sla::get_pad_fullheight(pcfg);
@ -742,8 +768,7 @@ void SLAPrint::process()
if(elevation < pad_h) {
// we have to count with the model geometry for the base plate
sla::base_plate(trmesh, bp, float(pad_h), float(lh),
thrfn);
sla::base_plate(trmesh, bp, float(pad_h), float(lh), thrfn);
}
pcfg.throw_on_cancel = thrfn;
@ -1344,6 +1369,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
|| opt_key == "pad_wall_thickness"
|| opt_key == "pad_wall_height"
|| opt_key == "pad_max_merge_distance"
|| opt_key == "pad_wall_slope"
|| opt_key == "pad_edge_radius") {
steps.emplace_back(slaposBasePool);
} else {

View file

@ -57,9 +57,7 @@
// Printbed textures generated from svg files
#define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7)
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// Toolbars, Gizmos and other GUI icons imported from svg files
#define ENABLE_SVG_ICONS (1 && ENABLE_1_42_0_ALPHA7)
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// Toolbars and Gizmos use icons imported from svg files
#define ENABLE_SVG_ICONS (1 && ENABLE_TEXTURES_FROM_SVG)
#endif // _technologies_h_