Merge branch 'master' of https://github.com/Prusa3d/Slic3r
This commit is contained in:
commit
4a1f0eb05f
13 changed files with 287 additions and 140 deletions
|
@ -1194,7 +1194,7 @@ Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>&
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
angles(1) = 0.0;
|
angles(0) = 0.0;
|
||||||
angles(1) = ::atan2(-rotation_matrix(2, 0), sy);
|
angles(1) = ::atan2(-rotation_matrix(2, 0), sy);
|
||||||
angles(2) = (angles(1) >-0.0) ? ::atan2(rotation_matrix(1, 2), rotation_matrix(1, 1)) : ::atan2(-rotation_matrix(1, 2), rotation_matrix(1, 1));
|
angles(2) = (angles(1) >-0.0) ? ::atan2(rotation_matrix(1, 2), rotation_matrix(1, 1)) : ::atan2(-rotation_matrix(1, 2), rotation_matrix(1, 1));
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,41 +215,67 @@ inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) {
|
||||||
return lower;
|
return lower;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ExP, class D>
|
|
||||||
Contour3D round_edges(const ExPolygon& base_plate,
|
Contour3D round_edges(const ExPolygon& base_plate,
|
||||||
double radius_mm,
|
double radius_mm,
|
||||||
double degrees,
|
double degrees,
|
||||||
double ceilheight_mm,
|
double ceilheight_mm,
|
||||||
bool dir,
|
bool dir,
|
||||||
ThrowOnCancel throw_on_cancel,
|
ThrowOnCancel throw_on_cancel,
|
||||||
ExP&& last_offset = ExP(), D&& last_height = D())
|
ExPolygon& last_offset, double& last_height)
|
||||||
{
|
{
|
||||||
auto ob = base_plate;
|
auto ob = base_plate;
|
||||||
auto ob_prev = ob;
|
auto ob_prev = ob;
|
||||||
double wh = ceilheight_mm, wh_prev = wh;
|
double wh = ceilheight_mm, wh_prev = wh;
|
||||||
Contour3D curvedwalls;
|
Contour3D curvedwalls;
|
||||||
|
|
||||||
int steps = 15; // int(std::ceil(10*std::pow(radius_mm, 1.0/3)));
|
int steps = 30;
|
||||||
double stepx = radius_mm / steps;
|
double stepx = radius_mm / steps;
|
||||||
coord_t s = dir? 1 : -1;
|
coord_t s = dir? 1 : -1;
|
||||||
degrees = std::fmod(degrees, 180);
|
degrees = std::fmod(degrees, 180);
|
||||||
|
|
||||||
if(degrees >= 90) {
|
// we use sin for x distance because we interpret the angle starting from
|
||||||
for(int i = 1; i <= steps; ++i) {
|
// PI/2
|
||||||
throw_on_cancel();
|
int tos = degrees < 90?
|
||||||
|
int(radius_mm*std::cos(degrees * PI / 180 - PI/2) / stepx) : steps;
|
||||||
|
|
||||||
|
for(int i = 1; i <= tos; ++i) {
|
||||||
|
throw_on_cancel();
|
||||||
|
|
||||||
|
ob = base_plate;
|
||||||
|
|
||||||
|
double r2 = radius_mm * radius_mm;
|
||||||
|
double xx = i*stepx;
|
||||||
|
double x2 = xx*xx;
|
||||||
|
double stepy = std::sqrt(r2 - x2);
|
||||||
|
|
||||||
|
offset(ob, s*mm(xx));
|
||||||
|
wh = ceilheight_mm - radius_mm + stepy;
|
||||||
|
|
||||||
|
Contour3D pwalls;
|
||||||
|
pwalls = walls(ob, ob_prev, wh, wh_prev, throw_on_cancel);
|
||||||
|
|
||||||
|
curvedwalls.merge(pwalls);
|
||||||
|
ob_prev = ob;
|
||||||
|
wh_prev = wh;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(degrees > 90) {
|
||||||
|
double tox = radius_mm - radius_mm*std::cos(degrees * PI / 180 - PI/2);
|
||||||
|
int tos = int(tox / stepx);
|
||||||
|
|
||||||
|
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;
|
||||||
double xx = i*stepx;
|
double xx = radius_mm - i*stepx;
|
||||||
double x2 = xx*xx;
|
double x2 = xx*xx;
|
||||||
double stepy = std::sqrt(r2 - x2);
|
double stepy = std::sqrt(r2 - x2);
|
||||||
|
|
||||||
offset(ob, s*mm(xx));
|
offset(ob, s*mm(xx));
|
||||||
wh = ceilheight_mm - radius_mm + stepy;
|
wh = ceilheight_mm - radius_mm - stepy;
|
||||||
|
|
||||||
Contour3D pwalls;
|
Contour3D pwalls;
|
||||||
pwalls = walls(ob, ob_prev, wh, wh_prev, throw_on_cancel);
|
pwalls = walls(ob_prev, ob, wh_prev, wh, throw_on_cancel);
|
||||||
|
|
||||||
curvedwalls.merge(pwalls);
|
curvedwalls.merge(pwalls);
|
||||||
ob_prev = ob;
|
ob_prev = ob;
|
||||||
|
@ -257,28 +283,6 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double tox = radius_mm - radius_mm*std::sin(degrees * PI / 180);
|
|
||||||
int tos = int(tox / stepx);
|
|
||||||
|
|
||||||
for(int i = 1; i <= tos; ++i) {
|
|
||||||
throw_on_cancel();
|
|
||||||
ob = base_plate;
|
|
||||||
|
|
||||||
double r2 = radius_mm * radius_mm;
|
|
||||||
double xx = radius_mm - i*stepx;
|
|
||||||
double x2 = xx*xx;
|
|
||||||
double stepy = std::sqrt(r2 - x2);
|
|
||||||
offset(ob, s*mm(xx));
|
|
||||||
wh = ceilheight_mm - radius_mm - stepy;
|
|
||||||
|
|
||||||
Contour3D pwalls;
|
|
||||||
pwalls = walls(ob_prev, ob, wh_prev, wh, throw_on_cancel);
|
|
||||||
|
|
||||||
curvedwalls.merge(pwalls);
|
|
||||||
ob_prev = ob;
|
|
||||||
wh_prev = wh;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_offset = std::move(ob);
|
last_offset = std::move(ob);
|
||||||
last_height = wh;
|
last_height = wh;
|
||||||
|
|
||||||
|
@ -457,32 +461,60 @@ 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)
|
||||||
{
|
{
|
||||||
double mdist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm) +
|
|
||||||
cfg.max_merge_distance_mm;
|
|
||||||
|
|
||||||
auto concavehs = concave_hull(ground_layer, mdist, cfg.throw_on_cancel);
|
double mergedist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm)+
|
||||||
|
cfg.max_merge_distance_mm;
|
||||||
|
|
||||||
|
// Here we get the base polygon from which the pad has to be generated.
|
||||||
|
// We create an artificial concave hull from this polygon and that will
|
||||||
|
// serve as the bottom plate of the pad. We will offset this concave hull
|
||||||
|
// and then offset back the result with clipper with rounding edges ON. This
|
||||||
|
// trick will create a nice rounded pad shape.
|
||||||
|
auto concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// scaled values
|
||||||
|
const coord_t s_thickness = mm(thickness);
|
||||||
|
const coord_t s_eradius = mm(cfg.edge_radius_mm);
|
||||||
|
const coord_t s_safety_dist = 2*s_eradius + coord_t(0.8*s_thickness);
|
||||||
|
// const coord_t wheight = mm(cfg.min_wall_height_mm);
|
||||||
|
coord_t s_wingdist = mm(wingdist);
|
||||||
|
|
||||||
|
auto& thrcl = cfg.throw_on_cancel;
|
||||||
|
|
||||||
for(ExPolygon& concaveh : concavehs) {
|
for(ExPolygon& concaveh : concavehs) {
|
||||||
if(concaveh.contour.points.empty()) return;
|
if(concaveh.contour.points.empty()) return;
|
||||||
|
|
||||||
|
// Get rif of any holes in the concave hull output.
|
||||||
concaveh.holes.clear();
|
concaveh.holes.clear();
|
||||||
|
|
||||||
const coord_t WALL_THICKNESS = mm(cfg.min_wall_thickness_mm);
|
// Here lies the trick that does the smooting only with clipper offset
|
||||||
|
// calls. The offset is configured to round edges. Inner edges will
|
||||||
const coord_t WALL_DISTANCE = mm(2*cfg.edge_radius_mm) +
|
// be rounded because we offset twice: ones to get the outer (top) plate
|
||||||
coord_t(0.8*WALL_THICKNESS);
|
// and again to get the inner (bottom) plate
|
||||||
|
|
||||||
const coord_t HEIGHT = mm(cfg.min_wall_height_mm);
|
|
||||||
|
|
||||||
auto outer_base = concaveh;
|
auto outer_base = concaveh;
|
||||||
offset(outer_base, WALL_THICKNESS+WALL_DISTANCE);
|
outer_base.holes.clear();
|
||||||
|
offset(outer_base, s_safety_dist + s_wingdist + s_thickness);
|
||||||
auto inner_base = outer_base;
|
auto inner_base = outer_base;
|
||||||
offset(inner_base, -WALL_THICKNESS);
|
offset(inner_base, -(s_thickness + s_wingdist));
|
||||||
inner_base.holes.clear(); outer_base.holes.clear();
|
|
||||||
|
|
||||||
|
// Punching a hole in the top plate for the cavity
|
||||||
ExPolygon top_poly;
|
ExPolygon top_poly;
|
||||||
|
ExPolygon middle_base;
|
||||||
top_poly.contour = outer_base.contour;
|
top_poly.contour = outer_base.contour;
|
||||||
top_poly.holes.emplace_back(inner_base.contour);
|
|
||||||
auto& tph = top_poly.holes.back().points;
|
if(wingheight > 0) {
|
||||||
std::reverse(tph.begin(), tph.end());
|
middle_base = outer_base;
|
||||||
|
offset(middle_base, -s_thickness);
|
||||||
|
top_poly.holes.emplace_back(middle_base.contour);
|
||||||
|
auto& tph = top_poly.holes.back().points;
|
||||||
|
std::reverse(tph.begin(), tph.end());
|
||||||
|
}
|
||||||
|
|
||||||
Contour3D pool;
|
Contour3D pool;
|
||||||
|
|
||||||
|
@ -497,13 +529,15 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
||||||
// y = cy + (r^2*py - r*px*sqrt(px^2 + py^2 - r^2) / (px^2 + py^2)
|
// y = cy + (r^2*py - r*px*sqrt(px^2 + py^2 - r^2) / (px^2 + py^2)
|
||||||
// where px and py are the coordinates of the point outside the circle
|
// where px and py are the coordinates of the point outside the circle
|
||||||
// cx and cy are the circle center, r is the radius
|
// cx and cy are the circle center, r is the radius
|
||||||
|
// We place the circle center to (0, 0) in the calculation the make
|
||||||
|
// things easier.
|
||||||
// to get the angle we use arcsin function and subtract 90 degrees then
|
// to get the angle we use arcsin function and subtract 90 degrees then
|
||||||
// flip the sign to get the right input to the round_edge function.
|
// flip the sign to get the right input to the round_edge function.
|
||||||
double r = cfg.edge_radius_mm;
|
double r = cfg.edge_radius_mm;
|
||||||
double cy = 0;
|
double cy = 0;
|
||||||
double cx = 0;
|
double cx = 0;
|
||||||
double px = cfg.min_wall_thickness_mm;
|
double px = thickness + wingdist;
|
||||||
double py = r - cfg.min_wall_height_mm;
|
double py = r - fullheight;
|
||||||
|
|
||||||
double pxcx = px - cx;
|
double pxcx = px - cx;
|
||||||
double pycy = py - cy;
|
double pycy = py - cy;
|
||||||
|
@ -513,45 +547,59 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
||||||
double vy = (r_2*pycy - r*pxcx*D) / b_2;
|
double vy = (r_2*pycy - r*pxcx*D) / b_2;
|
||||||
double phi = -(std::asin(vy/r) * 180 / PI - 90);
|
double phi = -(std::asin(vy/r) * 180 / PI - 90);
|
||||||
|
|
||||||
auto curvedwalls = round_edges(ob,
|
|
||||||
r,
|
|
||||||
phi, // 170 degrees
|
|
||||||
0, // z position of the input plane
|
|
||||||
true,
|
|
||||||
cfg.throw_on_cancel,
|
|
||||||
ob, wh);
|
|
||||||
|
|
||||||
pool.merge(curvedwalls);
|
// Generate the smoothed edge geometry
|
||||||
|
auto walledges = round_edges(ob,
|
||||||
|
r,
|
||||||
|
phi,
|
||||||
|
0, // z position of the input plane
|
||||||
|
true,
|
||||||
|
thrcl,
|
||||||
|
ob, wh);
|
||||||
|
pool.merge(walledges);
|
||||||
|
|
||||||
ExPolygon ob_contr = ob;
|
// Now that we have the rounded edge connencting the top plate with
|
||||||
ob_contr.holes.clear();
|
// the outer side walls, we can generate and merge the sidewall geometry
|
||||||
|
auto pwalls = walls(ob, inner_base, wh, -fullheight, thrcl);
|
||||||
auto pwalls = walls(ob_contr, inner_base, wh, -cfg.min_wall_height_mm,
|
|
||||||
cfg.throw_on_cancel);
|
|
||||||
pool.merge(pwalls);
|
pool.merge(pwalls);
|
||||||
|
|
||||||
|
if(wingheight > 0) {
|
||||||
|
// Generate the smoothed edge geometry
|
||||||
|
auto cavityedges = round_edges(middle_base,
|
||||||
|
r,
|
||||||
|
phi - 90, // from tangent lines
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
thrcl,
|
||||||
|
ob, wh);
|
||||||
|
pool.merge(cavityedges);
|
||||||
|
|
||||||
|
// Next is the cavity walls connecting to the top plate's
|
||||||
|
// artificially created hole.
|
||||||
|
auto cavitywalls = walls(inner_base, ob, -wingheight, wh, thrcl);
|
||||||
|
pool.merge(cavitywalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// eleveted by the thickness.
|
||||||
Polygons top_triangles, bottom_triangles;
|
Polygons top_triangles, bottom_triangles;
|
||||||
|
|
||||||
triangulate(top_poly, top_triangles);
|
triangulate(top_poly, top_triangles);
|
||||||
triangulate(inner_base, bottom_triangles);
|
triangulate(inner_base, bottom_triangles);
|
||||||
|
|
||||||
auto top_plate = convert(top_triangles, 0, false);
|
auto top_plate = convert(top_triangles, 0, false);
|
||||||
auto bottom_plate = convert(bottom_triangles, -HEIGHT, true);
|
auto bottom_plate = convert(bottom_triangles, -mm(fullheight), true);
|
||||||
|
|
||||||
ob = inner_base; wh = 0;
|
|
||||||
// rounded edge generation for the inner bed
|
|
||||||
curvedwalls = round_edges(ob,
|
|
||||||
cfg.edge_radius_mm,
|
|
||||||
90, // 90 degrees
|
|
||||||
0, // z position of the input plane
|
|
||||||
false,
|
|
||||||
cfg.throw_on_cancel,
|
|
||||||
ob, wh);
|
|
||||||
pool.merge(curvedwalls);
|
|
||||||
|
|
||||||
auto innerbed = inner_bed(ob, cfg.min_wall_height_mm/2 + wh, wh);
|
|
||||||
|
|
||||||
pool.merge(top_plate);
|
pool.merge(top_plate);
|
||||||
pool.merge(bottom_plate);
|
pool.merge(bottom_plate);
|
||||||
pool.merge(innerbed);
|
|
||||||
|
if(wingheight > 0) {
|
||||||
|
Polygons middle_triangles;
|
||||||
|
triangulate(inner_base, middle_triangles);
|
||||||
|
auto middle_plate = convert(middle_triangles, -mm(wingheight), false);
|
||||||
|
pool.merge(middle_plate);
|
||||||
|
}
|
||||||
|
|
||||||
out.merge(mesh(pool));
|
out.merge(mesh(pool));
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,11 @@ void create_base_pool(const ExPolygons& base_plate,
|
||||||
/// min_wall_thickness and it should be corrected in the future. This method
|
/// min_wall_thickness and it should be corrected in the future. This method
|
||||||
/// will return the correct value for further processing.
|
/// will return the correct value for further processing.
|
||||||
inline double get_pad_elevation(const PoolConfig& cfg) {
|
inline double get_pad_elevation(const PoolConfig& cfg) {
|
||||||
return cfg.min_wall_height_mm / 2.0;
|
return cfg.min_wall_thickness_mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double get_pad_fullheight(const PoolConfig& cfg) {
|
||||||
|
return cfg.min_wall_height_mm + cfg.min_wall_thickness_mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -515,7 +515,8 @@ struct Pad {
|
||||||
double ground_level,
|
double ground_level,
|
||||||
const PoolConfig& pcfg) :
|
const PoolConfig& pcfg) :
|
||||||
cfg(pcfg),
|
cfg(pcfg),
|
||||||
zlevel(ground_level + sla::get_pad_elevation(pcfg))
|
zlevel(ground_level +
|
||||||
|
(sla::get_pad_fullheight(pcfg) - sla::get_pad_elevation(pcfg)) )
|
||||||
{
|
{
|
||||||
ExPolygons basep;
|
ExPolygons basep;
|
||||||
cfg.throw_on_cancel();
|
cfg.throw_on_cancel();
|
||||||
|
@ -523,7 +524,8 @@ struct Pad {
|
||||||
// The 0.1f is the layer height with which the mesh is sampled and then
|
// The 0.1f is the layer height with which the mesh is sampled and then
|
||||||
// the layers are unified into one vector of polygons.
|
// 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), 0.1f, pcfg.throw_on_cancel);
|
float(cfg.min_wall_height_mm + cfg.min_wall_thickness_mm),
|
||||||
|
0.1f, pcfg.throw_on_cancel);
|
||||||
|
|
||||||
for(auto& bp : baseplate) basep.emplace_back(bp);
|
for(auto& bp : baseplate) basep.emplace_back(bp);
|
||||||
|
|
||||||
|
@ -781,7 +783,7 @@ public:
|
||||||
// WITH THE PAD
|
// WITH THE PAD
|
||||||
double full_height() const {
|
double full_height() const {
|
||||||
if(merged_mesh().empty() && !pad().empty())
|
if(merged_mesh().empty() && !pad().empty())
|
||||||
return pad().cfg.min_wall_height_mm;
|
return get_pad_fullheight(pad().cfg);
|
||||||
|
|
||||||
double h = mesh_height();
|
double h = mesh_height();
|
||||||
if(!pad().empty()) h += sla::get_pad_elevation(pad().cfg);
|
if(!pad().empty()) h += sla::get_pad_elevation(pad().cfg);
|
||||||
|
|
|
@ -630,7 +630,7 @@ void SLAPrint::process()
|
||||||
sla::PoolConfig pcfg(wt, h, md, er);
|
sla::PoolConfig pcfg(wt, h, md, er);
|
||||||
|
|
||||||
ExPolygons bp;
|
ExPolygons bp;
|
||||||
double pad_h = sla::get_pad_elevation(pcfg);
|
double pad_h = sla::get_pad_fullheight(pcfg);
|
||||||
auto&& trmesh = po.transformed_mesh();
|
auto&& trmesh = po.transformed_mesh();
|
||||||
|
|
||||||
// This call can get pretty time consuming
|
// This call can get pretty time consuming
|
||||||
|
|
|
@ -66,12 +66,8 @@ void Field::PostInitialize()
|
||||||
BUILD();
|
BUILD();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Field::on_kill_focus(wxEvent& event)
|
void Field::on_kill_focus()
|
||||||
{
|
{
|
||||||
// Without this, there will be nasty focus bugs on Windows.
|
|
||||||
// Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all
|
|
||||||
// non-command events to allow the default handling to take place."
|
|
||||||
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_opt_id);
|
m_on_kill_focus(m_opt_id);
|
||||||
|
@ -250,11 +246,23 @@ void TextCtrl::BUILD() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const long style = m_opt.multiline ? wxTE_MULTILINE : 0;
|
const long style = m_opt.multiline ? wxTE_MULTILINE : wxTE_PROCESS_ENTER/*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));
|
||||||
|
|
||||||
|
if (style == wxTE_PROCESS_ENTER) {
|
||||||
|
temp->Bind(wxEVT_TEXT_ENTER, ([this, temp](wxEvent& e)
|
||||||
|
{
|
||||||
|
#if !defined(__WXGTK__)
|
||||||
|
e.Skip();
|
||||||
|
temp->GetToolTip()->Enable(true);
|
||||||
|
#endif // __WXGTK__
|
||||||
|
propagate_value();
|
||||||
|
bEnterPressed = true;
|
||||||
|
}), temp->GetId());
|
||||||
|
}
|
||||||
|
|
||||||
temp->Bind(wxEVT_SET_FOCUS, ([this](wxEvent& e) { on_set_focus(e); }), temp->GetId());
|
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)
|
||||||
|
@ -272,14 +280,15 @@ void TextCtrl::BUILD() {
|
||||||
|
|
||||||
temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e)
|
temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e)
|
||||||
{
|
{
|
||||||
#if !defined(__WXGTK__)
|
|
||||||
e.Skip();
|
e.Skip();
|
||||||
|
#if !defined(__WXGTK__)
|
||||||
temp->GetToolTip()->Enable(true);
|
temp->GetToolTip()->Enable(true);
|
||||||
#endif // __WXGTK__
|
#endif // __WXGTK__
|
||||||
if (is_defined_input_value<wxTextCtrl>(window, m_opt.type))
|
if (bEnterPressed) {
|
||||||
on_change_field();
|
bEnterPressed = false;
|
||||||
else
|
return;
|
||||||
on_kill_focus(e);
|
}
|
||||||
|
propagate_value();
|
||||||
}), temp->GetId());
|
}), temp->GetId());
|
||||||
/*
|
/*
|
||||||
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt)
|
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt)
|
||||||
|
@ -311,6 +320,14 @@ void TextCtrl::BUILD() {
|
||||||
window = dynamic_cast<wxWindow*>(temp);
|
window = dynamic_cast<wxWindow*>(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextCtrl::propagate_value()
|
||||||
|
{
|
||||||
|
if (is_defined_input_value<wxTextCtrl>(window, m_opt.type))
|
||||||
|
on_change_field();
|
||||||
|
else
|
||||||
|
on_kill_focus();
|
||||||
|
}
|
||||||
|
|
||||||
boost::any& TextCtrl::get_value()
|
boost::any& TextCtrl::get_value()
|
||||||
{
|
{
|
||||||
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
|
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
|
||||||
|
@ -398,7 +415,7 @@ void SpinCtrl::BUILD() {
|
||||||
const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647;
|
const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647;
|
||||||
|
|
||||||
auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size,
|
auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size,
|
||||||
0, min_val, max_val, default_value);
|
0|wxTE_PROCESS_ENTER, min_val, max_val, default_value);
|
||||||
|
|
||||||
#ifndef __WXOSX__
|
#ifndef __WXOSX__
|
||||||
// #ys_FIXME_KILL_FOCUS
|
// #ys_FIXME_KILL_FOCUS
|
||||||
|
@ -407,15 +424,23 @@ void SpinCtrl::BUILD() {
|
||||||
// and on TEXT event under OSX
|
// and on TEXT event under OSX
|
||||||
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e)
|
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e)
|
||||||
{
|
{
|
||||||
if (tmp_value < 0)
|
e.Skip();
|
||||||
on_kill_focus(e);
|
if (bEnterPressed) {
|
||||||
else {
|
bEnterPressed = false;
|
||||||
e.Skip();
|
return;
|
||||||
on_change_field();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
propagate_value();
|
||||||
}), temp->GetId());
|
}), temp->GetId());
|
||||||
|
|
||||||
temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
||||||
|
|
||||||
|
temp->Bind(wxEVT_TEXT_ENTER, ([this](wxCommandEvent e)
|
||||||
|
{
|
||||||
|
e.Skip();
|
||||||
|
propagate_value();
|
||||||
|
bEnterPressed = true;
|
||||||
|
}), temp->GetId());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e)
|
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e)
|
||||||
|
@ -430,12 +455,7 @@ void SpinCtrl::BUILD() {
|
||||||
tmp_value = std::stoi(value);
|
tmp_value = std::stoi(value);
|
||||||
else tmp_value = -9999;
|
else tmp_value = -9999;
|
||||||
#ifdef __WXOSX__
|
#ifdef __WXOSX__
|
||||||
if (tmp_value < 0) {
|
propagate_value();
|
||||||
if (m_on_kill_focus != nullptr)
|
|
||||||
m_on_kill_focus(m_opt_id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
on_change_field();
|
|
||||||
#endif
|
#endif
|
||||||
}), temp->GetId());
|
}), temp->GetId());
|
||||||
|
|
||||||
|
@ -445,6 +465,14 @@ void SpinCtrl::BUILD() {
|
||||||
window = dynamic_cast<wxWindow*>(temp);
|
window = dynamic_cast<wxWindow*>(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpinCtrl::propagate_value()
|
||||||
|
{
|
||||||
|
if (tmp_value < 0)
|
||||||
|
on_kill_focus();
|
||||||
|
else
|
||||||
|
on_change_field();
|
||||||
|
}
|
||||||
|
|
||||||
void Choice::BUILD() {
|
void Choice::BUILD() {
|
||||||
auto size = wxSize(wxDefaultSize);
|
auto size = wxSize(wxDefaultSize);
|
||||||
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
|
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
|
||||||
|
@ -483,7 +511,7 @@ void Choice::BUILD() {
|
||||||
on_change_field();
|
on_change_field();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
on_kill_focus(e);
|
on_kill_focus();
|
||||||
}), temp->GetId());
|
}), temp->GetId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,8 +783,8 @@ void PointCtrl::BUILD()
|
||||||
val = default_pt(1);
|
val = default_pt(1);
|
||||||
wxString Y = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None);
|
wxString Y = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None);
|
||||||
|
|
||||||
x_textctrl = new wxTextCtrl(m_parent, wxID_ANY, X, wxDefaultPosition, field_size);
|
x_textctrl = new wxTextCtrl(m_parent, wxID_ANY, X, wxDefaultPosition, field_size, wxEVT_TEXT_ENTER);
|
||||||
y_textctrl = new wxTextCtrl(m_parent, wxID_ANY, Y, wxDefaultPosition, field_size);
|
y_textctrl = new wxTextCtrl(m_parent, wxID_ANY, Y, wxDefaultPosition, field_size, wxEVT_TEXT_ENTER);
|
||||||
|
|
||||||
temp->Add(new wxStaticText(m_parent, wxID_ANY, "x : "), 0, wxALIGN_CENTER_VERTICAL, 0);
|
temp->Add(new wxStaticText(m_parent, wxID_ANY, "x : "), 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||||
temp->Add(x_textctrl);
|
temp->Add(x_textctrl);
|
||||||
|
@ -766,8 +794,11 @@ void PointCtrl::BUILD()
|
||||||
// x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId());
|
// x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId());
|
||||||
// y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId());
|
// y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId());
|
||||||
|
|
||||||
x_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), x_textctrl->GetId());
|
x_textctrl->Bind(wxEVT_TEXT_ENTER, ([this](wxCommandEvent e) { propagate_value(x_textctrl); }), x_textctrl->GetId());
|
||||||
y_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), y_textctrl->GetId());
|
y_textctrl->Bind(wxEVT_TEXT_ENTER, ([this](wxCommandEvent e) { propagate_value(y_textctrl); }), y_textctrl->GetId());
|
||||||
|
|
||||||
|
x_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { e.Skip(); propagate_value(x_textctrl); }), x_textctrl->GetId());
|
||||||
|
y_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { e.Skip(); propagate_value(y_textctrl); }), y_textctrl->GetId());
|
||||||
|
|
||||||
// // recast as a wxWindow to fit the calling convention
|
// // recast as a wxWindow to fit the calling convention
|
||||||
sizer = dynamic_cast<wxSizer*>(temp);
|
sizer = dynamic_cast<wxSizer*>(temp);
|
||||||
|
@ -776,14 +807,12 @@ void PointCtrl::BUILD()
|
||||||
y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
|
y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PointCtrl::OnKillFocus(wxEvent& e, wxTextCtrl* win)
|
void PointCtrl::propagate_value(wxTextCtrl* win)
|
||||||
{
|
{
|
||||||
e.Skip();
|
if (!win->GetValue().empty())
|
||||||
if (!win->GetValue().empty()) {
|
|
||||||
on_change_field();
|
on_change_field();
|
||||||
}
|
|
||||||
else
|
else
|
||||||
on_kill_focus(e);
|
on_kill_focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PointCtrl::set_value(const Vec2d& value, bool change_event)
|
void PointCtrl::set_value(const Vec2d& value, bool change_event)
|
||||||
|
|
|
@ -74,7 +74,7 @@ protected:
|
||||||
/// Call the attached on_kill_focus method.
|
/// Call the attached on_kill_focus method.
|
||||||
//! It's important to use wxEvent instead of wxFocusEvent,
|
//! It's important to use wxEvent instead of wxFocusEvent,
|
||||||
//! 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();
|
||||||
/// Call the attached on_change method.
|
/// Call the attached on_change method.
|
||||||
void on_set_focus(wxEvent& event);
|
void on_set_focus(wxEvent& event);
|
||||||
/// Call the attached on_change method.
|
/// Call the attached on_change method.
|
||||||
|
@ -226,6 +226,8 @@ protected:
|
||||||
|
|
||||||
// current value
|
// current value
|
||||||
boost::any m_value;
|
boost::any m_value;
|
||||||
|
|
||||||
|
bool bEnterPressed = false;
|
||||||
|
|
||||||
friend class OptionsGroup;
|
friend class OptionsGroup;
|
||||||
};
|
};
|
||||||
|
@ -252,6 +254,8 @@ public:
|
||||||
~TextCtrl() {}
|
~TextCtrl() {}
|
||||||
|
|
||||||
void BUILD();
|
void BUILD();
|
||||||
|
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
|
||||||
|
void propagate_value();
|
||||||
wxWindow* window {nullptr};
|
wxWindow* window {nullptr};
|
||||||
|
|
||||||
virtual void set_value(const std::string& value, bool change_event = false) {
|
virtual void set_value(const std::string& value, bool change_event = false) {
|
||||||
|
@ -310,6 +314,8 @@ public:
|
||||||
|
|
||||||
wxWindow* window{ nullptr };
|
wxWindow* window{ nullptr };
|
||||||
void BUILD() override;
|
void BUILD() override;
|
||||||
|
/// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
|
||||||
|
void propagate_value() ;
|
||||||
|
|
||||||
void set_value(const std::string& value, bool change_event = false) {
|
void set_value(const std::string& value, bool change_event = false) {
|
||||||
m_disable_change_event = !change_event;
|
m_disable_change_event = !change_event;
|
||||||
|
@ -393,8 +399,8 @@ public:
|
||||||
wxTextCtrl* y_textctrl{ nullptr };
|
wxTextCtrl* y_textctrl{ nullptr };
|
||||||
|
|
||||||
void BUILD() override;
|
void BUILD() override;
|
||||||
|
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
|
||||||
void OnKillFocus(wxEvent& e, wxTextCtrl* win);
|
void propagate_value(wxTextCtrl* win);
|
||||||
void set_value(const Vec2d& value, bool change_event = false);
|
void set_value(const Vec2d& value, bool change_event = false);
|
||||||
void set_value(const boost::any& value, bool change_event = false);
|
void set_value(const boost::any& value, bool change_event = false);
|
||||||
boost::any& get_value() override;
|
boost::any& get_value() override;
|
||||||
|
|
|
@ -2039,11 +2039,17 @@ void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_fiel
|
||||||
const Vec3d& center = get_bounding_box().center();
|
const Vec3d& center = get_bounding_box().center();
|
||||||
|
|
||||||
if (is_single_full_instance())
|
if (is_single_full_instance())
|
||||||
|
{
|
||||||
::glTranslated(center(0), center(1), center(2));
|
::glTranslated(center(0), center(1), center(2));
|
||||||
|
if (boost::starts_with(sidebar_field, "scale"))
|
||||||
|
{
|
||||||
|
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
||||||
|
::glMultMatrixd(orient_matrix.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (is_single_volume() || is_single_modifier())
|
else if (is_single_volume() || is_single_modifier())
|
||||||
{
|
{
|
||||||
const GLVolume* volume = (*m_volumes)[*m_list.begin()];
|
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
||||||
Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true);
|
|
||||||
const Vec3d& offset = get_bounding_box().center();
|
const Vec3d& offset = get_bounding_box().center();
|
||||||
|
|
||||||
::glTranslated(offset(0), offset(1), offset(2));
|
::glTranslated(offset(0), offset(1), offset(2));
|
||||||
|
@ -2054,8 +2060,7 @@ void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_fiel
|
||||||
::glTranslated(center(0), center(1), center(2));
|
::glTranslated(center(0), center(1), center(2));
|
||||||
if (requires_local_axes())
|
if (requires_local_axes())
|
||||||
{
|
{
|
||||||
const GLVolume* volume = (*m_volumes)[*m_list.begin()];
|
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
||||||
Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true);
|
|
||||||
::glMultMatrixd(orient_matrix.data());
|
::glMultMatrixd(orient_matrix.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2513,7 +2518,9 @@ void GLCanvas3D::Selection::_render_sidebar_rotation_hints(const std::string& si
|
||||||
|
|
||||||
void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sidebar_field) const
|
void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sidebar_field) const
|
||||||
{
|
{
|
||||||
if (boost::ends_with(sidebar_field, "x") || requires_uniform_scale())
|
bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling();
|
||||||
|
|
||||||
|
if (boost::ends_with(sidebar_field, "x") || uniform_scale)
|
||||||
{
|
{
|
||||||
::glPushMatrix();
|
::glPushMatrix();
|
||||||
::glRotated(-90.0, 0.0, 0.0, 1.0);
|
::glRotated(-90.0, 0.0, 0.0, 1.0);
|
||||||
|
@ -2521,14 +2528,14 @@ void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sideb
|
||||||
::glPopMatrix();
|
::glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (boost::ends_with(sidebar_field, "y") || requires_uniform_scale())
|
if (boost::ends_with(sidebar_field, "y") || uniform_scale)
|
||||||
{
|
{
|
||||||
::glPushMatrix();
|
::glPushMatrix();
|
||||||
_render_sidebar_scale_hint(Y);
|
_render_sidebar_scale_hint(Y);
|
||||||
::glPopMatrix();
|
::glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (boost::ends_with(sidebar_field, "z") || requires_uniform_scale())
|
if (boost::ends_with(sidebar_field, "z") || uniform_scale)
|
||||||
{
|
{
|
||||||
::glPushMatrix();
|
::glPushMatrix();
|
||||||
::glRotated(90.0, 1.0, 0.0, 0.0);
|
::glRotated(90.0, 1.0, 0.0, 0.0);
|
||||||
|
@ -2559,7 +2566,7 @@ void GLCanvas3D::Selection::_render_sidebar_rotation_hint(Axis axis) const
|
||||||
|
|
||||||
void GLCanvas3D::Selection::_render_sidebar_scale_hint(Axis axis) const
|
void GLCanvas3D::Selection::_render_sidebar_scale_hint(Axis axis) const
|
||||||
{
|
{
|
||||||
m_arrow.set_color((requires_uniform_scale() ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3);
|
m_arrow.set_color(((requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3);
|
||||||
|
|
||||||
::glTranslated(0.0, 5.0, 0.0);
|
::glTranslated(0.0, 5.0, 0.0);
|
||||||
m_arrow.render();
|
m_arrow.render();
|
||||||
|
|
|
@ -633,8 +633,10 @@ void ObjectList::get_settings_choice(const wxString& category_name)
|
||||||
|
|
||||||
|
|
||||||
// Add settings item for object
|
// Add settings item for object
|
||||||
const auto item = GetSelection();
|
auto item = GetSelection();
|
||||||
if (item) {
|
if (item) {
|
||||||
|
if (m_objects_model->GetItemType(item) == itInstance)
|
||||||
|
item = m_objects_model->GetTopParent(item);
|
||||||
const auto settings_item = m_objects_model->IsSettingsItem(item) ? item : m_objects_model->GetSettingsItem(item);
|
const auto settings_item = m_objects_model->IsSettingsItem(item) ? item : m_objects_model->GetSettingsItem(item);
|
||||||
select_item(settings_item ? settings_item :
|
select_item(settings_item ? settings_item :
|
||||||
m_objects_model->AddSettingsChild(item));
|
m_objects_model->AddSettingsChild(item));
|
||||||
|
@ -849,7 +851,7 @@ void ObjectList::load_part( ModelObject* model_object,
|
||||||
new_volume->set_type(static_cast<ModelVolume::Type>(type));
|
new_volume->set_type(static_cast<ModelVolume::Type>(type));
|
||||||
new_volume->name = boost::filesystem::path(input_file).filename().string();
|
new_volume->name = boost::filesystem::path(input_file).filename().string();
|
||||||
|
|
||||||
part_names.Add(new_volume->name);
|
part_names.Add(from_u8(new_volume->name));
|
||||||
|
|
||||||
// set a default extruder value, since user can't add it manually
|
// set a default extruder value, since user can't add it manually
|
||||||
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||||
|
@ -903,7 +905,8 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const int
|
||||||
m_parts_changed = true;
|
m_parts_changed = true;
|
||||||
parts_changed(obj_idx);
|
parts_changed(obj_idx);
|
||||||
|
|
||||||
select_item(m_objects_model->AddVolumeChild(GetSelection(), name, type));
|
const auto object_item = m_objects_model->GetTopParent(GetSelection());
|
||||||
|
select_item(m_objects_model->AddVolumeChild(object_item, from_u8(name), type));
|
||||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||||
selection_changed();
|
selection_changed();
|
||||||
#endif //no __WXOSX__ //__WXMSW__
|
#endif //no __WXOSX__ //__WXMSW__
|
||||||
|
@ -1031,7 +1034,7 @@ void ObjectList::split()
|
||||||
parent = item;
|
parent = item;
|
||||||
|
|
||||||
for (auto id = 0; id < model_object->volumes.size(); id++) {
|
for (auto id = 0; id < model_object->volumes.size(); id++) {
|
||||||
const auto vol_item = m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name,
|
const auto vol_item = m_objects_model->AddVolumeChild(parent, from_u8(model_object->volumes[id]->name),
|
||||||
model_object->volumes[id]->is_modifier() ?
|
model_object->volumes[id]->is_modifier() ?
|
||||||
ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART,
|
ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART,
|
||||||
model_object->volumes[id]->config.has("extruder") ?
|
model_object->volumes[id]->config.has("extruder") ?
|
||||||
|
@ -1156,6 +1159,10 @@ void ObjectList::part_selection_changed()
|
||||||
else if (m_objects_model->GetItemType(item) == itInstance) {
|
else if (m_objects_model->GetItemType(item) == itInstance) {
|
||||||
og_name = _(L("Instance manipulation"));
|
og_name = _(L("Instance manipulation"));
|
||||||
update_and_show_manipulations = true;
|
update_and_show_manipulations = true;
|
||||||
|
|
||||||
|
// fill m_config by object's values
|
||||||
|
const int obj_idx_ = m_objects_model->GetObjectIdByItem(item);
|
||||||
|
m_config = &(*m_objects)[obj_idx_]->config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1188,7 +1195,7 @@ void ObjectList::part_selection_changed()
|
||||||
void ObjectList::add_object_to_list(size_t obj_idx)
|
void ObjectList::add_object_to_list(size_t obj_idx)
|
||||||
{
|
{
|
||||||
auto model_object = (*m_objects)[obj_idx];
|
auto model_object = (*m_objects)[obj_idx];
|
||||||
wxString item_name = model_object->name;
|
wxString item_name = from_u8(model_object->name);
|
||||||
const auto item = m_objects_model->Add(item_name,
|
const auto item = m_objects_model->Add(item_name,
|
||||||
!model_object->config.has("extruder") ? 0 :
|
!model_object->config.has("extruder") ? 0 :
|
||||||
model_object->config.option<ConfigOptionInt>("extruder")->value);
|
model_object->config.option<ConfigOptionInt>("extruder")->value);
|
||||||
|
@ -1207,8 +1214,8 @@ void ObjectList::add_object_to_list(size_t obj_idx)
|
||||||
if (model_object->volumes.size() > 1) {
|
if (model_object->volumes.size() > 1) {
|
||||||
for (auto id = 0; id < model_object->volumes.size(); id++) {
|
for (auto id = 0; id < model_object->volumes.size(); id++) {
|
||||||
auto vol_item = m_objects_model->AddVolumeChild(item,
|
auto vol_item = m_objects_model->AddVolumeChild(item,
|
||||||
model_object->volumes[id]->name,
|
from_u8(model_object->volumes[id]->name),
|
||||||
model_object->volumes[id]->type()/*ModelVolume::MODEL_PART*/,
|
model_object->volumes[id]->type(),
|
||||||
!model_object->volumes[id]->config.has("extruder") ? 0 :
|
!model_object->volumes[id]->config.has("extruder") ? 0 :
|
||||||
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value,
|
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value,
|
||||||
false);
|
false);
|
||||||
|
|
|
@ -19,7 +19,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||||
OG_Settings(parent, true)
|
OG_Settings(parent, true)
|
||||||
{
|
{
|
||||||
m_og->set_name(_(L("Object Manipulation")));
|
m_og->set_name(_(L("Object Manipulation")));
|
||||||
m_og->label_width = 100;
|
m_og->label_width = 125;
|
||||||
m_og->set_grid_vgap(5);
|
m_og->set_grid_vgap(5);
|
||||||
|
|
||||||
m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) {
|
m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) {
|
||||||
|
@ -150,7 +150,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||||
m_og->append_line(line);
|
m_og->append_line(line);
|
||||||
|
|
||||||
|
|
||||||
auto add_og_to_object_settings = [](const std::string& option_name, const std::string& sidetext)
|
auto add_og_to_object_settings = [this](const std::string& option_name, const std::string& sidetext)
|
||||||
{
|
{
|
||||||
Line line = { _(option_name), "" };
|
Line line = { _(option_name), "" };
|
||||||
ConfigOptionDef def;
|
ConfigOptionDef def;
|
||||||
|
@ -164,6 +164,27 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||||
def.max = 360;
|
def.max = 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add "uniform scaling" button in front of "Scale" option
|
||||||
|
else if (option_name == "Scale") {
|
||||||
|
line.near_label_widget = [this](wxWindow* parent) {
|
||||||
|
auto btn = new PrusaLockButton(parent, wxID_ANY);
|
||||||
|
btn->Bind(wxEVT_BUTTON, [btn, this](wxCommandEvent &event){
|
||||||
|
event.Skip();
|
||||||
|
wxTheApp->CallAfter([btn, this]() { set_uniform_scaling(btn->IsLocked()); });
|
||||||
|
});
|
||||||
|
m_lock_bnt = btn;
|
||||||
|
return btn;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add empty bmp (Its size have to be equal to PrusaLockButton) in front of "Size" option to label alignment
|
||||||
|
else if (option_name == "Size") {
|
||||||
|
line.near_label_widget = [this](wxWindow* parent) {
|
||||||
|
return new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap, wxDefaultPosition,
|
||||||
|
wxBitmap(from_u8(var("one_layer_lock_on.png")), wxBITMAP_TYPE_PNG).GetSize());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const std::string lower_name = boost::algorithm::to_lower_copy(option_name);
|
const std::string lower_name = boost::algorithm::to_lower_copy(option_name);
|
||||||
|
|
||||||
std::vector<std::string> axes{ "x", "y", "z" };
|
std::vector<std::string> axes{ "x", "y", "z" };
|
||||||
|
@ -379,6 +400,15 @@ void ObjectManipulation::update_if_dirty()
|
||||||
|
|
||||||
m_cache.rotation = m_new_rotation;
|
m_cache.rotation = m_new_rotation;
|
||||||
|
|
||||||
|
if (wxGetApp().plater()->canvas3D()->get_selection().requires_uniform_scale()) {
|
||||||
|
m_lock_bnt->SetLock(true);
|
||||||
|
m_lock_bnt->Disable();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_lock_bnt->SetLock(m_uniform_scale);
|
||||||
|
m_lock_bnt->Enable();
|
||||||
|
}
|
||||||
|
|
||||||
if (m_new_enabled)
|
if (m_new_enabled)
|
||||||
m_og->enable();
|
m_og->enable();
|
||||||
else
|
else
|
||||||
|
@ -479,7 +509,7 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale)
|
||||||
{
|
{
|
||||||
Vec3d scaling_factor = scale;
|
Vec3d scaling_factor = scale;
|
||||||
const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||||
if (selection.requires_uniform_scale())
|
if (m_uniform_scale || selection.requires_uniform_scale())
|
||||||
{
|
{
|
||||||
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||||
Vec3d abs_scale_diff = (scale - m_cache.scale).cwiseAbs();
|
Vec3d abs_scale_diff = (scale - m_cache.scale).cwiseAbs();
|
||||||
|
@ -535,7 +565,7 @@ void ObjectManipulation::change_size_value(const Vec3d& size)
|
||||||
Vec3d scale = 100.0 * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2));
|
Vec3d scale = 100.0 * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2));
|
||||||
Vec3d scaling_factor = scale;
|
Vec3d scaling_factor = scale;
|
||||||
|
|
||||||
if (selection.requires_uniform_scale())
|
if (m_uniform_scale || selection.requires_uniform_scale())
|
||||||
{
|
{
|
||||||
Vec3d abs_scale_diff = (scale - m_cache.scale).cwiseAbs();
|
Vec3d abs_scale_diff = (scale - m_cache.scale).cwiseAbs();
|
||||||
double max_diff = abs_scale_diff(X);
|
double max_diff = abs_scale_diff(X);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "GLCanvas3D.hpp"
|
#include "GLCanvas3D.hpp"
|
||||||
|
|
||||||
class wxStaticText;
|
class wxStaticText;
|
||||||
|
class PrusaLockButton;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
@ -74,6 +75,8 @@ class ObjectManipulation : public OG_Settings
|
||||||
Vec3d m_new_scale;
|
Vec3d m_new_scale;
|
||||||
Vec3d m_new_size;
|
Vec3d m_new_size;
|
||||||
bool m_new_enabled;
|
bool m_new_enabled;
|
||||||
|
bool m_uniform_scale {false};
|
||||||
|
PrusaLockButton* m_lock_bnt{ nullptr };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ObjectManipulation(wxWindow* parent);
|
ObjectManipulation(wxWindow* parent);
|
||||||
|
@ -88,6 +91,9 @@ public:
|
||||||
// Called from the App to update the UI if dirty.
|
// Called from the App to update the UI if dirty.
|
||||||
void update_if_dirty();
|
void update_if_dirty();
|
||||||
|
|
||||||
|
void set_uniform_scaling(const bool uniform_scale) { m_uniform_scale = uniform_scale;}
|
||||||
|
bool get_uniform_scaling() const { return m_uniform_scale; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void reset_settings_value();
|
void reset_settings_value();
|
||||||
|
|
||||||
|
|
|
@ -2201,6 +2201,7 @@ PrusaLockButton::PrusaLockButton( wxWindow *parent,
|
||||||
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||||
#endif // __WXMSW__
|
#endif // __WXMSW__
|
||||||
SetBitmap(m_bmp_unlock_on);
|
SetBitmap(m_bmp_unlock_on);
|
||||||
|
SetBitmapDisabled(m_bmp_lock_on);
|
||||||
|
|
||||||
//button events
|
//button events
|
||||||
Bind(wxEVT_BUTTON, &PrusaLockButton::OnButton, this);
|
Bind(wxEVT_BUTTON, &PrusaLockButton::OnButton, this);
|
||||||
|
@ -2216,6 +2217,12 @@ void PrusaLockButton::OnButton(wxCommandEvent& event)
|
||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrusaLockButton::SetLock(bool lock)
|
||||||
|
{
|
||||||
|
m_is_pushed = lock;
|
||||||
|
enter_button(true);
|
||||||
|
}
|
||||||
|
|
||||||
void PrusaLockButton::enter_button(const bool enter)
|
void PrusaLockButton::enter_button(const bool enter)
|
||||||
{
|
{
|
||||||
wxBitmap* icon = m_is_pushed ?
|
wxBitmap* icon = m_is_pushed ?
|
||||||
|
|
|
@ -851,6 +851,7 @@ public:
|
||||||
void OnLeaveBtn(wxMouseEvent& event) { enter_button(false); event.Skip(); }
|
void OnLeaveBtn(wxMouseEvent& event) { enter_button(false); event.Skip(); }
|
||||||
|
|
||||||
bool IsLocked() const { return m_is_pushed; }
|
bool IsLocked() const { return m_is_pushed; }
|
||||||
|
void SetLock(bool lock);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void enter_button(const bool enter);
|
void enter_button(const bool enter);
|
||||||
|
|
Loading…
Add table
Reference in a new issue