This commit is contained in:
bubnikv 2018-12-11 16:34:21 +01:00
commit 2b9319eea1
16 changed files with 206 additions and 102 deletions

View File

@ -1,10 +1,10 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <libslic3r.h> #include <libslic3r/libslic3r.h>
#include "TriangleMesh.hpp" #include <libslic3r/TriangleMesh.hpp>
#include "SLABasePool.hpp" #include <libslic3r/SLA/SLABasePool.hpp>
#include "benchmark.h" #include <libnest2d/tools/benchmark.h>
const std::string USAGE_STR = { const std::string USAGE_STR = {
"Usage: slabasebed stlfilename.stl" "Usage: slabasebed stlfilename.stl"
@ -28,7 +28,7 @@ int main(const int argc, const char *argv[]) {
ExPolygons ground_slice; ExPolygons ground_slice;
TriangleMesh basepool; TriangleMesh basepool;
sla::ground_layer(model, ground_slice, 0.1f); sla::base_plate(model, ground_slice, 0.1f);
bench.start(); bench.start();
sla::create_base_pool(ground_slice, basepool); sla::create_base_pool(ground_slice, basepool);

View File

@ -10,6 +10,10 @@
namespace Slic3r { namespace sla { namespace Slic3r { namespace sla {
namespace {
ThrowOnCancel throw_on_cancel = [](){};
}
/// Convert the triangulation output to an intermediate mesh. /// Convert the triangulation output to an intermediate mesh.
Contour3D convert(const Polygons& triangles, coord_t z, bool dir) { Contour3D convert(const Polygons& triangles, coord_t z, bool dir) {
@ -60,9 +64,13 @@ Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling,
}); });
}; };
auto& thr = throw_on_cancel;
std::for_each(tri.begin(), tri.end(), std::for_each(tri.begin(), tri.end(),
[&rp, &rpi, &poly, &idx, is_upper, fz, cz](const Polygon& pp) [&rp, &rpi, thr, &idx, is_upper, fz, cz](const Polygon& pp)
{ {
thr(); // may throw if cancellation was requested
for(auto& p : pp.points) for(auto& p : pp.points)
if(is_upper(p)) if(is_upper(p))
rp.emplace_back(unscale(x(p), y(p), mm(cz))); rp.emplace_back(unscale(x(p), y(p), mm(cz)));
@ -231,6 +239,8 @@ Contour3D round_edges(const ExPolygon& base_plate,
if(degrees >= 90) { if(degrees >= 90) {
for(int i = 1; i <= steps; ++i) { for(int i = 1; i <= steps; ++i) {
throw_on_cancel();
ob = base_plate; ob = base_plate;
double r2 = radius_mm * radius_mm; double r2 = radius_mm * radius_mm;
@ -254,6 +264,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
int tos = int(tox / stepx); int tos = int(tox / stepx);
for(int i = 1; i <= tos; ++i) { for(int i = 1; i <= tos; ++i) {
throw_on_cancel();
ob = base_plate; ob = base_plate;
double r2 = radius_mm * radius_mm; double r2 = radius_mm * radius_mm;
@ -385,7 +396,7 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50)
std::back_inserter(punion), std::back_inserter(punion),
[&punion, &boxindex, cc, max_dist_mm, &idx](const Point& c) [&punion, &boxindex, cc, max_dist_mm, &idx](const Point& c)
{ {
throw_on_cancel();
double dx = x(c) - x(cc), dy = y(c) - y(cc); double dx = x(c) - x(cc), dy = y(c) - y(cc);
double l = std::sqrt(dx * dx + dy * dy); double l = std::sqrt(dx * dx + dy * dy);
double nx = dx / l, ny = dy / l; double nx = dx / l, ny = dy / l;
@ -419,7 +430,7 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50)
} }
void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h, void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
float layerh) float layerh, ThrowOnCancel thrfn)
{ {
TriangleMesh m = mesh; TriangleMesh m = mesh;
TriangleMeshSlicer slicer(&m); TriangleMeshSlicer slicer(&m);
@ -431,7 +442,7 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
heights.emplace_back(hi); heights.emplace_back(hi);
std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh))); std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh)));
slicer.slice(heights, &out, [](){}); slicer.slice(heights, &out, thrfn);
size_t count = 0; for(auto& o : out) count += o.size(); size_t count = 0; for(auto& o : out) count += o.size();
ExPolygons tmp; tmp.reserve(count); ExPolygons tmp; tmp.reserve(count);
@ -444,6 +455,8 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
const PoolConfig& cfg) const PoolConfig& cfg)
{ {
throw_on_cancel = cfg.throw_on_cancel;
double mdist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm) + double mdist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm) +
cfg.max_merge_distance_mm; cfg.max_merge_distance_mm;

View File

@ -2,6 +2,7 @@
#define SLABASEPOOL_HPP #define SLABASEPOOL_HPP
#include <vector> #include <vector>
#include <functional>
namespace Slic3r { namespace Slic3r {
@ -11,12 +12,14 @@ class TriangleMesh;
namespace sla { namespace sla {
using ExPolygons = std::vector<ExPolygon>; using ExPolygons = std::vector<ExPolygon>;
using ThrowOnCancel = std::function<void(void)>;
/// Calculate the polygon representing the silhouette from the specified height /// Calculate the polygon representing the silhouette from the specified height
void base_plate(const TriangleMesh& mesh, void base_plate(const TriangleMesh& mesh, // input mesh
ExPolygons& output, ExPolygons& output, // Output will be merged with
float zlevel = 0.1f, float zlevel = 0.1f, // Plate creation level
float layerheight = 0.05f); float layerheight = 0.05f, // The sampling height
ThrowOnCancel thrfn = [](){}); // Will be called frequently
struct PoolConfig { struct PoolConfig {
double min_wall_thickness_mm = 2; double min_wall_thickness_mm = 2;
@ -24,6 +27,8 @@ struct PoolConfig {
double max_merge_distance_mm = 50; double max_merge_distance_mm = 50;
double edge_radius_mm = 1; double edge_radius_mm = 1;
ThrowOnCancel throw_on_cancel = [](){};
inline PoolConfig() {} inline PoolConfig() {}
inline PoolConfig(double wt, double wh, double md, double er): inline PoolConfig(double wt, double wh, double md, double er):
min_wall_thickness_mm(wt), min_wall_thickness_mm(wt),
@ -35,8 +40,7 @@ struct PoolConfig {
/// Calculate the pool for the mesh for SLA printing /// Calculate the pool for the mesh for SLA printing
void create_base_pool(const ExPolygons& base_plate, void create_base_pool(const ExPolygons& base_plate,
TriangleMesh& output_mesh, TriangleMesh& output_mesh,
const PoolConfig& = PoolConfig() const PoolConfig& = PoolConfig());
);
/// TODO: Currently the base plate of the pool will have half the height of the /// TODO: Currently the base plate of the pool will have half the height of the
/// whole pool. So the carved out space has also half the height. This is not /// whole pool. So the carved out space has also half the height. This is not

View File

@ -58,7 +58,7 @@ struct Contour3D {
points.insert(points.end(), ctr.points.begin(), ctr.points.end()); points.insert(points.end(), ctr.points.begin(), ctr.points.end());
indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end()); indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end());
for(auto n = s; n < indices.size(); n++) { for(size_t n = s; n < indices.size(); n++) {
auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3; auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3;
} }
} }

View File

@ -515,8 +515,13 @@ struct Pad {
zlevel(ground_level + sla::get_pad_elevation(pcfg)) zlevel(ground_level + sla::get_pad_elevation(pcfg))
{ {
ExPolygons basep; ExPolygons basep;
cfg.throw_on_cancel();
// The 0.1f is the layer height with which the mesh is sampled and then
// the layers are unified into one vector of polygons.
base_plate(object_support_mesh, basep, base_plate(object_support_mesh, basep,
float(cfg.min_wall_height_mm)/*,layer_height*/); float(cfg.min_wall_height_mm), 0.1f, pcfg.throw_on_cancel);
for(auto& bp : baseplate) basep.emplace_back(bp); for(auto& bp : baseplate) basep.emplace_back(bp);
create_base_pool(basep, tmesh, cfg); create_base_pool(basep, tmesh, cfg);
@ -622,12 +627,19 @@ class SLASupportTree::Impl {
std::vector<Junction> m_junctions; std::vector<Junction> m_junctions;
std::vector<Bridge> m_bridges; std::vector<Bridge> m_bridges;
std::vector<CompactBridge> m_compact_bridges; std::vector<CompactBridge> m_compact_bridges;
Controller m_ctl;
Pad m_pad; Pad m_pad;
mutable TriangleMesh meshcache; mutable bool meshcache_valid; mutable TriangleMesh meshcache; mutable bool meshcache_valid;
mutable double model_height = 0; // the full height of the model mutable double model_height = 0; // the full height of the model
public: public:
double ground_level = 0; double ground_level = 0;
Impl() = default;
inline Impl(const Controller& ctl): m_ctl(ctl) {}
const Controller& ctl() const { return m_ctl; }
template<class...Args> Head& add_head(Args&&... args) { template<class...Args> Head& add_head(Args&&... args) {
m_heads.emplace_back(std::forward<Args>(args)...); m_heads.emplace_back(std::forward<Args>(args)...);
m_heads.back().id = long(m_heads.size() - 1); m_heads.back().id = long(m_heads.size() - 1);
@ -710,27 +722,38 @@ public:
meshcache = TriangleMesh(); meshcache = TriangleMesh();
for(auto& head : heads()) { for(auto& head : heads()) {
if(m_ctl.stopcondition()) break;
auto&& m = mesh(head.mesh); auto&& m = mesh(head.mesh);
meshcache.merge(m); meshcache.merge(m);
} }
for(auto& stick : pillars()) { for(auto& stick : pillars()) {
if(m_ctl.stopcondition()) break;
meshcache.merge(mesh(stick.mesh)); meshcache.merge(mesh(stick.mesh));
meshcache.merge(mesh(stick.base)); meshcache.merge(mesh(stick.base));
} }
for(auto& j : junctions()) { for(auto& j : junctions()) {
if(m_ctl.stopcondition()) break;
meshcache.merge(mesh(j.mesh)); meshcache.merge(mesh(j.mesh));
} }
for(auto& cb : compact_bridges()) { for(auto& cb : compact_bridges()) {
if(m_ctl.stopcondition()) break;
meshcache.merge(mesh(cb.mesh)); meshcache.merge(mesh(cb.mesh));
} }
for(auto& bs : bridges()) { for(auto& bs : bridges()) {
if(m_ctl.stopcondition()) break;
meshcache.merge(mesh(bs.mesh)); meshcache.merge(mesh(bs.mesh));
} }
if(m_ctl.stopcondition()) {
// In case of failure we have to return an empty mesh
meshcache = TriangleMesh();
return meshcache;
}
// TODO: Is this necessary? // TODO: Is this necessary?
meshcache.repair(); meshcache.repair();
@ -1659,22 +1682,19 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
fullmesh.merge(get_pad()); fullmesh.merge(get_pad());
TriangleMeshSlicer slicer(&fullmesh); TriangleMeshSlicer slicer(&fullmesh);
SlicedSupports ret; SlicedSupports ret;
slicer.slice(heights, &ret, m_ctl.cancelfn); slicer.slice(heights, &ret, get().ctl().cancelfn);
return ret; return ret;
} }
const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate, const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate,
double min_wall_thickness_mm, const PoolConfig& pcfg) const
double min_wall_height_mm,
double max_merge_distance_mm,
double edge_radius_mm) const
{ {
PoolConfig pcfg; // PoolConfig pcfg;
pcfg.min_wall_thickness_mm = min_wall_thickness_mm; // pcfg.min_wall_thickness_mm = min_wall_thickness_mm;
pcfg.min_wall_height_mm = min_wall_height_mm; // pcfg.min_wall_height_mm = min_wall_height_mm;
pcfg.max_merge_distance_mm = max_merge_distance_mm; // pcfg.max_merge_distance_mm = max_merge_distance_mm;
pcfg.edge_radius_mm = edge_radius_mm; // pcfg.edge_radius_mm = edge_radius_mm;
return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh; return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh;
} }
@ -1687,14 +1707,14 @@ SLASupportTree::SLASupportTree(const PointSet &points,
const EigenMesh3D& emesh, const EigenMesh3D& emesh,
const SupportConfig &cfg, const SupportConfig &cfg,
const Controller &ctl): const Controller &ctl):
m_impl(new Impl()), m_ctl(ctl) m_impl(new Impl(ctl))
{ {
m_impl->ground_level = emesh.ground_level - cfg.object_elevation_mm; m_impl->ground_level = emesh.ground_level - cfg.object_elevation_mm;
generate(points, emesh, cfg, ctl); generate(points, emesh, cfg, ctl);
} }
SLASupportTree::SLASupportTree(const SLASupportTree &c): SLASupportTree::SLASupportTree(const SLASupportTree &c):
m_impl(new Impl(*c.m_impl)), m_ctl(c.m_ctl) {} m_impl(new Impl(*c.m_impl)) {}
SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c) SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c)
{ {

View File

@ -69,6 +69,8 @@ struct SupportConfig {
double object_elevation_mm = 10; double object_elevation_mm = 10;
}; };
struct PoolConfig;
/// A Control structure for the support calculation. Consists of the status /// A Control structure for the support calculation. Consists of the status
/// indicator callback and the stop condition predicate. /// indicator callback and the stop condition predicate.
struct Controller { struct Controller {
@ -119,7 +121,6 @@ public:
class SLASupportTree { class SLASupportTree {
class Impl; class Impl;
std::unique_ptr<Impl> m_impl; std::unique_ptr<Impl> m_impl;
Controller m_ctl;
Impl& get() { return *m_impl; } Impl& get() { return *m_impl; }
const Impl& get() const { return *m_impl; } const Impl& get() const { return *m_impl; }
@ -158,10 +159,7 @@ public:
/// Adding the "pad" (base pool) under the supports /// Adding the "pad" (base pool) under the supports
const TriangleMesh& add_pad(const SliceLayer& baseplate, const TriangleMesh& add_pad(const SliceLayer& baseplate,
double min_wall_thickness_mm, const PoolConfig& pcfg) const;
double min_wall_height_mm,
double max_merge_distance_mm,
double edge_radius_mm) const;
/// Get the pad geometry /// Get the pad geometry
const TriangleMesh& get_pad() const; const TriangleMesh& get_pad() const;

View File

@ -445,7 +445,7 @@ void SLAPrint::process()
// Slicing the model object. This method is oversimplified and needs to // Slicing the model object. This method is oversimplified and needs to
// be compared with the fff slicing algorithm for verification // be compared with the fff slicing algorithm for verification
auto slice_model = [this, ilh, ilhd](SLAPrintObject& po) { auto slice_model = [this, ilh](SLAPrintObject& po) {
double lh = po.m_config.layer_height.getFloat(); double lh = po.m_config.layer_height.getFloat();
TriangleMesh mesh = po.transformed_mesh(); TriangleMesh mesh = po.transformed_mesh();
@ -530,6 +530,11 @@ void SLAPrint::process()
po.m_supportdata->support_tree_ptr.reset( po.m_supportdata->support_tree_ptr.reset(
new SLASupportTree(pts, emesh, scfg, ctl)); new SLASupportTree(pts, emesh, scfg, ctl));
// Create the unified mesh
auto rc = SlicingStatus::RELOAD_SCENE;
set_status(-1, L("Visualizing supports"));
po.m_supportdata->support_tree_ptr->merged_mesh();
set_status(-1, L("Visualizing supports"), rc);
} catch(sla::SLASupportsStoppedException&) { } catch(sla::SLASupportsStoppedException&) {
// no need to rethrow // no need to rethrow
// throw_if_canceled(); // throw_if_canceled();
@ -560,18 +565,23 @@ void SLAPrint::process()
auto&& trmesh = po.transformed_mesh(); auto&& trmesh = po.transformed_mesh();
// This call can get pretty time consuming // This call can get pretty time consuming
if(elevation < pad_h) sla::base_plate(trmesh, bp, auto thrfn = [this](){ throw_if_canceled(); };
float(pad_h), float(lh));
po.m_supportdata->support_tree_ptr->add_pad(bp, wt, h, md, er); if(elevation < pad_h)
sla::base_plate(trmesh, bp, float(pad_h), float(lh),
thrfn);
pcfg.throw_on_cancel = thrfn;
po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg);
} }
// if the base pool (which means also the support tree) is // // if the base pool (which means also the support tree) is
// done, do a refresh when indicating progress. Now the // // done, do a refresh when indicating progress. Now the
// geometries for the supports and the optional base pad are // // geometries for the supports and the optional base pad are
// ready. We can grant access for the control thread to read // // ready. We can grant access for the control thread to read
// the geometries, but first we have to update the caches: // // the geometries, but first we have to update the caches:
po.support_mesh(); /*po->pad_mesh();*/ // po.support_mesh(); /*po->pad_mesh();*/
po.throw_if_canceled();
auto rc = SlicingStatus::RELOAD_SCENE; auto rc = SlicingStatus::RELOAD_SCENE;
set_status(-1, L("Visualizing supports"), rc); set_status(-1, L("Visualizing supports"), rc);
}; };
@ -589,9 +599,9 @@ void SLAPrint::process()
// We have the layer polygon collection but we need to unite them into // We have the layer polygon collection but we need to unite them into
// an index where the key is the height level in discrete levels (clipper) // an index where the key is the height level in discrete levels (clipper)
auto index_slices = [this, ilh, ilhd](SLAPrintObject& po) { auto index_slices = [ilhd](SLAPrintObject& po) {
po.m_slice_index.clear(); po.m_slice_index.clear();
auto sih = LevelID(scale_(ilh)); auto sih = LevelID(scale_(ilhd));
// For all print objects, go through its initial layers and place them // For all print objects, go through its initial layers and place them
// into the layers hash // into the layers hash
@ -635,7 +645,7 @@ void SLAPrint::process()
// shortcut for empty index into the slice vectors // shortcut for empty index into the slice vectors
static const auto EMPTY_SLICE = SLAPrintObject::SliceRecord::NONE; static const auto EMPTY_SLICE = SLAPrintObject::SliceRecord::NONE;
for(int i = 0; i < oslices.size(); ++i) { for(size_t i = 0; i < oslices.size(); ++i) {
LevelID h = levelids[i]; LevelID h = levelids[i];
float fh = float(double(h) * SCALING_FACTOR); float fh = float(double(h) * SCALING_FACTOR);
@ -652,7 +662,7 @@ void SLAPrint::process()
po.m_supportdata->level_ids.clear(); po.m_supportdata->level_ids.clear();
po.m_supportdata->level_ids.reserve(sslices.size()); po.m_supportdata->level_ids.reserve(sslices.size());
for(int i = 0; i < sslices.size(); ++i) { for(int i = 0; i < int(sslices.size()); ++i) {
int a = i == 0 ? 0 : 1; int a = i == 0 ? 0 : 1;
int b = i == 0 ? 0 : i - 1; int b = i == 0 ? 0 : i - 1;
LevelID h = sminZ + a * sih + b * slh; LevelID h = sminZ + a * sih + b * slh;
@ -662,7 +672,7 @@ void SLAPrint::process()
SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh]; SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh];
assert(sr.support_slices_idx == EMPTY_SLICE); assert(sr.support_slices_idx == EMPTY_SLICE);
sr.support_slices_idx = i; sr.support_slices_idx = SLAPrintObject::SliceRecord::Idx(i);
} }
} }
}; };
@ -670,7 +680,7 @@ void SLAPrint::process()
auto& levels = m_printer_input; auto& levels = m_printer_input;
// Rasterizing the model objects, and their supports // Rasterizing the model objects, and their supports
auto rasterize = [this, ilh, ilhd, max_objstatus, &levels]() { auto rasterize = [this, max_objstatus, &levels]() {
if(canceled()) return; if(canceled()) return;
// clear the rasterizer input // clear the rasterizer input
@ -688,14 +698,14 @@ void SLAPrint::process()
// now merge this object's support and object slices with the rest // now merge this object's support and object slices with the rest
// of the print object slices // of the print object slices
for(int i = 0; i < oslices.size(); ++i) { for(size_t i = 0; i < oslices.size(); ++i) {
auto& lyrs = levels[gndlvl + po.m_level_ids[i]]; auto& lyrs = levels[gndlvl + po.m_level_ids[i]];
lyrs.emplace_back(oslices[i], po.m_instances); lyrs.emplace_back(oslices[i], po.m_instances);
} }
if(!po.m_supportdata) continue; if(!po.m_supportdata) continue;
auto& sslices = po.m_supportdata->support_slices; auto& sslices = po.m_supportdata->support_slices;
for(int i = 0; i < sslices.size(); ++i) { for(size_t i = 0; i < sslices.size(); ++i) {
auto& lyrs = levels[gndlvl + po.m_supportdata->level_ids[i]]; auto& lyrs = levels[gndlvl + po.m_supportdata->level_ids[i]];
lyrs.emplace_back(sslices[i], po.m_instances); lyrs.emplace_back(sslices[i], po.m_instances);
} }
@ -713,8 +723,8 @@ void SLAPrint::process()
double w = printcfg.display_width.getFloat(); double w = printcfg.display_width.getFloat();
double h = printcfg.display_height.getFloat(); double h = printcfg.display_height.getFloat();
unsigned pw = printcfg.display_pixels_x.getInt(); auto pw = unsigned(printcfg.display_pixels_x.getInt());
unsigned ph = printcfg.display_pixels_y.getInt(); auto ph = unsigned(printcfg.display_pixels_y.getInt());
double lh = ocfg.layer_height.getFloat(); double lh = ocfg.layer_height.getFloat();
double exp_t = matcfg.exposure_time.getFloat(); double exp_t = matcfg.exposure_time.getFloat();
double iexp_t = matcfg.initial_exposure_time.getFloat(); double iexp_t = matcfg.initial_exposure_time.getFloat();
@ -1098,7 +1108,9 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const
const TriangleMesh& SLAPrintObject::support_mesh() const const TriangleMesh& SLAPrintObject::support_mesh() const
{ {
if(m_config.supports_enable.getBool() && m_supportdata && if(m_config.supports_enable.getBool() && m_supportdata &&
m_supportdata->support_tree_ptr) return m_supportdata->support_tree_ptr->merged_mesh(); m_supportdata->support_tree_ptr) {
return m_supportdata->support_tree_ptr->merged_mesh();
}
return EMPTY_MESH; return EMPTY_MESH;
} }

View File

@ -74,7 +74,16 @@ void Field::on_kill_focus(wxEvent& event)
event.Skip(); event.Skip();
// call the registered function if it is available // call the registered function if it is available
if (m_on_kill_focus!=nullptr) if (m_on_kill_focus!=nullptr)
m_on_kill_focus(); m_on_kill_focus(m_opt_id);
}
void Field::on_set_focus(wxEvent& event)
{
// to allow the default behavior
event.Skip();
// call the registered function if it is available
if (m_on_set_focus!=nullptr)
m_on_set_focus(m_opt_id);
} }
void Field::on_change_field() void Field::on_change_field()
@ -125,9 +134,9 @@ void Field::get_value_by_opt_type(wxString& str)
case coPercents: case coPercents:
case coFloats: case coFloats:
case coFloat:{ case coFloat:{
if (m_opt.type == coPercent && str.Last() == '%') if (m_opt.type == coPercent && !str.IsEmpty() && str.Last() == '%')
str.RemoveLast(); str.RemoveLast();
else if (str.Last() == '%') { else if (!str.IsEmpty() && str.Last() == '%') {
wxString label = m_Label->GetLabel(); wxString label = m_Label->GetLabel();
if (label.Last() == '\n') label.RemoveLast(); if (label.Last() == '\n') label.RemoveLast();
while (label.Last() == ' ') label.RemoveLast(); while (label.Last() == ' ') label.RemoveLast();
@ -162,7 +171,7 @@ void Field::get_value_by_opt_type(wxString& str)
} }
} }
bool TextCtrl::is_defined_input_value() bool TextCtrl::is_defined_input_value() const
{ {
if (static_cast<wxTextCtrl*>(window)->GetValue().empty() && m_opt.type != coString && m_opt.type != coStrings) if (static_cast<wxTextCtrl*>(window)->GetValue().empty() && m_opt.type != coString && m_opt.type != coStrings)
return false; return false;
@ -216,11 +225,13 @@ void TextCtrl::BUILD() {
break; break;
} }
const long style = m_opt.multiline ? wxTE_MULTILINE : 0 | m_process_enter ? wxTE_PROCESS_ENTER : 0; const long style = m_opt.multiline ? wxTE_MULTILINE : 0;
auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style); auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style);
temp->SetToolTip(get_tooltip_text(text_value)); temp->SetToolTip(get_tooltip_text(text_value));
temp->Bind(wxEVT_SET_FOCUS, ([this](wxEvent& e) { on_set_focus(e); }), temp->GetId());
temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event) temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event)
{ {
//! to allow the default handling //! to allow the default handling
@ -240,17 +251,13 @@ void TextCtrl::BUILD() {
e.Skip(); e.Skip();
temp->GetToolTip()->Enable(true); temp->GetToolTip()->Enable(true);
#endif // __WXGTK__ #endif // __WXGTK__
if (!is_defined_input_value()) // if (!is_defined_input_value())
if (is_defined_input_value())
on_change_field();
else
on_kill_focus(e); on_kill_focus(e);
}), temp->GetId()); }), temp->GetId());
/*
if (m_process_enter) {
temp->Bind(wxEVT_TEXT_ENTER, ([this](wxCommandEvent& evt) {
if(is_defined_input_value())
on_change_field();
}), temp->GetId());
}
else {
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt) temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt)
{ {
#ifdef __WXGTK__ #ifdef __WXGTK__
@ -267,8 +274,7 @@ void TextCtrl::BUILD() {
temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this); temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this);
temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this); temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this);
#endif //__WXGTK__ #endif //__WXGTK__
} */
// select all text using Ctrl+A // select all text using Ctrl+A
temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event) temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event)
{ {
@ -371,7 +377,15 @@ void SpinCtrl::BUILD() {
0, min_val, max_val, default_value); 0, min_val, max_val, default_value);
// temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { tmp_value = undef_spin_val; on_change_field(); }), temp->GetId()); // temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { tmp_value = undef_spin_val; on_change_field(); }), temp->GetId());
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { on_kill_focus(e); }), temp->GetId()); temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e)
{
if (tmp_value < 0)
on_kill_focus(e);
else {
e.Skip();
on_change_field();
}
}), temp->GetId());
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e)
{ {
// # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value // # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value
@ -382,7 +396,8 @@ void SpinCtrl::BUILD() {
std::string value = e.GetString().utf8_str().data(); std::string value = e.GetString().utf8_str().data();
if (is_matched(value, "^\\d+$")) if (is_matched(value, "^\\d+$"))
tmp_value = std::stoi(value); tmp_value = std::stoi(value);
on_change_field(); else tmp_value = -9999;
// on_change_field();
// # We don't reset tmp_value here because _on_change might put callbacks // # We don't reset tmp_value here because _on_change might put callbacks
// # in the CallAfter queue, and we want the tmp value to be available from // # in the CallAfter queue, and we want the tmp value to be available from
// # them as well. // # them as well.

View File

@ -29,8 +29,8 @@ namespace Slic3r { namespace GUI {
class Field; class Field;
using t_field = std::unique_ptr<Field>; using t_field = std::unique_ptr<Field>;
using t_kill_focus = std::function<void()>; using t_kill_focus = std::function<void(const std::string&)>;
using t_change = std::function<void(t_config_option_key, const boost::any&)>; using t_change = std::function<void(const t_config_option_key&, const boost::any&)>;
using t_back_to_init = std::function<void(const std::string&)>; using t_back_to_init = std::function<void(const std::string&)>;
wxString double_to_string(double const value, const int max_precision = 4); wxString double_to_string(double const value, const int max_precision = 4);
@ -76,6 +76,8 @@ protected:
//! in another case we can't unfocused control at all //! in another case we can't unfocused control at all
void on_kill_focus(wxEvent& event); void on_kill_focus(wxEvent& event);
/// Call the attached on_change method. /// Call the attached on_change method.
void on_set_focus(wxEvent& event);
/// Call the attached on_change method.
void on_change_field(); void on_change_field();
/// Call the attached m_back_to_initial_value method. /// Call the attached m_back_to_initial_value method.
void on_back_to_initial_value(); void on_back_to_initial_value();
@ -89,6 +91,9 @@ public:
/// Function object to store callback passed in from owning object. /// Function object to store callback passed in from owning object.
t_kill_focus m_on_kill_focus {nullptr}; t_kill_focus m_on_kill_focus {nullptr};
/// Function object to store callback passed in from owning object.
t_kill_focus m_on_set_focus {nullptr};
/// Function object to store callback passed in from owning object. /// Function object to store callback passed in from owning object.
t_change m_on_change {nullptr}; t_change m_on_change {nullptr};
@ -139,10 +144,9 @@ public:
/// Factory method for generating new derived classes. /// Factory method for generating new derived classes.
template<class T> template<class T>
static t_field Create(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id, const bool process_enter = false)// interface for creating shared objects static t_field Create(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id)// interface for creating shared objects
{ {
auto p = Slic3r::make_unique<T>(parent, opt, id); auto p = Slic3r::make_unique<T>(parent, opt, id);
p->m_process_enter = process_enter;
p->PostInitialize(); p->PostInitialize();
return std::move(p); //!p; return std::move(p); //!p;
} }
@ -223,9 +227,6 @@ protected:
// current value // current value
boost::any m_value; boost::any m_value;
//this variable shows a mode of a call of the on_change function
bool m_process_enter { false };
friend class OptionsGroup; friend class OptionsGroup;
}; };
@ -265,7 +266,7 @@ public:
} }
boost::any& get_value() override; boost::any& get_value() override;
bool is_defined_input_value(); bool is_defined_input_value() const ;
virtual void enable(); virtual void enable();
virtual void disable(); virtual void disable();

View File

@ -971,6 +971,8 @@ public:
void viewport_changed(); void viewport_changed();
#endif // ENABLE_CONSTRAINED_CAMERA_TARGET #endif // ENABLE_CONSTRAINED_CAMERA_TARGET
void handle_sidebar_focus_event(const std::string& opt_key) {}
private: private:
bool _is_shown_on_screen() const; bool _is_shown_on_screen() const;
void _force_zoom_to_bed(); void _force_zoom_to_bed();

View File

@ -453,7 +453,6 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event)
obj->SetText(wxString::Format("%d", m_objects_model->GetVolumeIdByItem(item))); obj->SetText(wxString::Format("%d", m_objects_model->GetVolumeIdByItem(item)));
event.SetDataObject(obj); event.SetDataObject(obj);
event.SetDragFlags(/*wxDrag_AllowMove*/wxDrag_DefaultMove); // allows both copy and move; event.SetDragFlags(/*wxDrag_AllowMove*/wxDrag_DefaultMove); // allows both copy and move;
printf("BeginDrag\n");
} }
void ObjectList::OnDropPossible(wxDataViewEvent &event) void ObjectList::OnDropPossible(wxDataViewEvent &event)
@ -464,7 +463,6 @@ void ObjectList::OnDropPossible(wxDataViewEvent &event)
if (event.GetDataFormat() != wxDF_UNICODETEXT || item.IsOk() && if (event.GetDataFormat() != wxDF_UNICODETEXT || item.IsOk() &&
(m_objects_model->GetParent(item) == wxDataViewItem(0) || m_objects_model->GetItemType(item) != itVolume)) (m_objects_model->GetParent(item) == wxDataViewItem(0) || m_objects_model->GetItemType(item) != itVolume))
event.Veto(); event.Veto();
printf("DropPossible\n");
} }
void ObjectList::OnDrop(wxDataViewEvent &event) void ObjectList::OnDrop(wxDataViewEvent &event)
@ -477,13 +475,14 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
event.Veto(); event.Veto();
return; return;
} }
printf("Drop\n");
wxTextDataObject obj; wxTextDataObject obj;
obj.SetData(wxDF_UNICODETEXT, event.GetDataSize(), event.GetDataBuffer()); obj.SetData(wxDF_UNICODETEXT, event.GetDataSize(), event.GetDataBuffer());
printf("Drop\n");
int from_volume_id = std::stoi(obj.GetText().ToStdString()); int from_volume_id = std::stoi(obj.GetText().ToStdString());
int to_volume_id = m_objects_model->GetVolumeIdByItem(item); int to_volume_id = m_objects_model->GetVolumeIdByItem(item);
printf("from %d to %d\n", from_volume_id, to_volume_id);
#ifdef __WXGTK__ #ifdef __WXGTK__
/* Under GTK, DnD moves an item between another two items. /* Under GTK, DnD moves an item between another two items.
@ -498,10 +497,12 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
int cnt = 0; int cnt = 0;
for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++) for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++)
std::swap(volumes[id], volumes[id + delta]); std::swap(volumes[id], volumes[id + delta]);
printf("Volumes are swapped\n");
select_item(m_objects_model->ReorganizeChildren(from_volume_id, to_volume_id, select_item(m_objects_model->ReorganizeChildren(from_volume_id, to_volume_id,
m_objects_model->GetParent(item))); m_objects_model->GetParent(item)));
printf("ItemChildren are Reorganized\n");
m_parts_changed = true; m_parts_changed = true;
parts_changed(m_selected_object_id); parts_changed(m_selected_object_id);

View File

@ -21,9 +21,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
m_og->set_name(_(L("Object Manipulation"))); m_og->set_name(_(L("Object Manipulation")));
m_og->label_width = 100; m_og->label_width = 100;
m_og->set_grid_vgap(5); m_og->set_grid_vgap(5);
m_og->set_process_enter(); // We need to update new values only after press ENTER
m_og->m_on_change = [this](t_config_option_key opt_key, boost::any value) { m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) {
std::vector<std::string> axes{ "_x", "_y", "_z" }; std::vector<std::string> axes{ "_x", "_y", "_z" };
if (opt_key == "scale_unit") { if (opt_key == "scale_unit") {
@ -54,6 +53,29 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
change_scale_value(new_value); change_scale_value(new_value);
}; };
m_og->m_fill_empty_value = [this](const std::string& opt_key)
{
if (opt_key == "scale_unit")
return;
std::string param;
std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param));
if (param == "position") {
int axis = opt_key.back() == 'x' ? 0 :
opt_key.back() == 'y' ? 1 : 2;
m_og->set_value(opt_key, double_to_string(cache_position(axis)));
return;
}
m_og->set_value(opt_key, double_to_string(0.0));
};
m_og->m_set_focus = [this](const std::string& opt_key)
{
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key);
};
ConfigOptionDef def; ConfigOptionDef def;
// Objects(sub-objects) name // Objects(sub-objects) name

View File

@ -17,7 +17,7 @@ protected:
wxWindow* m_parent; wxWindow* m_parent;
public: public:
OG_Settings(wxWindow* parent, const bool staticbox); OG_Settings(wxWindow* parent, const bool staticbox);
~OG_Settings() {} virtual ~OG_Settings() {}
virtual bool IsShown(); virtual bool IsShown();
virtual void Show(const bool show); virtual void Show(const bool show);

View File

@ -44,7 +44,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
case coPercents: case coPercents:
case coString: case coString:
case coStrings: case coStrings:
m_fields.emplace(id, std::move(TextCtrl::Create<TextCtrl>(parent(), opt, id, process_enter))); m_fields.emplace(id, std::move(TextCtrl::Create<TextCtrl>(parent(), opt, id)));
break; break;
case coBool: case coBool:
case coBools: case coBools:
@ -67,16 +67,21 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
} }
// Grab a reference to fields for convenience // Grab a reference to fields for convenience
const t_field& field = m_fields[id]; const t_field& field = m_fields[id];
field->m_on_change = [this](std::string opt_id, boost::any value) { field->m_on_change = [this](const std::string& opt_id, const boost::any& value) {
//! This function will be called from Field. //! This function will be called from Field.
//! Call OptionGroup._on_change(...) //! Call OptionGroup._on_change(...)
if (!m_disabled) if (!m_disabled)
this->on_change_OG(opt_id, value); this->on_change_OG(opt_id, value);
}; };
field->m_on_kill_focus = [this]() { field->m_on_kill_focus = [this](const std::string& opt_id) {
//! This function will be called from Field. //! This function will be called from Field.
if (!m_disabled) if (!m_disabled)
this->on_kill_focus(); this->on_kill_focus(opt_id);
};
field->m_on_set_focus = [this](const std::string& opt_id) {
//! This function will be called from Field.
if (!m_disabled)
this->on_set_focus(opt_id);
}; };
field->m_parent = parent(); field->m_parent = parent();
@ -277,6 +282,12 @@ Line OptionsGroup::create_single_option_line(const Option& option) const {
return retval; return retval;
} }
void OptionsGroup::on_set_focus(const std::string& opt_key)
{
if (m_set_focus != nullptr)
m_set_focus(opt_key);
}
void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value) { void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value) {
if (m_on_change != nullptr) if (m_on_change != nullptr)
m_on_change(opt_id, value); m_on_change(opt_id, value);
@ -378,6 +389,15 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config,
on_change_OG(opt_key, get_value(opt_key)); on_change_OG(opt_key, get_value(opt_key));
} }
void ConfigOptionsGroup::on_kill_focus(const std::string& opt_key)
{
if (m_fill_empty_value) {
m_fill_empty_value(opt_key);
return;
}
reload_config();
}
void ConfigOptionsGroup::reload_config() { void ConfigOptionsGroup::reload_config() {
for (t_opt_map::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) { for (t_opt_map::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) {
auto opt_id = it->first; auto opt_id = it->first;

View File

@ -85,7 +85,9 @@ public:
size_t label_width {200}; size_t label_width {200};
wxSizer* sizer {nullptr}; wxSizer* sizer {nullptr};
column_t extra_column {nullptr}; column_t extra_column {nullptr};
t_change m_on_change {nullptr}; t_change m_on_change { nullptr };
t_kill_focus m_fill_empty_value { nullptr };
t_kill_focus m_set_focus { nullptr };
std::function<DynamicPrintConfig()> m_get_initial_config{ nullptr }; std::function<DynamicPrintConfig()> m_get_initial_config{ nullptr };
std::function<DynamicPrintConfig()> m_get_sys_config{ nullptr }; std::function<DynamicPrintConfig()> m_get_sys_config{ nullptr };
std::function<bool()> have_sys_config{ nullptr }; std::function<bool()> have_sys_config{ nullptr };
@ -94,8 +96,6 @@ public:
wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
int sidetext_width{ -1 }; int sidetext_width{ -1 };
bool process_enter { false };
/// Returns a copy of the pointer of the parent wxWindow. /// Returns a copy of the pointer of the parent wxWindow.
/// Accessor function is because users are not allowed to change the parent /// Accessor function is because users are not allowed to change the parent
/// but defining it as const means a lot of const_casts to deal with wx functions. /// but defining it as const means a lot of const_casts to deal with wx functions.
@ -154,11 +154,6 @@ public:
m_show_modified_btns = show; m_show_modified_btns = show;
} }
// The controls inside this option group will generate the event wxEVT_TEXT_ENTER
void set_process_enter() {
process_enter = true;
}
OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false, OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false,
column_t extra_clmn = nullptr) : column_t extra_clmn = nullptr) :
m_parent(_parent), title(title), m_parent(_parent), title(title),
@ -215,7 +210,8 @@ protected:
const t_field& build_field(const Option& opt, wxStaticText* label = nullptr); const t_field& build_field(const Option& opt, wxStaticText* label = nullptr);
void add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field); void add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field);
virtual void on_kill_focus () {}; virtual void on_kill_focus(const std::string& opt_key) {};
virtual void on_set_focus(const std::string& opt_key);
virtual void on_change_OG(const t_config_option_key& opt_id, const boost::any& value); virtual void on_change_OG(const t_config_option_key& opt_id, const boost::any& value);
virtual void back_to_initial_value(const std::string& opt_key) {} virtual void back_to_initial_value(const std::string& opt_key) {}
virtual void back_to_sys_value(const std::string& opt_key) {} virtual void back_to_sys_value(const std::string& opt_key) {}
@ -251,7 +247,7 @@ public:
void back_to_initial_value(const std::string& opt_key) override; void back_to_initial_value(const std::string& opt_key) override;
void back_to_sys_value(const std::string& opt_key) override; void back_to_sys_value(const std::string& opt_key) override;
void back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key); void back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key);
void on_kill_focus() override{ reload_config();} void on_kill_focus(const std::string& opt_key) override;// { reload_config(); }
void reload_config(); void reload_config();
// return value shows visibility : false => all options are hidden // return value shows visibility : false => all options are hidden
void Hide(); void Hide();

View File

@ -270,7 +270,7 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str
auto panel = this; auto panel = this;
#endif #endif
PageShp page(new Page(panel, title, icon_idx)); PageShp page(new Page(panel, title, icon_idx));
page->SetScrollbars(1, 1, 1, 1); page->SetScrollbars(1, 1, 1, 2);
page->Hide(); page->Hide();
m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5); m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5);