mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-09-27 00:13:12 +08:00
Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_sequential
This commit is contained in:
commit
04b7a52ef1
@ -237,14 +237,14 @@ documentation_link = https://help.prusa3d.com/article/prusaslicer-printables-com
|
|||||||
weight = 3
|
weight = 3
|
||||||
|
|
||||||
[hint:Cut tool]
|
[hint:Cut tool]
|
||||||
text = Cut tool\nDid you know that you can cut a model at any angle and even create aligning pins with the updated Cut tool? Learn more in the documentation.
|
text = Cut tool\nDid you know that you can cut a model at any angle and even create aligning pins with the updated <a>Cut tool</a>? Learn more in the documentation.
|
||||||
documentation_link = https://help.prusa3d.com/article/cut-tool_1779
|
documentation_link = https://help.prusa3d.com/article/cut-tool_1779
|
||||||
hypertext_type = gizmo
|
hypertext_type = gizmo
|
||||||
hypertext_gizmo_item = cut
|
hypertext_gizmo_item = cut
|
||||||
weight = 3
|
weight = 3
|
||||||
|
|
||||||
[hint:Measurement tool]
|
[hint:Measurement tool]
|
||||||
text = Measurement tool\nDid you know that you can measure the distances between points, edges and planes, the radius of a hole or the angle between edges or planes? Learn more in the documentation.
|
text = Measurement tool\nDid you know that you can <a>measure</a> the distances between points, edges and planes, the radius of a hole or the angle between edges or planes? Learn more in the documentation.
|
||||||
documentation_link = https://help.prusa3d.com/article/measurement-tool_399451
|
documentation_link = https://help.prusa3d.com/article/measurement-tool_399451
|
||||||
hypertext_type = gizmo
|
hypertext_type = gizmo
|
||||||
hypertext_gizmo_item = measure
|
hypertext_gizmo_item = measure
|
||||||
|
@ -720,28 +720,26 @@ void Transformation::reset()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_WORLD_COORDINATE
|
#if ENABLE_WORLD_COORDINATE
|
||||||
|
void Transformation::reset_rotation()
|
||||||
|
{
|
||||||
|
const Geometry::TransformationSVD svd(*this);
|
||||||
|
m_matrix = get_offset_matrix() * Transform3d(svd.v * svd.s * svd.v.transpose()) * svd.mirror_matrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transformation::reset_scaling_factor()
|
||||||
|
{
|
||||||
|
const Geometry::TransformationSVD svd(*this);
|
||||||
|
m_matrix = get_offset_matrix() * Transform3d(svd.u) * Transform3d(svd.v.transpose()) * svd.mirror_matrix();
|
||||||
|
}
|
||||||
|
|
||||||
void Transformation::reset_skew()
|
void Transformation::reset_skew()
|
||||||
{
|
{
|
||||||
Matrix3d rotation;
|
auto new_scale_factor = [](const Matrix3d& s) {
|
||||||
Matrix3d scale;
|
return pow(s(0, 0) * s(1, 1) * s(2, 2), 1. / 3.); // scale average
|
||||||
m_matrix.computeRotationScaling(&rotation, &scale);
|
};
|
||||||
|
|
||||||
const double average_scale = std::cbrt(scale(0, 0) * scale(1, 1) * scale(2, 2));
|
const Geometry::TransformationSVD svd(*this);
|
||||||
|
m_matrix = get_offset_matrix() * Transform3d(svd.u) * scale_transform(new_scale_factor(svd.s)) * Transform3d(svd.v.transpose()) * svd.mirror_matrix();
|
||||||
scale(0, 0) = is_left_handed() ? -average_scale : average_scale;
|
|
||||||
scale(1, 1) = average_scale;
|
|
||||||
scale(2, 2) = average_scale;
|
|
||||||
|
|
||||||
scale(0, 1) = 0.0;
|
|
||||||
scale(0, 2) = 0.0;
|
|
||||||
scale(1, 0) = 0.0;
|
|
||||||
scale(1, 2) = 0.0;
|
|
||||||
scale(2, 0) = 0.0;
|
|
||||||
scale(2, 1) = 0.0;
|
|
||||||
|
|
||||||
const Vec3d offset = get_offset();
|
|
||||||
m_matrix = rotation * scale;
|
|
||||||
m_matrix.translation() = offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform3d Transformation::get_matrix_no_offset() const
|
Transform3d Transformation::get_matrix_no_offset() const
|
||||||
@ -838,6 +836,43 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation
|
|||||||
}
|
}
|
||||||
#endif // !ENABLE_WORLD_COORDINATE
|
#endif // !ENABLE_WORLD_COORDINATE
|
||||||
|
|
||||||
|
#if ENABLE_WORLD_COORDINATE
|
||||||
|
TransformationSVD::TransformationSVD(const Transform3d& trafo)
|
||||||
|
{
|
||||||
|
const auto &m0 = trafo.matrix().block<3, 3>(0, 0);
|
||||||
|
mirror = m0.determinant() < 0.0;
|
||||||
|
|
||||||
|
Matrix3d m;
|
||||||
|
if (mirror)
|
||||||
|
m = m0 * Eigen::DiagonalMatrix<double, 3, 3>(-1.0, 1.0, 1.0);
|
||||||
|
else
|
||||||
|
m = m0;
|
||||||
|
const Eigen::JacobiSVD<Matrix3d> svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV);
|
||||||
|
u = svd.matrixU();
|
||||||
|
v = svd.matrixV();
|
||||||
|
s = svd.singularValues().asDiagonal();
|
||||||
|
|
||||||
|
scale = !s.isApprox(Matrix3d::Identity());
|
||||||
|
anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2));
|
||||||
|
rotation = !v.isApprox(u);
|
||||||
|
|
||||||
|
if (anisotropic_scale) {
|
||||||
|
rotation_90_degrees = true;
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
const Vec3d row = v.row(i).cwiseAbs();
|
||||||
|
size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.);
|
||||||
|
size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.);
|
||||||
|
if (num_zeros != 2 || num_ones != 1) {
|
||||||
|
rotation_90_degrees = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skew = ! rotation_90_degrees;
|
||||||
|
} else
|
||||||
|
skew = false;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
|
|
||||||
// For parsing a transformation matrix from 3MF / AMF.
|
// For parsing a transformation matrix from 3MF / AMF.
|
||||||
Transform3d transform3d_from_string(const std::string& transform_str)
|
Transform3d transform3d_from_string(const std::string& transform_str)
|
||||||
{
|
{
|
||||||
|
@ -492,8 +492,8 @@ public:
|
|||||||
void reset();
|
void reset();
|
||||||
#if ENABLE_WORLD_COORDINATE
|
#if ENABLE_WORLD_COORDINATE
|
||||||
void reset_offset() { set_offset(Vec3d::Zero()); }
|
void reset_offset() { set_offset(Vec3d::Zero()); }
|
||||||
void reset_rotation() { set_rotation(Vec3d::Zero()); }
|
void reset_rotation();
|
||||||
void reset_scaling_factor() { set_scaling_factor(Vec3d::Ones()); }
|
void reset_scaling_factor();
|
||||||
void reset_mirror() { set_mirror(Vec3d::Ones()); }
|
void reset_mirror() { set_mirror(Vec3d::Ones()); }
|
||||||
void reset_skew();
|
void reset_skew();
|
||||||
|
|
||||||
@ -538,6 +538,27 @@ private:
|
|||||||
#endif // ENABLE_WORLD_COORDINATE
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if ENABLE_WORLD_COORDINATE
|
||||||
|
struct TransformationSVD
|
||||||
|
{
|
||||||
|
Matrix3d u = Matrix3d::Identity();
|
||||||
|
Matrix3d s = Matrix3d::Identity();
|
||||||
|
Matrix3d v = Matrix3d::Identity();
|
||||||
|
|
||||||
|
bool mirror{ false };
|
||||||
|
bool scale{ false };
|
||||||
|
bool anisotropic_scale{ false };
|
||||||
|
bool rotation{ false };
|
||||||
|
bool rotation_90_degrees{ false };
|
||||||
|
bool skew{ false };
|
||||||
|
|
||||||
|
explicit TransformationSVD(const Transformation& trafo) : TransformationSVD(trafo.get_matrix()) {}
|
||||||
|
explicit TransformationSVD(const Transform3d& trafo);
|
||||||
|
|
||||||
|
Eigen::DiagonalMatrix<double, 3, 3> mirror_matrix() const { return Eigen::DiagonalMatrix<double, 3, 3>(this->mirror ? -1. : 1., 1., 1.); }
|
||||||
|
};
|
||||||
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
|
|
||||||
// For parsing a transformation matrix from 3MF / AMF.
|
// For parsing a transformation matrix from 3MF / AMF.
|
||||||
extern Transform3d transform3d_from_string(const std::string& transform_str);
|
extern Transform3d transform3d_from_string(const std::string& transform_str);
|
||||||
|
|
||||||
|
@ -127,9 +127,7 @@ static void connect_layer_slices(
|
|||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
auto assert_intersection_valid = [this](int i, int j) {
|
auto assert_intersection_valid = [this](int i, int j) {
|
||||||
assert(i != j);
|
assert(i < j);
|
||||||
if (i > j)
|
|
||||||
std::swap(i, j);
|
|
||||||
assert(i >= m_offset_below);
|
assert(i >= m_offset_below);
|
||||||
assert(i < m_offset_above);
|
assert(i < m_offset_above);
|
||||||
assert(j >= m_offset_above);
|
assert(j >= m_offset_above);
|
||||||
@ -140,35 +138,47 @@ static void connect_layer_slices(
|
|||||||
if (polynode.Contour.size() >= 3) {
|
if (polynode.Contour.size() >= 3) {
|
||||||
// If there is an intersection point, it should indicate which contours (one from layer below, the other from layer above) intersect.
|
// If there is an intersection point, it should indicate which contours (one from layer below, the other from layer above) intersect.
|
||||||
// Otherwise the contour is fully inside another contour.
|
// Otherwise the contour is fully inside another contour.
|
||||||
int32_t i = 0, j = 0;
|
int32_t i = -1, j = -1;
|
||||||
for (int icontour = 0; icontour <= polynode.ChildCount(); ++ icontour) {
|
for (int icontour = 0; icontour <= polynode.ChildCount(); ++ icontour) {
|
||||||
const bool first = icontour == 0;
|
const ClipperLib_Z::Path &contour = icontour == 0 ? polynode.Contour : polynode.Childs[icontour - 1]->Contour;
|
||||||
const ClipperLib_Z::Path &contour = first ? polynode.Contour : polynode.Childs[icontour - 1]->Contour;
|
|
||||||
if (contour.size() >= 3) {
|
if (contour.size() >= 3) {
|
||||||
if (first) {
|
|
||||||
i = contour.front().z();
|
|
||||||
j = i;
|
|
||||||
if (i < 0) {
|
|
||||||
std::tie(i, j) = m_intersections[-i - 1];
|
|
||||||
assert(assert_intersection_valid(i, j));
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const ClipperLib_Z::IntPoint &pt : contour) {
|
for (const ClipperLib_Z::IntPoint &pt : contour) {
|
||||||
j = pt.z();
|
j = pt.z();
|
||||||
if (j < 0) {
|
if (j < 0) {
|
||||||
|
const auto &intersection = m_intersections[-j - 1];
|
||||||
|
assert(intersection.first <= intersection.second);
|
||||||
|
if (intersection.second < m_offset_above) {
|
||||||
|
// Ignore intersection of polygons on the 1st layer.
|
||||||
|
assert(intersection.first >= m_offset_below);
|
||||||
|
j = i;
|
||||||
|
} else if (intersection.first >= m_offset_above) {
|
||||||
|
// Ignore intersection of polygons on the 2nd layer
|
||||||
|
assert(intersection.second < m_offset_end);
|
||||||
|
j = i;
|
||||||
|
} else {
|
||||||
std::tie(i, j) = m_intersections[-j - 1];
|
std::tie(i, j) = m_intersections[-j - 1];
|
||||||
assert(assert_intersection_valid(i, j));
|
assert(assert_intersection_valid(i, j));
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (i != j)
|
} else if (i == -1) {
|
||||||
|
// First source contour of this expolygon was found.
|
||||||
|
i = j;
|
||||||
|
} else if (i != j) {
|
||||||
|
// Second source contour of this expolygon was found.
|
||||||
|
if (i > j)
|
||||||
|
std::swap(i, j);
|
||||||
|
assert(assert_intersection_valid(i, j));
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
end:
|
end:
|
||||||
bool found = false;
|
bool found = false;
|
||||||
if (i == j) {
|
if (i == -1) {
|
||||||
|
// This should not happen. It may only happen if the source contours had just self intersections or intersections with contours at the same layer.
|
||||||
|
assert(false);
|
||||||
|
} else if (i == j) {
|
||||||
// The contour is completely inside another contour.
|
// The contour is completely inside another contour.
|
||||||
Point pt(polynode.Contour.front().x(), polynode.Contour.front().y());
|
Point pt(polynode.Contour.front().x(), polynode.Contour.front().y());
|
||||||
if (i < m_offset_above) {
|
if (i < m_offset_above) {
|
||||||
@ -202,8 +212,6 @@ static void connect_layer_slices(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(assert_intersection_valid(i, j));
|
assert(assert_intersection_valid(i, j));
|
||||||
if (i > j)
|
|
||||||
std::swap(i, j);
|
|
||||||
i -= m_offset_below;
|
i -= m_offset_below;
|
||||||
j -= m_offset_above;
|
j -= m_offset_above;
|
||||||
assert(i >= 0 && i < m_below.lslices_ex.size());
|
assert(i >= 0 && i < m_below.lslices_ex.size());
|
||||||
|
@ -458,7 +458,7 @@ static std::vector<std::string> s_Preset_print_options {
|
|||||||
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
|
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
|
||||||
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
|
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
|
||||||
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
|
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
|
||||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects",
|
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio",
|
||||||
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
|
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
|
||||||
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
|
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
|
||||||
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
|
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||||
|
@ -598,14 +598,6 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloat(0.f));
|
def->set_default_value(new ConfigOptionFloat(0.f));
|
||||||
|
|
||||||
def = this->add("clip_multipart_objects", coBool);
|
|
||||||
def->label = L("Clip multi-part objects");
|
|
||||||
def->tooltip = L("When printing multi-material objects, this settings will make Slic3r "
|
|
||||||
"to clip the overlapping object parts one by the other "
|
|
||||||
"(2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc).");
|
|
||||||
def->mode = comExpert;
|
|
||||||
def->set_default_value(new ConfigOptionBool(true));
|
|
||||||
|
|
||||||
def = this->add("colorprint_heights", coFloats);
|
def = this->add("colorprint_heights", coFloats);
|
||||||
def->label = L("Colorprint height");
|
def->label = L("Colorprint height");
|
||||||
def->tooltip = L("Heights at which a filament change is to occur.");
|
def->tooltip = L("Heights at which a filament change is to occur.");
|
||||||
@ -4052,6 +4044,22 @@ void PrintConfigDef::init_sla_params()
|
|||||||
def->set_default_value(new ConfigOptionFloat(0.001));
|
def->set_default_value(new ConfigOptionFloat(0.001));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore the following obsolete configuration keys:
|
||||||
|
static std::set<std::string> PrintConfigDef_ignore = {
|
||||||
|
"clip_multipart_objects",
|
||||||
|
"duplicate_x", "duplicate_y", "gcode_arcs", "multiply_x", "multiply_y",
|
||||||
|
"support_material_tool", "acceleration", "adjust_overhang_flow",
|
||||||
|
"standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
|
||||||
|
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
|
||||||
|
"seal_position", "vibration_limit", "bed_size",
|
||||||
|
"print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe",
|
||||||
|
"serial_port", "serial_speed",
|
||||||
|
// Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed.
|
||||||
|
"fuzzy_skin_perimeter_mode", "fuzzy_skin_shape",
|
||||||
|
// Introduced in PrusaSlicer 2.3.0-alpha2, later replaced by automatic calculation based on extrusion width.
|
||||||
|
"wall_add_middle_threshold", "wall_split_middle_threshold",
|
||||||
|
};
|
||||||
|
|
||||||
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
|
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
|
||||||
{
|
{
|
||||||
// handle legacy options
|
// handle legacy options
|
||||||
@ -4125,32 +4133,17 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// Ignore the following obsolete configuration keys:
|
|
||||||
static std::set<std::string> ignore = {
|
|
||||||
"duplicate_x", "duplicate_y", "gcode_arcs", "multiply_x", "multiply_y",
|
|
||||||
"support_material_tool", "acceleration", "adjust_overhang_flow",
|
|
||||||
"standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
|
|
||||||
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
|
|
||||||
"seal_position", "vibration_limit", "bed_size",
|
|
||||||
"print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe",
|
|
||||||
"serial_port", "serial_speed",
|
|
||||||
// Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed.
|
|
||||||
"fuzzy_skin_perimeter_mode", "fuzzy_skin_shape",
|
|
||||||
// Introduced in PrusaSlicer 2.3.0-alpha2, later replaced by automatic calculation based on extrusion width.
|
|
||||||
"wall_add_middle_threshold", "wall_split_middle_threshold",
|
|
||||||
};
|
|
||||||
|
|
||||||
// In PrusaSlicer 2.3.0-alpha0 the "monotonous" infill was introduced, which was later renamed to "monotonic".
|
// In PrusaSlicer 2.3.0-alpha0 the "monotonous" infill was introduced, which was later renamed to "monotonic".
|
||||||
if (value == "monotonous" && (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern" || opt_key == "fill_pattern"))
|
if (value == "monotonous" && (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern" || opt_key == "fill_pattern"))
|
||||||
value = "monotonic";
|
value = "monotonic";
|
||||||
|
|
||||||
if (ignore.find(opt_key) != ignore.end()) {
|
if (PrintConfigDef_ignore.find(opt_key) != PrintConfigDef_ignore.end()) {
|
||||||
opt_key = "";
|
opt_key = {};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! print_config_def.has(opt_key)) {
|
if (! print_config_def.has(opt_key)) {
|
||||||
opt_key = "";
|
opt_key = {};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -487,7 +487,6 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||||||
((ConfigOptionFloat, brim_separation))
|
((ConfigOptionFloat, brim_separation))
|
||||||
((ConfigOptionEnum<BrimType>, brim_type))
|
((ConfigOptionEnum<BrimType>, brim_type))
|
||||||
((ConfigOptionFloat, brim_width))
|
((ConfigOptionFloat, brim_width))
|
||||||
((ConfigOptionBool, clip_multipart_objects))
|
|
||||||
((ConfigOptionBool, dont_support_bridges))
|
((ConfigOptionBool, dont_support_bridges))
|
||||||
((ConfigOptionFloat, elefant_foot_compensation))
|
((ConfigOptionFloat, elefant_foot_compensation))
|
||||||
((ConfigOptionFloatOrPercent, extrusion_width))
|
((ConfigOptionFloatOrPercent, extrusion_width))
|
||||||
|
@ -620,8 +620,7 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||||||
|| opt_key == "slicing_mode") {
|
|| opt_key == "slicing_mode") {
|
||||||
steps.emplace_back(posSlice);
|
steps.emplace_back(posSlice);
|
||||||
} else if (
|
} else if (
|
||||||
opt_key == "clip_multipart_objects"
|
opt_key == "elefant_foot_compensation"
|
||||||
|| opt_key == "elefant_foot_compensation"
|
|
||||||
|| opt_key == "support_material_contact_distance"
|
|| opt_key == "support_material_contact_distance"
|
||||||
|| opt_key == "xy_size_compensation") {
|
|| opt_key == "xy_size_compensation") {
|
||||||
steps.emplace_back(posSlice);
|
steps.emplace_back(posSlice);
|
||||||
|
@ -237,9 +237,6 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||||||
const PrintObjectRegions &print_object_regions,
|
const PrintObjectRegions &print_object_regions,
|
||||||
const std::vector<float> &zs,
|
const std::vector<float> &zs,
|
||||||
std::vector<VolumeSlices> &&volume_slices,
|
std::vector<VolumeSlices> &&volume_slices,
|
||||||
// If clipping is disabled, then ExPolygons produced by different volumes will never be merged, thus they will be allowed to overlap.
|
|
||||||
// It is up to the model designer to handle these overlaps.
|
|
||||||
const bool clip_multipart_objects,
|
|
||||||
const std::function<void()> &throw_on_cancel_callback)
|
const std::function<void()> &throw_on_cancel_callback)
|
||||||
{
|
{
|
||||||
model_volumes_sort_by_id(model_volumes);
|
model_volumes_sort_by_id(model_volumes);
|
||||||
@ -308,7 +305,7 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||||||
}
|
}
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
tbb::blocked_range<size_t>(0, zs_complex.size()),
|
tbb::blocked_range<size_t>(0, zs_complex.size()),
|
||||||
[&slices_by_region, &print_object_regions, &zs_complex, &layer_ranges_regions_to_slices, clip_multipart_objects, &throw_on_cancel_callback]
|
[&slices_by_region, &print_object_regions, &zs_complex, &layer_ranges_regions_to_slices, &throw_on_cancel_callback]
|
||||||
(const tbb::blocked_range<size_t> &range) {
|
(const tbb::blocked_range<size_t> &range) {
|
||||||
float z = zs_complex[range.begin()].second;
|
float z = zs_complex[range.begin()].second;
|
||||||
auto it_layer_range = layer_range_first(print_object_regions.layer_ranges, z);
|
auto it_layer_range = layer_range_first(print_object_regions.layer_ranges, z);
|
||||||
@ -359,7 +356,7 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||||||
if (next_region_same_modifier)
|
if (next_region_same_modifier)
|
||||||
// To be used in the following iteration.
|
// To be used in the following iteration.
|
||||||
temp_slices[idx_region + 1].expolygons = std::move(source);
|
temp_slices[idx_region + 1].expolygons = std::move(source);
|
||||||
} else if ((region.model_volume->is_model_part() && clip_multipart_objects) || region.model_volume->is_negative_volume()) {
|
} else if (region.model_volume->is_model_part() || region.model_volume->is_negative_volume()) {
|
||||||
// Clip every non-zero region preceding it.
|
// Clip every non-zero region preceding it.
|
||||||
for (int idx_region2 = 0; idx_region2 < idx_region; ++ idx_region2)
|
for (int idx_region2 = 0; idx_region2 < idx_region; ++ idx_region2)
|
||||||
if (! temp_slices[idx_region2].expolygons.empty()) {
|
if (! temp_slices[idx_region2].expolygons.empty()) {
|
||||||
@ -388,10 +385,7 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||||||
merged = true;
|
merged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Don't unite the regions if ! clip_multipart_objects. In that case it is user's responsibility
|
if (merged)
|
||||||
// to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters
|
|
||||||
// for example.
|
|
||||||
if (merged && clip_multipart_objects)
|
|
||||||
expolygons = closing_ex(expolygons, float(scale_(EPSILON)));
|
expolygons = closing_ex(expolygons, float(scale_(EPSILON)));
|
||||||
slices_by_region[temp_slices[i].region_id][z_idx] = std::move(expolygons);
|
slices_by_region[temp_slices[i].region_id][z_idx] = std::move(expolygons);
|
||||||
i = j;
|
i = j;
|
||||||
@ -696,7 +690,6 @@ void PrintObject::slice_volumes()
|
|||||||
slice_volumes_inner(
|
slice_volumes_inner(
|
||||||
print->config(), this->config(), this->trafo_centered(),
|
print->config(), this->config(), this->trafo_centered(),
|
||||||
this->model_object()->volumes, m_shared_regions->layer_ranges, slice_zs, throw_on_cancel_callback),
|
this->model_object()->volumes, m_shared_regions->layer_ranges, slice_zs, throw_on_cancel_callback),
|
||||||
m_config.clip_multipart_objects,
|
|
||||||
throw_on_cancel_callback);
|
throw_on_cancel_callback);
|
||||||
|
|
||||||
for (size_t region_id = 0; region_id < region_slices.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < region_slices.size(); ++ region_id) {
|
||||||
|
@ -269,7 +269,8 @@ void set_current_thread_qos()
|
|||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
// OSX specific: Set Quality of Service to "user initiated", so that the threads will be scheduled to high performance
|
// OSX specific: Set Quality of Service to "user initiated", so that the threads will be scheduled to high performance
|
||||||
// cores if available.
|
// cores if available.
|
||||||
pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0);
|
// With QOS_CLASS_USER_INITIATED the worker threads drop priority once slicer loses user focus.
|
||||||
|
pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
|
||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1315,10 +1315,12 @@ PageUpdate::PageUpdate(ConfigWizard *parent)
|
|||||||
box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); });
|
box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace DownloaderUtils
|
namespace DownloaderUtils
|
||||||
{
|
{
|
||||||
|
namespace {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
wxString get_downloads_path()
|
wxString get_downloads_path()
|
||||||
{
|
{
|
||||||
wxString ret;
|
wxString ret;
|
||||||
@ -1330,7 +1332,6 @@ namespace DownloaderUtils
|
|||||||
CoTaskMemFree(path);
|
CoTaskMemFree(path);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif __APPLE__
|
#elif __APPLE__
|
||||||
wxString get_downloads_path()
|
wxString get_downloads_path()
|
||||||
{
|
{
|
||||||
@ -1348,9 +1349,8 @@ namespace DownloaderUtils
|
|||||||
}
|
}
|
||||||
return wxString();
|
return wxString();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
Worker::Worker(wxWindow* parent)
|
Worker::Worker(wxWindow* parent)
|
||||||
: wxBoxSizer(wxHORIZONTAL)
|
: wxBoxSizer(wxHORIZONTAL)
|
||||||
, m_parent(parent)
|
, m_parent(parent)
|
||||||
@ -1432,16 +1432,16 @@ PageDownloader::PageDownloader(ConfigWizard* parent)
|
|||||||
)));
|
)));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
box_allow_downloads->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->downloader->allow(event.IsChecked()); });
|
box_allow_downloads->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->m_downloader->allow(event.IsChecked()); });
|
||||||
|
|
||||||
downloader = new DownloaderUtils::Worker(this);
|
m_downloader = new DownloaderUtils::Worker(this);
|
||||||
append(downloader);
|
append(m_downloader);
|
||||||
downloader->allow(box_allow_value);
|
m_downloader->allow(box_allow_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PageDownloader::on_finish_downloader() const
|
bool PageDownloader::on_finish_downloader() const
|
||||||
{
|
{
|
||||||
return downloader->on_finish();
|
return m_downloader->on_finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DownloaderUtils::Worker::perform_register(const std::string& path_override/* = {}*/)
|
bool DownloaderUtils::Worker::perform_register(const std::string& path_override/* = {}*/)
|
||||||
@ -3035,9 +3035,11 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
// Desktop integration on Linux
|
// Desktop integration on Linux
|
||||||
BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << page_downloader->downloader->get_perform_registration_linux();
|
BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << page_downloader->m_downloader->get_perform_registration_linux();
|
||||||
if (page_welcome->integrate_desktop() || page_downloader->downloader->get_perform_registration_linux())
|
if (page_welcome->integrate_desktop())
|
||||||
DesktopIntegrationDialog::perform_desktop_integration(page_downloader->downloader->get_perform_registration_linux());
|
DesktopIntegrationDialog::perform_desktop_integration();
|
||||||
|
if (page_downloader->m_downloader->get_perform_registration_linux())
|
||||||
|
DesktopIntegrationDialog::perform_downloader_desktop_integration();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Decide whether to create snapshot based on run_reason and the reset profile checkbox
|
// Decide whether to create snapshot based on run_reason and the reset profile checkbox
|
||||||
@ -3175,6 +3177,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||||||
|
|
||||||
// apply materials in app_config
|
// apply materials in app_config
|
||||||
for (const std::string& section_name : {AppConfig::SECTION_FILAMENTS, AppConfig::SECTION_MATERIALS})
|
for (const std::string& section_name : {AppConfig::SECTION_FILAMENTS, AppConfig::SECTION_MATERIALS})
|
||||||
|
if (appconfig_new.has_section(section_name))
|
||||||
app_config->set_section(section_name, appconfig_new.get_section(section_name));
|
app_config->set_section(section_name, appconfig_new.get_section(section_name));
|
||||||
|
|
||||||
app_config->set_vendors(appconfig_new);
|
app_config->set_vendors(appconfig_new);
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <wx/dialog.h>
|
#include <wx/dialog.h>
|
||||||
|
#include <wx/sizer.h>
|
||||||
|
#include <wx/textctrl.h>
|
||||||
|
|
||||||
#include "GUI_Utils.hpp"
|
#include "GUI_Utils.hpp"
|
||||||
|
|
||||||
@ -14,6 +16,36 @@ class PresetUpdater;
|
|||||||
|
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
|
namespace DownloaderUtils {
|
||||||
|
class Worker : public wxBoxSizer
|
||||||
|
{
|
||||||
|
wxWindow* m_parent{ nullptr };
|
||||||
|
wxTextCtrl* m_input_path{ nullptr };
|
||||||
|
bool downloader_checked{ false };
|
||||||
|
#ifdef __linux__
|
||||||
|
bool perform_registration_linux{ false };
|
||||||
|
#endif // __linux__
|
||||||
|
|
||||||
|
void deregister();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Worker(wxWindow* parent);
|
||||||
|
~Worker() {}
|
||||||
|
|
||||||
|
void allow(bool allow_) { downloader_checked = allow_; }
|
||||||
|
bool is_checked() const { return downloader_checked; }
|
||||||
|
wxString path_name() const { return m_input_path ? m_input_path->GetValue() : wxString(); }
|
||||||
|
|
||||||
|
void set_path_name(wxString name);
|
||||||
|
void set_path_name(const std::string& name);
|
||||||
|
|
||||||
|
bool on_finish();
|
||||||
|
bool perform_register(const std::string& path_override = {});
|
||||||
|
#ifdef __linux__
|
||||||
|
bool get_perform_registration_linux() { return perform_registration_linux; }
|
||||||
|
#endif // __linux__
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class ConfigWizard: public DPIDialog
|
class ConfigWizard: public DPIDialog
|
||||||
{
|
{
|
||||||
|
@ -10,12 +10,10 @@
|
|||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
#include <wx/sizer.h>
|
|
||||||
#include <wx/panel.h>
|
#include <wx/panel.h>
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
#include <wx/choice.h>
|
#include <wx/choice.h>
|
||||||
#include <wx/spinctrl.h>
|
#include <wx/spinctrl.h>
|
||||||
#include <wx/textctrl.h>
|
|
||||||
#include <wx/listbox.h>
|
#include <wx/listbox.h>
|
||||||
#include <wx/checklst.h>
|
#include <wx/checklst.h>
|
||||||
#include <wx/radiobut.h>
|
#include <wx/radiobut.h>
|
||||||
@ -418,44 +416,10 @@ struct PageUpdate: ConfigWizardPage
|
|||||||
PageUpdate(ConfigWizard *parent);
|
PageUpdate(ConfigWizard *parent);
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace DownloaderUtils {
|
|
||||||
wxString get_downloads_path();
|
|
||||||
|
|
||||||
class Worker : public wxBoxSizer
|
|
||||||
{
|
|
||||||
wxWindow* m_parent {nullptr};
|
|
||||||
wxTextCtrl* m_input_path {nullptr};
|
|
||||||
bool downloader_checked {false};
|
|
||||||
#ifdef __linux__
|
|
||||||
bool perform_registration_linux { false };
|
|
||||||
#endif // __linux__
|
|
||||||
|
|
||||||
void deregister();
|
|
||||||
|
|
||||||
public:
|
|
||||||
Worker(wxWindow* parent);
|
|
||||||
~Worker(){}
|
|
||||||
|
|
||||||
void allow(bool allow_) { downloader_checked = allow_; }
|
|
||||||
bool is_checked() const { return downloader_checked; }
|
|
||||||
wxString path_name() const { return m_input_path ? m_input_path->GetValue() : wxString(); }
|
|
||||||
|
|
||||||
void set_path_name(wxString name);
|
|
||||||
void set_path_name(const std::string& name);
|
|
||||||
|
|
||||||
bool on_finish();
|
|
||||||
bool perform_register(const std::string& path_override = {});
|
|
||||||
#ifdef __linux__
|
|
||||||
bool get_perform_registration_linux() { return perform_registration_linux; }
|
|
||||||
#endif // __linux__
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct PageDownloader : ConfigWizardPage
|
struct PageDownloader : ConfigWizardPage
|
||||||
{
|
{
|
||||||
DownloaderUtils::Worker* downloader{ nullptr };
|
DownloaderUtils::Worker* m_downloader { nullptr };
|
||||||
|
|
||||||
PageDownloader(ConfigWizard* parent);
|
PageDownloader(ConfigWizard* parent);
|
||||||
|
|
||||||
|
@ -218,9 +218,9 @@ bool DesktopIntegrationDialog::integration_possible()
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void DesktopIntegrationDialog::perform_desktop_integration(bool perform_downloader)
|
void DesktopIntegrationDialog::perform_desktop_integration()
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(debug) << "performing desktop integration. With downloader integration: " << perform_downloader;
|
BOOST_LOG_TRIVIAL(debug) << "performing desktop integration.";
|
||||||
// Path to appimage
|
// Path to appimage
|
||||||
const char *appimage_env = std::getenv("APPIMAGE");
|
const char *appimage_env = std::getenv("APPIMAGE");
|
||||||
std::string excutable_path;
|
std::string excutable_path;
|
||||||
@ -423,38 +423,6 @@ void DesktopIntegrationDialog::perform_desktop_integration(bool perform_download
|
|||||||
show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully."));
|
show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (perform_downloader)
|
|
||||||
{
|
|
||||||
std::string desktop_file_downloader = GUI::format(
|
|
||||||
"[Desktop Entry]\n"
|
|
||||||
"Name=PrusaSlicer URL Protocol%1%\n"
|
|
||||||
"Exec=\"%3%\" --single-instance %%u\n"
|
|
||||||
"Icon=PrusaSlicer%4%\n"
|
|
||||||
"Terminal=false\n"
|
|
||||||
"Type=Application\n"
|
|
||||||
"MimeType=x-scheme-handler/prusaslicer;\n"
|
|
||||||
"StartupNotify=false\n"
|
|
||||||
, name_suffix, version_suffix, excutable_path, version_suffix);
|
|
||||||
|
|
||||||
// desktop file for downloader as part of main app
|
|
||||||
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix);
|
|
||||||
if (create_desktop_file(desktop_path, desktop_file_downloader)) {
|
|
||||||
// save path to desktop file
|
|
||||||
app_config->set("desktop_integration_URL_path", desktop_path);
|
|
||||||
// finish registration on mime type
|
|
||||||
std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix);
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "system command: " << command;
|
|
||||||
int r = system(command.c_str());
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "system result: " << r;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create URL Protocol desktop file";
|
|
||||||
show_error(nullptr, _L("Performing desktop integration failed - could not create URL Protocol desktop file."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
|
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
|
||||||
}
|
}
|
||||||
void DesktopIntegrationDialog::undo_desktop_intgration()
|
void DesktopIntegrationDialog::undo_desktop_intgration()
|
||||||
@ -487,15 +455,162 @@ void DesktopIntegrationDialog::undo_desktop_intgration()
|
|||||||
std::remove(path.c_str());
|
std::remove(path.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// URL Protocol
|
|
||||||
path = std::string(app_config->get("desktop_integration_URL_path"));
|
|
||||||
if (!path.empty()) {
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
|
||||||
std::remove(path.c_str());
|
|
||||||
}
|
|
||||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess);
|
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess);
|
||||||
}
|
}
|
||||||
|
void DesktopIntegrationDialog::perform_downloader_desktop_integration()
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "performing downloader desktop integration.";
|
||||||
|
// Path to appimage
|
||||||
|
const char* appimage_env = std::getenv("APPIMAGE");
|
||||||
|
std::string excutable_path;
|
||||||
|
if (appimage_env) {
|
||||||
|
try {
|
||||||
|
excutable_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string();
|
||||||
|
}
|
||||||
|
catch (std::exception&) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed - boost::filesystem::canonical did not return appimage path.";
|
||||||
|
show_error(nullptr, _L("Performing downloader desktop integration failed - boost::filesystem::canonical did not return appimage path."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// not appimage - find executable
|
||||||
|
excutable_path = boost::dll::program_location().string();
|
||||||
|
//excutable_path = wxStandardPaths::Get().GetExecutablePath().string();
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "non-appimage path to executable: " << excutable_path;
|
||||||
|
if (excutable_path.empty())
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed - no executable found.";
|
||||||
|
show_error(nullptr, _L("Performing downloader desktop integration failed - Could not find executable."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape ' characters in appimage, other special symbols will be esacaped in desktop file by 'excutable_path'
|
||||||
|
//boost::replace_all(excutable_path, "'", "'\\''");
|
||||||
|
excutable_path = escape_string(excutable_path);
|
||||||
|
|
||||||
|
// Find directories icons and applications
|
||||||
|
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
|
||||||
|
// If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
|
||||||
|
// $XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data files in addition to the $XDG_DATA_HOME base directory.
|
||||||
|
// The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
|
||||||
|
// If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
|
||||||
|
std::vector<std::string>target_candidates;
|
||||||
|
resolve_path_from_var("XDG_DATA_HOME", target_candidates);
|
||||||
|
resolve_path_from_var("XDG_DATA_DIRS", target_candidates);
|
||||||
|
|
||||||
|
AppConfig* app_config = wxGetApp().app_config;
|
||||||
|
// suffix string to create different desktop file for alpha, beta.
|
||||||
|
|
||||||
|
std::string version_suffix;
|
||||||
|
std::string name_suffix;
|
||||||
|
std::string version(SLIC3R_VERSION);
|
||||||
|
if (version.find("alpha") != std::string::npos)
|
||||||
|
{
|
||||||
|
version_suffix = "-alpha";
|
||||||
|
name_suffix = " - alpha";
|
||||||
|
}
|
||||||
|
else if (version.find("beta") != std::string::npos)
|
||||||
|
{
|
||||||
|
version_suffix = "-beta";
|
||||||
|
name_suffix = " - beta";
|
||||||
|
}
|
||||||
|
|
||||||
|
// theme path to icon destination
|
||||||
|
std::string icon_theme_path;
|
||||||
|
std::string icon_theme_dirs;
|
||||||
|
|
||||||
|
if (platform_flavor() == PlatformFlavor::LinuxOnChromium) {
|
||||||
|
icon_theme_path = "hicolor/96x96/apps/";
|
||||||
|
icon_theme_dirs = "/hicolor/96x96/apps";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string target_dir_desktop;
|
||||||
|
|
||||||
|
// desktop file
|
||||||
|
// iterate thru target_candidates to find applications folder
|
||||||
|
|
||||||
|
std::string desktop_file_downloader = GUI::format(
|
||||||
|
"[Desktop Entry]\n"
|
||||||
|
"Name=PrusaSlicer URL Protocol%1%\n"
|
||||||
|
"Exec=\"%2%\" --single-instance %%u\n"
|
||||||
|
"Terminal=false\n"
|
||||||
|
"Type=Application\n"
|
||||||
|
"MimeType=x-scheme-handler/prusaslicer;\n"
|
||||||
|
"StartupNotify=false\n"
|
||||||
|
"NoDisplay=true\n"
|
||||||
|
, name_suffix, excutable_path);
|
||||||
|
|
||||||
|
// desktop file for downloader as part of main app
|
||||||
|
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix);
|
||||||
|
if (create_desktop_file(desktop_path, desktop_file_downloader)) {
|
||||||
|
// save path to desktop file
|
||||||
|
app_config->set("desktop_integration_URL_path", desktop_path);
|
||||||
|
// finish registration on mime type
|
||||||
|
std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix);
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "system command: " << command;
|
||||||
|
int r = system(command.c_str());
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "system result: " << r;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool candidate_found = false;
|
||||||
|
for (size_t i = 0; i < target_candidates.size(); ++i) {
|
||||||
|
if (contains_path_dir(target_candidates[i], "applications")) {
|
||||||
|
target_dir_desktop = target_candidates[i];
|
||||||
|
// Write slicer desktop file
|
||||||
|
std::string path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix);
|
||||||
|
if (create_desktop_file(path, desktop_file_downloader)) {
|
||||||
|
app_config->set("desktop_integration_URL_path", path);
|
||||||
|
candidate_found = true;
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "PrusaSlicerURLProtocol.desktop file installation success.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// write failed - try another path
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Attempt to PrusaSlicerURLProtocol.desktop file installation failed. failed path: " << target_candidates[i];
|
||||||
|
target_dir_desktop.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if all failed - try creating default home folder
|
||||||
|
if (!candidate_found) {
|
||||||
|
// create $HOME/.local/share
|
||||||
|
create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications");
|
||||||
|
// create desktop file
|
||||||
|
target_dir_desktop = GUI::format("%1%/.local/share", wxFileName::GetHomeDir());
|
||||||
|
std::string path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix);
|
||||||
|
if (contains_path_dir(target_dir_desktop, "applications")) {
|
||||||
|
if (!create_desktop_file(path, desktop_file_downloader)) {
|
||||||
|
// Desktop file not written - end desktop integration
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed - could not create desktop file.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
app_config->set("desktop_integration_URL_path", path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Desktop file not written - end desktop integration
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed because the application directory was not found.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(!target_dir_desktop.empty());
|
||||||
|
if (target_dir_desktop.empty()) {
|
||||||
|
// Desktop file not written - end desktop integration
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed because the application directory was not found.";
|
||||||
|
show_error(nullptr, _L("Performing downloader desktop integration failed because the application directory was not found."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// finish registration on mime type
|
||||||
|
std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix);
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "system command: " << command;
|
||||||
|
int r = system(command.c_str());
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "system result: " << r;
|
||||||
|
|
||||||
|
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
|
||||||
|
}
|
||||||
void DesktopIntegrationDialog::undo_downloader_registration()
|
void DesktopIntegrationDialog::undo_downloader_registration()
|
||||||
{
|
{
|
||||||
const AppConfig *app_config = wxGetApp().app_config;
|
const AppConfig *app_config = wxGetApp().app_config;
|
||||||
@ -532,7 +647,7 @@ DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent)
|
|||||||
wxButton *btn_perform = new wxButton(this, wxID_ANY, _L("Perform"));
|
wxButton *btn_perform = new wxButton(this, wxID_ANY, _L("Perform"));
|
||||||
btn_szr->Add(btn_perform, 0, wxALL, 10);
|
btn_szr->Add(btn_perform, 0, wxALL, 10);
|
||||||
|
|
||||||
btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(false); EndModal(wxID_ANY); });
|
btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(); EndModal(wxID_ANY); });
|
||||||
|
|
||||||
if (can_undo){
|
if (can_undo){
|
||||||
wxButton *btn_undo = new wxButton(this, wxID_ANY, _L("Undo"));
|
wxButton *btn_undo = new wxButton(this, wxID_ANY, _L("Undo"));
|
||||||
|
@ -29,10 +29,11 @@ public:
|
|||||||
// if perform_downloader:
|
// if perform_downloader:
|
||||||
// Creates Destktop files for PrusaSlicer downloader feature
|
// Creates Destktop files for PrusaSlicer downloader feature
|
||||||
// Regiters PrusaSlicer to start on prusaslicer:// URL
|
// Regiters PrusaSlicer to start on prusaslicer:// URL
|
||||||
static void perform_desktop_integration(bool perform_downloader);
|
static void perform_desktop_integration();
|
||||||
// Deletes Desktop files and icons for both PrusaSlicer and GcodeViewer at paths stored in App Config.
|
// Deletes Desktop files and icons for both PrusaSlicer and GcodeViewer at paths stored in App Config.
|
||||||
static void undo_desktop_intgration();
|
static void undo_desktop_intgration();
|
||||||
|
|
||||||
|
static void perform_downloader_desktop_integration();
|
||||||
static void undo_downloader_registration();
|
static void undo_downloader_registration();
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -137,13 +137,30 @@ void FileGet::priv::get_perform()
|
|||||||
std::string extension = boost::filesystem::extension(dest_path);
|
std::string extension = boost::filesystem::extension(dest_path);
|
||||||
std::string just_filename = m_filename.substr(0, m_filename.size() - extension.size());
|
std::string just_filename = m_filename.substr(0, m_filename.size() - extension.size());
|
||||||
std::string final_filename = just_filename;
|
std::string final_filename = just_filename;
|
||||||
|
// Find unsed filename
|
||||||
|
try {
|
||||||
size_t version = 0;
|
size_t version = 0;
|
||||||
while (boost::filesystem::exists(m_dest_folder / (final_filename + extension)) || boost::filesystem::exists(m_dest_folder / (final_filename + extension + "." + std::to_string(get_current_pid()) + ".download")))
|
while (boost::filesystem::exists(m_dest_folder / (final_filename + extension)) || boost::filesystem::exists(m_dest_folder / (final_filename + extension + "." + std::to_string(get_current_pid()) + ".download")))
|
||||||
{
|
{
|
||||||
++version;
|
++version;
|
||||||
final_filename = just_filename + "(" + std::to_string(version) + ")";
|
if (version > 999) {
|
||||||
|
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR);
|
||||||
|
evt->SetString(GUI::format_wxstr(L"Failed to find suitable filename. Last name: %1%." , (m_dest_folder / (final_filename + extension)).string()));
|
||||||
|
evt->SetInt(m_id);
|
||||||
|
m_evt_handler->QueueEvent(evt);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
final_filename = GUI::format("%1%(%2%)", just_filename, std::to_string(version));
|
||||||
|
}
|
||||||
|
} catch (const boost::filesystem::filesystem_error& e)
|
||||||
|
{
|
||||||
|
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR);
|
||||||
|
evt->SetString(e.what());
|
||||||
|
evt->SetInt(m_id);
|
||||||
|
m_evt_handler->QueueEvent(evt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_filename = final_filename + extension;
|
m_filename = final_filename + extension;
|
||||||
|
|
||||||
m_tmp_path = m_dest_folder / (m_filename + "." + std::to_string(get_current_pid()) + ".download");
|
m_tmp_path = m_dest_folder / (m_filename + "." + std::to_string(get_current_pid()) + ".download");
|
||||||
|
@ -172,16 +172,20 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar
|
|||||||
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX)
|
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX)
|
||||||
, m_selected_paths (selected_paths)
|
, m_selected_paths (selected_paths)
|
||||||
{
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
wxGetApp().UpdateDarkUI(this);
|
||||||
|
#else
|
||||||
|
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||||
|
#endif
|
||||||
|
|
||||||
int em = em_unit();
|
int em = em_unit();
|
||||||
|
|
||||||
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
m_avc = new ArchiveViewCtrl(this, wxSize(45 * em, 30 * em));
|
||||||
m_avc = new ArchiveViewCtrl(this, wxSize(60 * em, 30 * em));
|
wxDataViewColumn* toggle_column = m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);
|
||||||
m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);
|
|
||||||
m_avc->AppendTextColumn("filename", 1);
|
m_avc->AppendTextColumn("filename", 1);
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<ArchiveViewNode>> stack;
|
std::vector<std::shared_ptr<ArchiveViewNode>> stack;
|
||||||
|
|
||||||
std::function<void(std::vector<std::shared_ptr<ArchiveViewNode> >&, size_t)> reduce_stack = [] (std::vector<std::shared_ptr<ArchiveViewNode>>& stack, size_t size) {
|
std::function<void(std::vector<std::shared_ptr<ArchiveViewNode> >&, size_t)> reduce_stack = [] (std::vector<std::shared_ptr<ArchiveViewNode>>& stack, size_t size) {
|
||||||
@ -233,40 +237,51 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar
|
|||||||
}
|
}
|
||||||
// sorting files will help adjust_stack function to not create multiple same folders
|
// sorting files will help adjust_stack function to not create multiple same folders
|
||||||
std::sort(filtered_entries.begin(), filtered_entries.end(), [](const boost::filesystem::path& p1, const boost::filesystem::path& p2){ return p1.string() > p2.string(); });
|
std::sort(filtered_entries.begin(), filtered_entries.end(), [](const boost::filesystem::path& p1, const boost::filesystem::path& p2){ return p1.string() > p2.string(); });
|
||||||
|
size_t entry_count = 0;
|
||||||
|
size_t depth = 1;
|
||||||
for (const boost::filesystem::path& path : filtered_entries)
|
for (const boost::filesystem::path& path : filtered_entries)
|
||||||
{
|
{
|
||||||
std::shared_ptr<ArchiveViewNode> parent(nullptr);
|
std::shared_ptr<ArchiveViewNode> parent(nullptr);
|
||||||
|
|
||||||
adjust_stack(path, stack);
|
depth = std::max(depth, adjust_stack(path, stack));
|
||||||
if (!stack.empty())
|
if (!stack.empty())
|
||||||
parent = stack.back();
|
parent = stack.back();
|
||||||
if (std::regex_match(path.extension().string(), pattern_drop)) { // this leaves out non-compatible files
|
if (std::regex_match(path.extension().string(), pattern_drop)) { // this leaves out non-compatible files
|
||||||
m_avc->get_model()->AddFile(parent, GUI::format_wxstr(path.filename().string()), false)->set_fullpath(/*std::move(path)*/path); // filename string to wstring?
|
m_avc->get_model()->AddFile(parent, GUI::format_wxstr(path.filename().string()), false)->set_fullpath(/*std::move(path)*/path); // filename string to wstring?
|
||||||
|
entry_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (entry_count == 1)
|
||||||
|
on_all_button();
|
||||||
|
|
||||||
|
toggle_column->SetWidth((4 + depth) * em);
|
||||||
|
|
||||||
wxBoxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
wxBoxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
wxButton* btn_all = new wxButton(this, wxID_ANY, "All");
|
wxButton* btn_all = new wxButton(this, wxID_ANY, _L("All"));
|
||||||
btn_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_all_button(); });
|
btn_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_all_button(); });
|
||||||
btn_sizer->Add(btn_all, 0, wxLeft);
|
btn_sizer->Add(btn_all, 0);
|
||||||
|
|
||||||
wxButton* btn_none = new wxButton(this, wxID_ANY, "None");
|
wxButton* btn_none = new wxButton(this, wxID_ANY, _L("None"));
|
||||||
btn_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_none_button(); });
|
btn_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_none_button(); });
|
||||||
btn_sizer->Add(btn_none, 0, wxLeft);
|
btn_sizer->Add(btn_none, 0, wxLEFT, em);
|
||||||
|
|
||||||
btn_sizer->AddStretchSpacer();
|
btn_sizer->AddStretchSpacer();
|
||||||
wxButton* btn_run = new wxButton(this, wxID_OK, "Open");
|
wxButton* btn_run = new wxButton(this, wxID_OK, _L("Open"));
|
||||||
btn_run->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_open_button(); });
|
btn_run->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_open_button(); });
|
||||||
btn_sizer->Add(btn_run, 0, wxRIGHT);
|
btn_sizer->Add(btn_run, 0, wxRIGHT, em);
|
||||||
|
|
||||||
wxButton* cancel_btn = new wxButton(this, wxID_CANCEL, "Cancel");
|
wxButton* cancel_btn = new wxButton(this, wxID_CANCEL, _L("Cancel"));
|
||||||
cancel_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { this->EndModal(wxID_CANCEL); });
|
cancel_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { this->EndModal(wxID_CANCEL); });
|
||||||
btn_sizer->Add(cancel_btn, 0, wxRIGHT);
|
btn_sizer->Add(cancel_btn, 0, wxRIGHT, em);
|
||||||
|
|
||||||
topSizer->Add(m_avc, 1, wxEXPAND | wxALL, 10);
|
topSizer->Add(m_avc, 1, wxEXPAND | wxALL, 10);
|
||||||
topSizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 10);
|
topSizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 10);
|
||||||
this->SetMinSize(wxSize(80 * em, 30 * em));
|
|
||||||
this->SetSizer(topSizer);
|
this->SetSizer(topSizer);
|
||||||
|
SetMinSize(wxSize(40 * em, 30 * em));
|
||||||
|
|
||||||
|
for (const wxString& id : {_L("All"), _L("None"), _L("Open"), _L("Cancel") })
|
||||||
|
wxGetApp().UpdateDarkUI(static_cast<wxButton*>(FindWindowByLabel(id, this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect)
|
void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect)
|
||||||
@ -277,9 +292,8 @@ void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect)
|
|||||||
//for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn })
|
//for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn })
|
||||||
// if (btn) btn->msw_rescale();
|
// if (btn) btn->msw_rescale();
|
||||||
|
|
||||||
const wxSize& size = wxSize(70 * em, 30 * em);
|
const wxSize& size = wxSize(45 * em, 40 * em);
|
||||||
SetMinSize(size);
|
SetSize(size);
|
||||||
|
|
||||||
//m_tree->Rescale(em);
|
//m_tree->Rescale(em);
|
||||||
|
|
||||||
Fit();
|
Fit();
|
||||||
|
@ -3947,8 +3947,8 @@ void GLCanvas3D::update_sequential_clearance()
|
|||||||
// the results are then cached for following displacements
|
// the results are then cached for following displacements
|
||||||
if (m_sequential_print_clearance_first_displacement) {
|
if (m_sequential_print_clearance_first_displacement) {
|
||||||
m_sequential_print_clearance.m_hull_2d_cache.clear();
|
m_sequential_print_clearance.m_hull_2d_cache.clear();
|
||||||
float shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON));
|
const float shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON));
|
||||||
double mitter_limit = scale_(0.1);
|
const double mitter_limit = scale_(0.1);
|
||||||
m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size());
|
m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size());
|
||||||
for (size_t i = 0; i < m_model->objects.size(); ++i) {
|
for (size_t i = 0; i < m_model->objects.size(); ++i) {
|
||||||
ModelObject* model_object = m_model->objects[i];
|
ModelObject* model_object = m_model->objects[i];
|
||||||
@ -3956,7 +3956,7 @@ void GLCanvas3D::update_sequential_clearance()
|
|||||||
#if ENABLE_WORLD_COORDINATE
|
#if ENABLE_WORLD_COORDINATE
|
||||||
Geometry::Transformation trafo = model_instance0->get_transformation();
|
Geometry::Transformation trafo = model_instance0->get_transformation();
|
||||||
trafo.set_offset({ 0.0, 0.0, model_instance0->get_offset().z() });
|
trafo.set_offset({ 0.0, 0.0, model_instance0->get_offset().z() });
|
||||||
Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()),
|
const Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()),
|
||||||
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
||||||
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||||||
shrink_factor,
|
shrink_factor,
|
||||||
@ -3984,13 +3984,8 @@ void GLCanvas3D::update_sequential_clearance()
|
|||||||
polygons.reserve(instances_count);
|
polygons.reserve(instances_count);
|
||||||
for (size_t i = 0; i < instance_transforms.size(); ++i) {
|
for (size_t i = 0; i < instance_transforms.size(); ++i) {
|
||||||
const auto& instances = instance_transforms[i];
|
const auto& instances = instance_transforms[i];
|
||||||
double rotation_z0 = instances.front()->get_rotation().z();
|
|
||||||
for (const auto& instance : instances) {
|
for (const auto& instance : instances) {
|
||||||
Geometry::Transformation transformation;
|
const Transform3d& trafo = instance->get_matrix();
|
||||||
const Vec3d& offset = instance->get_offset();
|
|
||||||
transformation.set_offset({ offset.x(), offset.y(), 0.0 });
|
|
||||||
transformation.set_rotation(Z, instance->get_rotation().z() - rotation_z0);
|
|
||||||
const Transform3d& trafo = transformation.get_matrix();
|
|
||||||
const Pointf3s& hull_2d = m_sequential_print_clearance.m_hull_2d_cache[i];
|
const Pointf3s& hull_2d = m_sequential_print_clearance.m_hull_2d_cache[i];
|
||||||
Points inst_pts;
|
Points inst_pts;
|
||||||
inst_pts.reserve(hull_2d.size());
|
inst_pts.reserve(hull_2d.size());
|
||||||
@ -7060,7 +7055,7 @@ void GLCanvas3D::GizmoHighlighter::init(GLGizmosManager* manager, GLGizmosManage
|
|||||||
{
|
{
|
||||||
if (m_timer.IsRunning())
|
if (m_timer.IsRunning())
|
||||||
invalidate();
|
invalidate();
|
||||||
if (!gizmo || !canvas)
|
if (gizmo == GLGizmosManager::EType::Undefined || !canvas)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_timer.Start(300, false);
|
m_timer.Start(300, false);
|
||||||
|
@ -79,7 +79,6 @@
|
|||||||
#include "DesktopIntegrationDialog.hpp"
|
#include "DesktopIntegrationDialog.hpp"
|
||||||
#include "SendSystemInfoDialog.hpp"
|
#include "SendSystemInfoDialog.hpp"
|
||||||
#include "Downloader.hpp"
|
#include "Downloader.hpp"
|
||||||
#include "ConfigWizard_private.hpp"
|
|
||||||
|
|
||||||
#include "BitmapCache.hpp"
|
#include "BitmapCache.hpp"
|
||||||
#include "Notebook.hpp"
|
#include "Notebook.hpp"
|
||||||
@ -2890,6 +2889,7 @@ void GUI_App::MacOpenURL(const wxString& url)
|
|||||||
{
|
{
|
||||||
if (app_config && !app_config->get_bool("downloader_url_registered"))
|
if (app_config && !app_config->get_bool("downloader_url_registered"))
|
||||||
{
|
{
|
||||||
|
notification_manager()->push_notification(NotificationType::URLNotRegistered);
|
||||||
BOOST_LOG_TRIVIAL(error) << "Recieved command to open URL, but it is not allowed in app configuration. URL: " << url;
|
BOOST_LOG_TRIVIAL(error) << "Recieved command to open URL, but it is not allowed in app configuration. URL: " << url;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3081,11 +3081,11 @@ void GUI_App::show_downloader_registration_dialog()
|
|||||||
), SLIC3R_APP_NAME, SLIC3R_VERSION)
|
), SLIC3R_APP_NAME, SLIC3R_VERSION)
|
||||||
, true, wxYES_NO);
|
, true, wxYES_NO);
|
||||||
if (msg.ShowModal() == wxID_YES) {
|
if (msg.ShowModal() == wxID_YES) {
|
||||||
auto downloader = new DownloaderUtils::Worker(nullptr);
|
auto downloader_worker = new DownloaderUtils::Worker(nullptr);
|
||||||
downloader->perform_register(app_config->get("url_downloader_dest"));
|
downloader_worker->perform_register(app_config->get("url_downloader_dest"));
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
if (downloader->get_perform_registration_linux())
|
if (downloader_worker->get_perform_registration_linux())
|
||||||
DesktopIntegrationDialog::perform_desktop_integration(true);
|
DesktopIntegrationDialog::perform_downloader_desktop_integration();
|
||||||
#endif // __linux__
|
#endif // __linux__
|
||||||
} else {
|
} else {
|
||||||
app_config->set("downloader_url_registered", "0");
|
app_config->set("downloader_url_registered", "0");
|
||||||
|
@ -448,14 +448,21 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||||||
Selection& selection = canvas->get_selection();
|
Selection& selection = canvas->get_selection();
|
||||||
|
|
||||||
#if ENABLE_WORLD_COORDINATE
|
#if ENABLE_WORLD_COORDINATE
|
||||||
if (selection.is_single_volume_or_modifier())
|
if (selection.is_single_volume_or_modifier()) {
|
||||||
|
GLVolume* vol = const_cast<GLVolume*>(selection.get_first_volume());
|
||||||
|
Geometry::Transformation trafo = vol->get_volume_transformation();
|
||||||
|
trafo.reset_rotation();
|
||||||
|
vol->set_volume_transformation(trafo);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
if (selection.is_single_volume() || selection.is_single_modifier())
|
if (selection.is_single_volume() || selection.is_single_modifier())
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
|
||||||
const_cast<GLVolume*>(selection.get_first_volume())->set_volume_rotation(Vec3d::Zero());
|
const_cast<GLVolume*>(selection.get_first_volume())->set_volume_rotation(Vec3d::Zero());
|
||||||
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
else if (selection.is_single_full_instance()) {
|
else if (selection.is_single_full_instance()) {
|
||||||
|
Geometry::Transformation trafo = selection.get_first_volume()->get_instance_transformation();
|
||||||
|
trafo.reset_rotation();
|
||||||
for (unsigned int idx : selection.get_volume_idxs()) {
|
for (unsigned int idx : selection.get_volume_idxs()) {
|
||||||
const_cast<GLVolume*>(selection.get_volume(idx))->set_instance_rotation(Vec3d::Zero());
|
const_cast<GLVolume*>(selection.get_volume(idx))->set_instance_transformation(trafo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -484,24 +491,22 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||||
Selection& selection = canvas->get_selection();
|
Selection& selection = canvas->get_selection();
|
||||||
if (selection.is_single_volume_or_modifier()) {
|
if (selection.is_single_volume_or_modifier()) {
|
||||||
const bool is_left_handed = selection.get_first_volume()->get_volume_transformation().is_left_handed();
|
GLVolume* vol = const_cast<GLVolume*>(selection.get_first_volume());
|
||||||
const_cast<GLVolume*>(selection.get_first_volume())->set_volume_scaling_factor(Vec3d::Ones());
|
Geometry::Transformation trafo = vol->get_volume_transformation();
|
||||||
if (is_left_handed)
|
trafo.reset_scaling_factor();
|
||||||
const_cast<GLVolume*>(selection.get_first_volume())->set_volume_mirror({ -1.0 , 1.0, 1.0 });
|
vol->set_volume_transformation(trafo);
|
||||||
}
|
}
|
||||||
else if (selection.is_single_full_instance()) {
|
else if (selection.is_single_full_instance()) {
|
||||||
const bool is_left_handed = selection.get_first_volume()->get_instance_transformation().is_left_handed();
|
Geometry::Transformation trafo = selection.get_first_volume()->get_instance_transformation();
|
||||||
|
trafo.reset_scaling_factor();
|
||||||
for (unsigned int idx : selection.get_volume_idxs()) {
|
for (unsigned int idx : selection.get_volume_idxs()) {
|
||||||
const_cast<GLVolume*>(selection.get_volume(idx))->set_instance_scaling_factor(Vec3d::Ones());
|
const_cast<GLVolume*>(selection.get_volume(idx))->set_instance_transformation(trafo);
|
||||||
if (is_left_handed)
|
|
||||||
const_cast<GLVolume*>(selection.get_volume(idx))->set_instance_mirror({ -1.0 , 1.0, 1.0 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
|
||||||
canvas->do_scale(L("Reset scale"));
|
canvas->do_scale(L("Reset scale"));
|
||||||
|
|
||||||
UpdateAndShow(true);
|
UpdateAndShow(true);
|
||||||
#else
|
#else
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset scale"));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset scale"));
|
||||||
@ -740,7 +745,6 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
|
|||||||
m_new_rotate_label_string = L("Rotate (relative)");
|
m_new_rotate_label_string = L("Rotate (relative)");
|
||||||
m_new_position = Vec3d::Zero();
|
m_new_position = Vec3d::Zero();
|
||||||
m_new_rotation = Vec3d::Zero();
|
m_new_rotation = Vec3d::Zero();
|
||||||
m_new_scale = Vec3d(100.0, 100.0, 100.0);
|
|
||||||
m_new_size = selection.get_bounding_box_in_current_reference_system().first.size();
|
m_new_size = selection.get_bounding_box_in_current_reference_system().first.size();
|
||||||
#else
|
#else
|
||||||
m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI);
|
m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI);
|
||||||
@ -927,93 +931,48 @@ void ObjectManipulation::update_if_dirty()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if ENABLE_WORLD_COORDINATE
|
||||||
void ObjectManipulation::update_reset_buttons_visibility()
|
void ObjectManipulation::update_reset_buttons_visibility()
|
||||||
{
|
{
|
||||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||||
if (!canvas)
|
if (!canvas)
|
||||||
return;
|
return;
|
||||||
const Selection& selection = canvas->get_selection();
|
|
||||||
|
|
||||||
|
bool show_drop_to_bed = false;
|
||||||
bool show_rotation = false;
|
bool show_rotation = false;
|
||||||
bool show_scale = false;
|
bool show_scale = false;
|
||||||
bool show_drop_to_bed = false;
|
bool show_mirror = false;
|
||||||
#if ENABLE_WORLD_COORDINATE
|
|
||||||
bool show_skew = false;
|
bool show_skew = false;
|
||||||
bool show_mirror_warning = false;
|
|
||||||
|
|
||||||
|
const Selection& selection = canvas->get_selection();
|
||||||
if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) {
|
if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) {
|
||||||
const double min_z = selection.is_single_full_instance() ? selection.get_scaled_instance_bounding_box().min.z() :
|
const double min_z = selection.is_single_full_instance() ? selection.get_scaled_instance_bounding_box().min.z() :
|
||||||
get_volume_min_z(*selection.get_first_volume());
|
get_volume_min_z(*selection.get_first_volume());
|
||||||
|
|
||||||
show_drop_to_bed = std::abs(min_z) > EPSILON;
|
show_drop_to_bed = std::abs(min_z) > EPSILON;
|
||||||
const GLVolume* volume = selection.get_first_volume();
|
const GLVolume* volume = selection.get_first_volume();
|
||||||
Geometry::Transformation trafo;
|
const Geometry::Transformation trafo = selection.is_single_full_instance() ? volume->get_instance_transformation() : volume->get_volume_transformation();
|
||||||
#else
|
|
||||||
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
|
|
||||||
const GLVolume* volume = selection.get_first_volume();
|
|
||||||
Vec3d rotation;
|
|
||||||
Vec3d scale;
|
|
||||||
double min_z = 0.0;
|
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
|
||||||
|
|
||||||
if (selection.is_single_full_instance()) {
|
const Geometry::TransformationSVD trafo_svd(trafo);
|
||||||
#if ENABLE_WORLD_COORDINATE
|
show_rotation = trafo_svd.rotation;
|
||||||
trafo = volume->get_instance_transformation();
|
show_scale = trafo_svd.scale;
|
||||||
const Selection::IndicesList& idxs = selection.get_volume_idxs();
|
show_mirror = trafo_svd.mirror;
|
||||||
for (unsigned int id : idxs) {
|
show_skew = trafo_svd.skew;
|
||||||
const Geometry::Transformation world_trafo(selection.get_volume(id)->world_matrix());
|
|
||||||
show_skew |= world_trafo.has_skew();
|
|
||||||
show_mirror_warning |= world_trafo.get_matrix().matrix().determinant() < 0.0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
rotation = volume->get_instance_rotation();
|
|
||||||
scale = volume->get_instance_scaling_factor();
|
|
||||||
min_z = selection.get_scaled_instance_bounding_box().min.z();
|
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
#if ENABLE_WORLD_COORDINATE
|
|
||||||
Geometry::Transformation trafo = volume->get_volume_transformation();
|
|
||||||
const Geometry::Transformation world_trafo(volume->world_matrix());
|
|
||||||
show_skew |= world_trafo.has_skew();
|
|
||||||
show_mirror_warning |= world_trafo.get_matrix().matrix().determinant() < 0.0;
|
|
||||||
#else
|
|
||||||
rotation = volume->get_volume_rotation();
|
|
||||||
scale = volume->get_volume_scaling_factor();
|
|
||||||
min_z = get_volume_min_z(*volume);
|
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
|
||||||
}
|
|
||||||
#if ENABLE_WORLD_COORDINATE
|
|
||||||
const Transform3d rotation = trafo.get_rotation_matrix();
|
|
||||||
const Transform3d scale = trafo.get_scaling_factor_matrix();
|
|
||||||
show_rotation = show_mirror_warning ? !trafo.get_matrix().matrix().block<3, 3>(0, 0).isDiagonal() : !rotation.isApprox(Transform3d::Identity());
|
|
||||||
show_scale = !scale.isApprox(Transform3d::Identity());
|
|
||||||
#else
|
|
||||||
show_rotation = !rotation.isApprox(Vec3d::Zero());
|
|
||||||
show_scale = !scale.isApprox(Vec3d::Ones());
|
|
||||||
show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD;
|
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_WORLD_COORDINATE
|
wxGetApp().CallAfter([this, show_drop_to_bed, show_rotation, show_scale, show_mirror, show_skew] {
|
||||||
wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed, show_skew, show_mirror_warning] {
|
|
||||||
#else
|
|
||||||
wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] {
|
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
|
||||||
// There is a case (under OSX), when this function is called after the Manipulation panel is hidden
|
// There is a case (under OSX), when this function is called after the Manipulation panel is hidden
|
||||||
// So, let check if Manipulation panel is still shown for this moment
|
// So, let check if Manipulation panel is still shown for this moment
|
||||||
if (!this->IsShown())
|
if (!this->IsShown())
|
||||||
return;
|
return;
|
||||||
|
m_drop_to_bed_button->Show(show_drop_to_bed);
|
||||||
m_reset_rotation_button->Show(show_rotation);
|
m_reset_rotation_button->Show(show_rotation);
|
||||||
m_reset_scale_button->Show(show_scale);
|
m_reset_scale_button->Show(show_scale);
|
||||||
m_drop_to_bed_button->Show(show_drop_to_bed);
|
m_mirror_warning_bitmap->SetBitmap(show_mirror ? m_manifold_warning_bmp.bmp() : wxNullBitmap);
|
||||||
#if ENABLE_WORLD_COORDINATE
|
m_mirror_warning_bitmap->SetMinSize(show_mirror ? m_manifold_warning_bmp.GetSize() : wxSize(0, 0));
|
||||||
|
m_mirror_warning_bitmap->SetToolTip(show_mirror ? _L("Left handed") : "");
|
||||||
m_reset_skew_button->Show(show_skew);
|
m_reset_skew_button->Show(show_skew);
|
||||||
m_skew_label->Show(show_skew);
|
m_skew_label->Show(show_skew);
|
||||||
m_mirror_warning_bitmap->SetBitmap(show_mirror_warning ? m_manifold_warning_bmp.bmp() : wxNullBitmap);
|
|
||||||
m_mirror_warning_bitmap->SetMinSize(show_mirror_warning ? m_manifold_warning_bmp.GetSize() : wxSize(0, 0));
|
|
||||||
m_mirror_warning_bitmap->SetToolTip(show_mirror_warning ? _L("Left handed") : "");
|
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
|
||||||
|
|
||||||
// Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time
|
// Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time
|
||||||
Sidebar& panel = wxGetApp().sidebar();
|
Sidebar& panel = wxGetApp().sidebar();
|
||||||
@ -1024,23 +983,75 @@ void ObjectManipulation::update_reset_buttons_visibility()
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
void ObjectManipulation::update_reset_buttons_visibility()
|
||||||
|
{
|
||||||
|
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||||
|
if (!canvas)
|
||||||
|
return;
|
||||||
|
const Selection& selection = canvas->get_selection();
|
||||||
|
|
||||||
|
bool show_rotation = false;
|
||||||
|
bool show_scale = false;
|
||||||
|
bool show_drop_to_bed = false;
|
||||||
|
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
|
||||||
|
const GLVolume* volume = selection.get_first_volume();
|
||||||
|
Vec3d rotation;
|
||||||
|
Vec3d scale;
|
||||||
|
double min_z = 0.0;
|
||||||
|
|
||||||
|
if (selection.is_single_full_instance()) {
|
||||||
|
rotation = volume->get_instance_rotation();
|
||||||
|
scale = volume->get_instance_scaling_factor();
|
||||||
|
min_z = selection.get_scaled_instance_bounding_box().min.z();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rotation = volume->get_volume_rotation();
|
||||||
|
scale = volume->get_volume_scaling_factor();
|
||||||
|
min_z = get_volume_min_z(*volume);
|
||||||
|
}
|
||||||
|
show_rotation = !rotation.isApprox(Vec3d::Zero());
|
||||||
|
show_scale = !scale.isApprox(Vec3d::Ones());
|
||||||
|
show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] {
|
||||||
|
// There is a case (under OSX), when this function is called after the Manipulation panel is hidden
|
||||||
|
// So, let check if Manipulation panel is still shown for this moment
|
||||||
|
if (!this->IsShown())
|
||||||
|
return;
|
||||||
|
m_reset_rotation_button->Show(show_rotation);
|
||||||
|
m_reset_scale_button->Show(show_scale);
|
||||||
|
m_drop_to_bed_button->Show(show_drop_to_bed);
|
||||||
|
|
||||||
|
// Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time
|
||||||
|
Sidebar& panel = wxGetApp().sidebar();
|
||||||
|
if (!panel.IsFrozen()) {
|
||||||
|
panel.Freeze();
|
||||||
|
panel.Layout();
|
||||||
|
panel.Thaw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ObjectManipulation::update_mirror_buttons_visibility()
|
void ObjectManipulation::update_mirror_buttons_visibility()
|
||||||
{
|
{
|
||||||
|
#if ENABLE_WORLD_COORDINATE
|
||||||
|
const bool can_mirror = wxGetApp().plater()->can_mirror();
|
||||||
|
for (ScalableButton* button : m_mirror_buttons) {
|
||||||
|
button->Enable(can_mirror);
|
||||||
|
}
|
||||||
|
#else
|
||||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||||
Selection& selection = canvas->get_selection();
|
Selection& selection = canvas->get_selection();
|
||||||
|
|
||||||
#if ENABLE_WORLD_COORDINATE
|
|
||||||
if (is_local_coordinates()) {
|
|
||||||
if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) {
|
|
||||||
#else
|
|
||||||
std::array<MirrorButtonState, 3> new_states = { mbHidden, mbHidden, mbHidden };
|
std::array<MirrorButtonState, 3> new_states = { mbHidden, mbHidden, mbHidden };
|
||||||
|
|
||||||
if (!m_world_coordinates) {
|
if (!m_world_coordinates) {
|
||||||
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
|
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
|
||||||
const GLVolume* volume = selection.get_first_volume();
|
const GLVolume* volume = selection.get_first_volume();
|
||||||
Vec3d mirror;
|
Vec3d mirror;
|
||||||
|
|
||||||
@ -1049,19 +1060,10 @@ void ObjectManipulation::update_mirror_buttons_visibility()
|
|||||||
else
|
else
|
||||||
mirror = volume->get_volume_mirror();
|
mirror = volume->get_volume_mirror();
|
||||||
|
|
||||||
#if !ENABLE_WORLD_COORDINATE
|
|
||||||
for (unsigned char i=0; i<3; ++i)
|
for (unsigned char i=0; i<3; ++i)
|
||||||
new_states[i] = (mirror[i] < 0. ? mbActive : mbShown);
|
new_states[i] = (mirror[i] < 0. ? mbActive : mbShown);
|
||||||
#endif // !ENABLE_WORLD_COORDINATE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_WORLD_COORDINATE
|
|
||||||
const bool can_mirror = wxGetApp().plater()->can_mirror();
|
|
||||||
for (ScalableButton* button : m_mirror_buttons) {
|
|
||||||
button->Enable(can_mirror);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
else {
|
else {
|
||||||
// the mirroring buttons should be hidden in world coordinates,
|
// the mirroring buttons should be hidden in world coordinates,
|
||||||
// unless we make it actually mirror in world coords.
|
// unless we make it actually mirror in world coords.
|
||||||
|
@ -198,16 +198,23 @@ void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoBase::render_grabbers(float size) const
|
void GLGizmoBase::render_grabbers(float size) const
|
||||||
|
{
|
||||||
|
render_grabbers(0, m_grabbers.size() - 1, size, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLGizmoBase::render_grabbers(size_t first, size_t last, float size, bool force_hover) const
|
||||||
{
|
{
|
||||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||||
if (shader == nullptr)
|
if (shader == nullptr)
|
||||||
return;
|
return;
|
||||||
shader->start_using();
|
shader->start_using();
|
||||||
shader->set_uniform("emission_factor", 0.1f);
|
shader->set_uniform("emission_factor", 0.1f);
|
||||||
for (int i = 0; i < (int)m_grabbers.size(); ++i) {
|
glsafe(::glDisable(GL_CULL_FACE));
|
||||||
|
for (size_t i = first; i <= last; ++i) {
|
||||||
if (m_grabbers[i].enabled)
|
if (m_grabbers[i].enabled)
|
||||||
m_grabbers[i].render(m_hover_id == i, size);
|
m_grabbers[i].render(force_hover ? true : m_hover_id == (int)i, size);
|
||||||
}
|
}
|
||||||
|
glsafe(::glEnable(GL_CULL_FACE));
|
||||||
shader->stop_using();
|
shader->stop_using();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,6 +219,7 @@ protected:
|
|||||||
|
|
||||||
void render_grabbers(const BoundingBoxf3& box) const;
|
void render_grabbers(const BoundingBoxf3& box) const;
|
||||||
void render_grabbers(float size) const;
|
void render_grabbers(float size) const;
|
||||||
|
void render_grabbers(size_t first, size_t last, float size, bool force_hover) const;
|
||||||
|
|
||||||
std::string format(float value, unsigned int decimals) const;
|
std::string format(float value, unsigned int decimals) const;
|
||||||
|
|
||||||
|
@ -244,7 +244,7 @@ std::string GLGizmoCut3D::get_tooltip() const
|
|||||||
if (m_hover_id == Z || (m_dragging && m_hover_id == CutPlane)) {
|
if (m_hover_id == Z || (m_dragging && m_hover_id == CutPlane)) {
|
||||||
double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0;
|
double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0;
|
||||||
std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm"));
|
std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm"));
|
||||||
const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center);
|
const BoundingBoxf3& tbb = m_transformed_bounding_box;
|
||||||
if (tbb.max.z() >= 0.0) {
|
if (tbb.max.z() >= 0.0) {
|
||||||
double top = (tbb.min.z() <= 0.0 ? tbb.max.z() : tbb.size().z()) * koef;
|
double top = (tbb.min.z() <= 0.0 ? tbb.max.z() : tbb.size().z()) * koef;
|
||||||
tooltip += format(top, 2) + " " + unit_str + " (" + _u8L("Top part") + ")";
|
tooltip += format(top, 2) + " " + unit_str + " (" + _u8L("Top part") + ")";
|
||||||
@ -401,7 +401,7 @@ bool GLGizmoCut3D::is_looking_forward() const
|
|||||||
|
|
||||||
void GLGizmoCut3D::update_clipper()
|
void GLGizmoCut3D::update_clipper()
|
||||||
{
|
{
|
||||||
BoundingBoxf3 box = bounding_box();
|
BoundingBoxf3 box = m_bounding_box;
|
||||||
|
|
||||||
// update cut_normal
|
// update cut_normal
|
||||||
Vec3d beg, end = beg = m_plane_center;
|
Vec3d beg, end = beg = m_plane_center;
|
||||||
@ -549,7 +549,7 @@ bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& v
|
|||||||
return !is_approx(old_val, value);
|
return !is_approx(old_val, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const BoundingBoxf3 bbox = bounding_box();
|
const BoundingBoxf3 bbox = m_bounding_box;
|
||||||
const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0) * (m_imperial_units ? f_mm_to_in : 1.f);
|
const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0) * (m_imperial_units ? f_mm_to_in : 1.f);
|
||||||
|
|
||||||
ImGuiWrapper::text(label);
|
ImGuiWrapper::text(label);
|
||||||
@ -795,7 +795,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers()
|
|||||||
|
|
||||||
const Transform3d view_matrix = wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m;
|
const Transform3d view_matrix = wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m;
|
||||||
|
|
||||||
const double mean_size = get_grabber_mean_size(bounding_box());
|
const double mean_size = get_grabber_mean_size(m_bounding_box);
|
||||||
double size;
|
double size;
|
||||||
|
|
||||||
const bool dragging_by_cut_plane = m_dragging && m_hover_id == CutPlane;
|
const bool dragging_by_cut_plane = m_dragging && m_hover_id == CutPlane;
|
||||||
@ -1033,7 +1033,7 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform()
|
|||||||
else if (!cut_line_processing()){
|
else if (!cut_line_processing()){
|
||||||
const Transform3d trafo = translation_transform(m_plane_center) * m_rotation_m;
|
const Transform3d trafo = translation_transform(m_plane_center) * m_rotation_m;
|
||||||
|
|
||||||
const BoundingBoxf3 box = bounding_box();
|
const BoundingBoxf3 box = m_bounding_box;
|
||||||
|
|
||||||
const double size = get_half_size(get_grabber_mean_size(box));
|
const double size = get_half_size(get_grabber_mean_size(box));
|
||||||
Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size);
|
Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size);
|
||||||
@ -1127,14 +1127,14 @@ void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data)
|
|||||||
|
|
||||||
Vec3d starting_vec = m_rotation_m * Vec3d::UnitZ();
|
Vec3d starting_vec = m_rotation_m * Vec3d::UnitZ();
|
||||||
if (starting_vec.norm() != 0.0) {
|
if (starting_vec.norm() != 0.0) {
|
||||||
Vec3d mouse_dir = data.mouse_ray.unit_vector();
|
const Vec3d mouse_dir = data.mouse_ray.unit_vector();
|
||||||
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
|
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
|
||||||
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
|
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
|
||||||
// in our case plane normal and ray direction are the same (orthogonal view)
|
// in our case plane normal and ray direction are the same (orthogonal view)
|
||||||
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
|
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
|
||||||
Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
|
const Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir;
|
||||||
// vector from the starting position to the found intersection
|
// vector from the starting position to the found intersection
|
||||||
Vec3d inters_vec = inters - starting_drag_position;
|
const Vec3d inters_vec = inters - starting_drag_position;
|
||||||
|
|
||||||
starting_vec.normalize();
|
starting_vec.normalize();
|
||||||
// finds projection of the vector along the staring direction
|
// finds projection of the vector along the staring direction
|
||||||
@ -1183,7 +1183,11 @@ void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data)
|
|||||||
|
|
||||||
Vec3d rotation = Vec3d::Zero();
|
Vec3d rotation = Vec3d::Zero();
|
||||||
rotation[m_hover_id] = theta;
|
rotation[m_hover_id] = theta;
|
||||||
m_rotation_m = m_start_dragging_m * rotation_transform(rotation);
|
|
||||||
|
const Transform3d rotation_tmp = m_start_dragging_m * rotation_transform(rotation);
|
||||||
|
if (m_rotation_m.rotation() != rotation_tmp.rotation())
|
||||||
|
m_transformed_bounding_box = transformed_bounding_box(m_plane_center);
|
||||||
|
m_rotation_m = rotation_tmp;
|
||||||
|
|
||||||
m_angle = theta;
|
m_angle = theta;
|
||||||
while (m_angle > two_pi)
|
while (m_angle > two_pi)
|
||||||
@ -1245,9 +1249,13 @@ void GLGizmoCut3D::on_stop_dragging()
|
|||||||
|
|
||||||
void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false*/)
|
void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false*/)
|
||||||
{
|
{
|
||||||
|
if (m_plane_center == center_pos)
|
||||||
|
return;
|
||||||
|
|
||||||
bool can_set_center_pos = force;
|
bool can_set_center_pos = force;
|
||||||
|
BoundingBoxf3 tbb;
|
||||||
if (!can_set_center_pos) {
|
if (!can_set_center_pos) {
|
||||||
const BoundingBoxf3 tbb = transformed_bounding_box(center_pos);
|
tbb = transformed_bounding_box(center_pos);
|
||||||
if (tbb.max.z() > -1. && tbb.min.z() < 1.)
|
if (tbb.max.z() > -1. && tbb.min.z() < 1.)
|
||||||
can_set_center_pos = true;
|
can_set_center_pos = true;
|
||||||
else {
|
else {
|
||||||
@ -1260,6 +1268,7 @@ void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false*
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (can_set_center_pos) {
|
if (can_set_center_pos) {
|
||||||
|
m_transformed_bounding_box = tbb;
|
||||||
m_plane_center = center_pos;
|
m_plane_center = center_pos;
|
||||||
m_center_offset = m_plane_center - m_bb_center;
|
m_center_offset = m_plane_center - m_bb_center;
|
||||||
}
|
}
|
||||||
@ -1279,7 +1288,7 @@ BoundingBoxf3 GLGizmoCut3D::bounding_box() const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center, bool revert_move /*= false*/) const
|
BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center) const
|
||||||
{
|
{
|
||||||
// #ysFIXME !!!
|
// #ysFIXME !!!
|
||||||
BoundingBoxf3 ret;
|
BoundingBoxf3 ret;
|
||||||
@ -1299,10 +1308,7 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center,
|
|||||||
Vec3d cut_center_offset = plane_center - instance_offset;
|
Vec3d cut_center_offset = plane_center - instance_offset;
|
||||||
cut_center_offset[Z] -= sel_info->get_sla_shift();
|
cut_center_offset[Z] -= sel_info->get_sla_shift();
|
||||||
|
|
||||||
const auto move = translation_transform(-cut_center_offset);
|
const auto cut_matrix = Transform3d::Identity() * m_rotation_m.inverse() * translation_transform(-cut_center_offset);
|
||||||
const auto move2 = translation_transform(plane_center);
|
|
||||||
|
|
||||||
const auto cut_matrix = (revert_move ? move2 : Transform3d::Identity()) * m_rotation_m.inverse() * move;
|
|
||||||
|
|
||||||
const Selection& selection = m_parent.get_selection();
|
const Selection& selection = m_parent.get_selection();
|
||||||
const Selection::IndicesList& idxs = selection.get_volume_idxs();
|
const Selection::IndicesList& idxs = selection.get_volume_idxs();
|
||||||
@ -1335,6 +1341,8 @@ bool GLGizmoCut3D::update_bb()
|
|||||||
const BoundingBoxf3 box = bounding_box();
|
const BoundingBoxf3 box = bounding_box();
|
||||||
if (m_max_pos != box.max || m_min_pos != box.min) {
|
if (m_max_pos != box.max || m_min_pos != box.min) {
|
||||||
|
|
||||||
|
m_bounding_box = box;
|
||||||
|
|
||||||
invalidate_cut_plane();
|
invalidate_cut_plane();
|
||||||
|
|
||||||
m_max_pos = box.max;
|
m_max_pos = box.max;
|
||||||
@ -1388,7 +1396,7 @@ void GLGizmoCut3D::init_picking_models()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!m_plane.model.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) {
|
if (!m_plane.model.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) {
|
||||||
const double cp_width = 0.02 * get_grabber_mean_size(bounding_box());
|
const double cp_width = 0.02 * get_grabber_mean_size(m_bounding_box);
|
||||||
indexed_triangle_set its = its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4);
|
indexed_triangle_set its = its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4);
|
||||||
m_plane.model.init_from(its);
|
m_plane.model.init_from(its);
|
||||||
m_plane.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
|
m_plane.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
|
||||||
@ -1631,9 +1639,8 @@ void GLGizmoCut3D::render_build_size()
|
|||||||
{
|
{
|
||||||
double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0;
|
double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0;
|
||||||
wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm"));
|
wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm"));
|
||||||
const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center);
|
|
||||||
|
|
||||||
Vec3d tbb_sz = tbb.size();
|
Vec3d tbb_sz = m_transformed_bounding_box.size();
|
||||||
wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str +
|
wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str +
|
||||||
", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str +
|
", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str +
|
||||||
", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str;
|
", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str;
|
||||||
@ -1646,7 +1653,7 @@ void GLGizmoCut3D::render_build_size()
|
|||||||
|
|
||||||
void GLGizmoCut3D::reset_cut_plane()
|
void GLGizmoCut3D::reset_cut_plane()
|
||||||
{
|
{
|
||||||
set_center(bounding_box().center());
|
set_center(m_bb_center);
|
||||||
m_rotation_m = Transform3d::Identity();
|
m_rotation_m = Transform3d::Identity();
|
||||||
m_angle_arc.reset();
|
m_angle_arc.reset();
|
||||||
update_clipper();
|
update_clipper();
|
||||||
@ -1746,7 +1753,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
|
|||||||
|
|
||||||
const bool has_connectors = !connectors.empty();
|
const bool has_connectors = !connectors.empty();
|
||||||
|
|
||||||
const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && bounding_box().center() == m_plane_center;
|
const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_center == m_plane_center;
|
||||||
m_imgui->disabled_begin(is_cut_plane_init);
|
m_imgui->disabled_begin(is_cut_plane_init);
|
||||||
if (render_reset_button("cut_plane", _u8L("Reset cutting plane")))
|
if (render_reset_button("cut_plane", _u8L("Reset cutting plane")))
|
||||||
reset_cut_plane();
|
reset_cut_plane();
|
||||||
@ -1838,10 +1845,9 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
|
|||||||
add_vertical_scaled_interval(0.75f);
|
add_vertical_scaled_interval(0.75f);
|
||||||
|
|
||||||
m_imgui->disabled_begin(has_connectors);
|
m_imgui->disabled_begin(has_connectors);
|
||||||
add_horizontal_shift(m_imgui->scaled(/*1*/.2f));
|
|
||||||
ImGuiWrapper::text(_L("Cut to") + ":");
|
ImGuiWrapper::text(_L("Cut to") + ":");
|
||||||
|
|
||||||
ImGui::SameLine();
|
add_horizontal_scaled_interval(1.2f);
|
||||||
if (m_imgui->radio_button(_L("Objects"), !m_keep_as_parts))
|
if (m_imgui->radio_button(_L("Objects"), !m_keep_as_parts))
|
||||||
m_keep_as_parts = false;
|
m_keep_as_parts = false;
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@ -1990,38 +1996,6 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
|
|||||||
render_debug_input_window(x);
|
render_debug_input_window(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get volume transformation regarding to the "border". Border is related from the size of connectors
|
|
||||||
Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) const
|
|
||||||
{
|
|
||||||
bool is_prizm_dowel = m_connector_type == CutConnectorType::Dowel && m_connector_style == size_t(CutConnectorStyle::Prizm);
|
|
||||||
#if ENABLE_WORLD_COORDINATE
|
|
||||||
const Transform3d connector_trafo = is_prizm_dowel ?
|
|
||||||
Geometry::translation_transform(-m_connector_depth_ratio * Vec3d::UnitZ()) * m_rotation_m * Geometry::scale_transform({ 0.5 * m_connector_size, 0.5 * m_connector_size, 2 * m_connector_depth_ratio }) :
|
|
||||||
m_rotation_m * Geometry::scale_transform({ 0.5 * m_connector_size, 0.5 * m_connector_size, m_connector_depth_ratio });
|
|
||||||
|
|
||||||
#else
|
|
||||||
const Transform3d connector_trafo = assemble_transform(
|
|
||||||
is_prizm_dowel ? Vec3d(0.0, 0.0, -m_connector_depth_ratio) : Vec3d::Zero(),
|
|
||||||
Transformation(m_rotation_m).get_rotation(),
|
|
||||||
Vec3d(0.5*m_connector_size, 0.5*m_connector_size, is_prizm_dowel ? 2 * m_connector_depth_ratio : m_connector_depth_ratio),
|
|
||||||
Vec3d::Ones());
|
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
|
||||||
const Vec3d connector_bb = m_connector_mesh.transformed_bounding_box(connector_trafo).size();
|
|
||||||
|
|
||||||
const Vec3d bb = volume->mesh().bounding_box().size();
|
|
||||||
|
|
||||||
// calculate an unused border - part of the the volume, where we can't put connectors
|
|
||||||
const Vec3d border_scale(connector_bb.x() / bb.x(), connector_bb.y() / bb.y(), connector_bb.z() / bb.z());
|
|
||||||
|
|
||||||
const Transform3d vol_matrix = volume->get_matrix();
|
|
||||||
const Vec3d vol_trans = vol_matrix.translation();
|
|
||||||
// offset of the volume will be changed after scaling, so calculate the needed offset and set it to a volume_trafo
|
|
||||||
const Vec3d offset(vol_trans.x() * border_scale.x(), vol_trans.y() * border_scale.y(), vol_trans.z() * border_scale.z());
|
|
||||||
|
|
||||||
// scale and translate volume to suppress to put connectors too close to the border
|
|
||||||
return translation_transform(offset) * scale_transform(Vec3d::Ones() - border_scale) * vol_matrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos)
|
bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos)
|
||||||
{
|
{
|
||||||
// check if connector pos is out of clipping plane
|
// check if connector pos is out of clipping plane
|
||||||
@ -2072,7 +2046,7 @@ bool GLGizmoCut3D::is_conflict_for_connector(size_t idx, const CutConnectors& co
|
|||||||
const BoundingBoxf3 cur_tbb = m_shapes[cur_connector.attribs].model.get_bounding_box().transformed(matrix);
|
const BoundingBoxf3 cur_tbb = m_shapes[cur_connector.attribs].model.get_bounding_box().transformed(matrix);
|
||||||
|
|
||||||
// check if connector's bounding box is inside the object's bounding box
|
// check if connector's bounding box is inside the object's bounding box
|
||||||
if (!bounding_box().contains(cur_tbb)) {
|
if (!m_bounding_box.contains(cur_tbb)) {
|
||||||
m_info_stats.outside_bb++;
|
m_info_stats.outside_bb++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,9 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
Vec3d m_bb_center{ Vec3d::Zero() };
|
Vec3d m_bb_center{ Vec3d::Zero() };
|
||||||
Vec3d m_center_offset{ Vec3d::Zero() };
|
Vec3d m_center_offset{ Vec3d::Zero() };
|
||||||
|
|
||||||
|
BoundingBoxf3 m_bounding_box;
|
||||||
|
BoundingBoxf3 m_transformed_bounding_box;
|
||||||
|
|
||||||
// values from RotationGizmo
|
// values from RotationGizmo
|
||||||
double m_radius{ 0.0 };
|
double m_radius{ 0.0 };
|
||||||
double m_grabber_radius{ 0.0 };
|
double m_grabber_radius{ 0.0 };
|
||||||
@ -193,7 +196,7 @@ public:
|
|||||||
void invalidate_cut_plane();
|
void invalidate_cut_plane();
|
||||||
|
|
||||||
BoundingBoxf3 bounding_box() const;
|
BoundingBoxf3 bounding_box() const;
|
||||||
BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center, bool revert_move = false) const;
|
BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool on_init() override;
|
bool on_init() override;
|
||||||
@ -263,7 +266,6 @@ private:
|
|||||||
void render_connect_mode_radio_button(CutConnectorMode mode);
|
void render_connect_mode_radio_button(CutConnectorMode mode);
|
||||||
bool render_reset_button(const std::string& label_id, const std::string& tooltip) const;
|
bool render_reset_button(const std::string& label_id, const std::string& tooltip) const;
|
||||||
bool render_connect_type_radio_button(CutConnectorType type);
|
bool render_connect_type_radio_button(CutConnectorType type);
|
||||||
Transform3d get_volume_transformation(const ModelVolume* volume) const;
|
|
||||||
bool is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
|
bool is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
|
||||||
bool is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
|
bool is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
|
||||||
void render_connectors();
|
void render_connectors();
|
||||||
|
@ -519,6 +519,13 @@ bool GLGizmoFdmSupports::has_backend_supports()
|
|||||||
|
|
||||||
void GLGizmoFdmSupports::auto_generate()
|
void GLGizmoFdmSupports::auto_generate()
|
||||||
{
|
{
|
||||||
|
std::string err = wxGetApp().plater()->fff_print().validate();
|
||||||
|
if (!err.empty()) {
|
||||||
|
MessageDialog dlg(GUI::wxGetApp().plater(), _L("Automatic painting requires valid print setup. \n") + from_u8(err), _L("Warning"), wxOK);
|
||||||
|
dlg.ShowModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ModelObject *mo = m_c->selection_info()->model_object();
|
ModelObject *mo = m_c->selection_info()->model_object();
|
||||||
bool not_painted = std::all_of(mo->volumes.begin(), mo->volumes.end(), [](const ModelVolume* vol){
|
bool not_painted = std::all_of(mo->volumes.begin(), mo->volumes.end(), [](const ModelVolume* vol){
|
||||||
return vol->type() != ModelVolumeType::MODEL_PART || vol->supported_facets.empty();
|
return vol->type() != ModelVolumeType::MODEL_PART || vol->supported_facets.empty();
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||||
#include "slic3r/GUI/GUI_App.hpp"
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
#include "slic3r/GUI/Plater.hpp"
|
#include "slic3r/GUI/Plater.hpp"
|
||||||
|
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
|
||||||
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
|
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
|
||||||
|
|
||||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||||
@ -38,6 +38,7 @@ bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event)
|
|||||||
// Rotate the object so the normal points downward:
|
// Rotate the object so the normal points downward:
|
||||||
selection.flattening_rotate(m_planes[m_hover_id].normal);
|
selection.flattening_rotate(m_planes[m_hover_id].normal);
|
||||||
m_parent.do_rotate(L("Gizmo-Place on Face"));
|
m_parent.do_rotate(L("Gizmo-Place on Face"));
|
||||||
|
wxGetApp().obj_manipul()->set_dirty();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -339,7 +339,7 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const
|
|||||||
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
|
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
|
||||||
// in our case plane normal and ray direction are the same (orthogonal view)
|
// in our case plane normal and ray direction are the same (orthogonal view)
|
||||||
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
|
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
|
||||||
const Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
|
const Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir;
|
||||||
// vector from the starting position to the found intersection
|
// vector from the starting position to the found intersection
|
||||||
const Vec3d inters_vec = inters - m_starting_drag_position;
|
const Vec3d inters_vec = inters - m_starting_drag_position;
|
||||||
|
|
||||||
|
@ -238,7 +238,13 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection)
|
|||||||
selection.get_bounding_box_in_reference_system(ECoordinatesType::Local) : selection.get_bounding_box_in_current_reference_system();
|
selection.get_bounding_box_in_reference_system(ECoordinatesType::Local) : selection.get_bounding_box_in_current_reference_system();
|
||||||
m_bounding_box = box;
|
m_bounding_box = box;
|
||||||
m_center = box_trafo.translation();
|
m_center = box_trafo.translation();
|
||||||
m_orient_matrix = box_trafo;
|
m_orient_matrix = Geometry::translation_transform(m_center);
|
||||||
|
if (!wxGetApp().obj_manipul()->is_world_coordinates()) {
|
||||||
|
const GLVolume& v = *selection.get_first_volume();
|
||||||
|
m_orient_matrix = m_orient_matrix * v.get_instance_transformation().get_rotation_matrix();
|
||||||
|
if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates())
|
||||||
|
m_orient_matrix = m_orient_matrix * v.get_volume_transformation().get_rotation_matrix();
|
||||||
|
}
|
||||||
|
|
||||||
m_radius = Offset + m_bounding_box.radius();
|
m_radius = Offset + m_bounding_box.radius();
|
||||||
m_snap_coarse_in_radius = m_radius / 3.0f;
|
m_snap_coarse_in_radius = m_radius / 3.0f;
|
||||||
|
@ -218,15 +218,7 @@ void GLGizmoScale3D::on_render()
|
|||||||
m_bounding_box = box;
|
m_bounding_box = box;
|
||||||
m_center = box_trafo.translation();
|
m_center = box_trafo.translation();
|
||||||
m_grabbers_transform = box_trafo;
|
m_grabbers_transform = box_trafo;
|
||||||
m_instance_center = Vec3d::Zero();
|
m_instance_center = (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) ? selection.get_first_volume()->get_instance_offset() : m_center;
|
||||||
if (selection.is_single_full_instance() && !wxGetApp().obj_manipul()->is_world_coordinates())
|
|
||||||
m_instance_center = selection.get_first_volume()->get_instance_offset();
|
|
||||||
else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_instance_coordinates())
|
|
||||||
m_instance_center = m_center;
|
|
||||||
else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates())
|
|
||||||
m_instance_center = m_center;
|
|
||||||
else
|
|
||||||
m_instance_center = selection.is_single_full_instance() ? selection.get_first_volume()->get_instance_offset() : m_center;
|
|
||||||
|
|
||||||
// x axis
|
// x axis
|
||||||
const Vec3d box_half_size = 0.5 * m_bounding_box.size();
|
const Vec3d box_half_size = 0.5 * m_bounding_box.size();
|
||||||
@ -264,9 +256,8 @@ void GLGizmoScale3D::on_render()
|
|||||||
#endif // ENABLE_GL_CORE_PROFILE
|
#endif // ENABLE_GL_CORE_PROFILE
|
||||||
glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
|
glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
|
||||||
|
|
||||||
const Transform3d base_matrix = local_transform(selection);
|
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
m_grabbers[i].matrix = base_matrix;
|
m_grabbers[i].matrix = m_grabbers_transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float grabber_mean_size = (float)((m_bounding_box.size().x() + m_bounding_box.size().y() + m_bounding_box.size().z()) / 3.0);
|
const float grabber_mean_size = (float)((m_bounding_box.size().x() + m_bounding_box.size().y() + m_bounding_box.size().z()) / 3.0);
|
||||||
@ -281,7 +272,7 @@ void GLGizmoScale3D::on_render()
|
|||||||
if (shader != nullptr) {
|
if (shader != nullptr) {
|
||||||
shader->start_using();
|
shader->start_using();
|
||||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix);
|
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform);
|
||||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||||
#if ENABLE_GL_CORE_PROFILE
|
#if ENABLE_GL_CORE_PROFILE
|
||||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||||
@ -315,7 +306,7 @@ void GLGizmoScale3D::on_render()
|
|||||||
if (shader != nullptr) {
|
if (shader != nullptr) {
|
||||||
shader->start_using();
|
shader->start_using();
|
||||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix);
|
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform);
|
||||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||||
#if ENABLE_GL_CORE_PROFILE
|
#if ENABLE_GL_CORE_PROFILE
|
||||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||||
@ -332,8 +323,7 @@ void GLGizmoScale3D::on_render()
|
|||||||
if (shader != nullptr) {
|
if (shader != nullptr) {
|
||||||
shader->start_using();
|
shader->start_using();
|
||||||
shader->set_uniform("emission_factor", 0.1f);
|
shader->set_uniform("emission_factor", 0.1f);
|
||||||
m_grabbers[0].render(true, grabber_mean_size);
|
render_grabbers(0, 1, grabber_mean_size, true);
|
||||||
m_grabbers[1].render(true, grabber_mean_size);
|
|
||||||
shader->stop_using();
|
shader->stop_using();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -347,7 +337,7 @@ void GLGizmoScale3D::on_render()
|
|||||||
if (shader != nullptr) {
|
if (shader != nullptr) {
|
||||||
shader->start_using();
|
shader->start_using();
|
||||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix);
|
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform);
|
||||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||||
#if ENABLE_GL_CORE_PROFILE
|
#if ENABLE_GL_CORE_PROFILE
|
||||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||||
@ -364,8 +354,7 @@ void GLGizmoScale3D::on_render()
|
|||||||
if (shader != nullptr) {
|
if (shader != nullptr) {
|
||||||
shader->start_using();
|
shader->start_using();
|
||||||
shader->set_uniform("emission_factor", 0.1f);
|
shader->set_uniform("emission_factor", 0.1f);
|
||||||
m_grabbers[2].render(true, grabber_mean_size);
|
render_grabbers(2, 3, grabber_mean_size, true);
|
||||||
m_grabbers[3].render(true, grabber_mean_size);
|
|
||||||
shader->stop_using();
|
shader->stop_using();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -379,7 +368,7 @@ void GLGizmoScale3D::on_render()
|
|||||||
if (shader != nullptr) {
|
if (shader != nullptr) {
|
||||||
shader->start_using();
|
shader->start_using();
|
||||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix);
|
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform);
|
||||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||||
#if ENABLE_GL_CORE_PROFILE
|
#if ENABLE_GL_CORE_PROFILE
|
||||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||||
@ -396,8 +385,7 @@ void GLGizmoScale3D::on_render()
|
|||||||
if (shader != nullptr) {
|
if (shader != nullptr) {
|
||||||
shader->start_using();
|
shader->start_using();
|
||||||
shader->set_uniform("emission_factor", 0.1f);
|
shader->set_uniform("emission_factor", 0.1f);
|
||||||
m_grabbers[4].render(true, grabber_mean_size);
|
render_grabbers(4, 5, grabber_mean_size, true);
|
||||||
m_grabbers[5].render(true, grabber_mean_size);
|
|
||||||
shader->stop_using();
|
shader->stop_using();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -411,7 +399,7 @@ void GLGizmoScale3D::on_render()
|
|||||||
if (shader != nullptr) {
|
if (shader != nullptr) {
|
||||||
shader->start_using();
|
shader->start_using();
|
||||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix);
|
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform);
|
||||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||||
#if ENABLE_GL_CORE_PROFILE
|
#if ENABLE_GL_CORE_PROFILE
|
||||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||||
@ -431,9 +419,7 @@ void GLGizmoScale3D::on_render()
|
|||||||
if (shader != nullptr) {
|
if (shader != nullptr) {
|
||||||
shader->start_using();
|
shader->start_using();
|
||||||
shader->set_uniform("emission_factor", 0.1f);
|
shader->set_uniform("emission_factor", 0.1f);
|
||||||
for (int i = 6; i < 10; ++i) {
|
render_grabbers(6, 9, grabber_mean_size, true);
|
||||||
m_grabbers[i].render(true, grabber_mean_size);
|
|
||||||
}
|
|
||||||
shader->stop_using();
|
shader->stop_using();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -757,7 +743,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
|
|||||||
double ratio = calc_ratio(data);
|
double ratio = calc_ratio(data);
|
||||||
if (ratio > 0.0) {
|
if (ratio > 0.0) {
|
||||||
Vec3d curr_scale = m_scale;
|
Vec3d curr_scale = m_scale;
|
||||||
Vec3d starting_scale = m_starting.scale;
|
const Vec3d starting_scale = m_starting.scale;
|
||||||
const Selection& selection = m_parent.get_selection();
|
const Selection& selection = m_parent.get_selection();
|
||||||
const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
|
const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
|
||||||
|
|
||||||
@ -770,13 +756,6 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
|
|||||||
if (m_hover_id == 2 * axis)
|
if (m_hover_id == 2 * axis)
|
||||||
local_offset *= -1.0;
|
local_offset *= -1.0;
|
||||||
|
|
||||||
Vec3d center_offset = m_starting.instance_center - m_starting.center; // world coordinates (== Vec3d::Zero() for single volume selection)
|
|
||||||
if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local)
|
|
||||||
// from world coordinates to instance coordinates
|
|
||||||
center_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix().inverse() * center_offset;
|
|
||||||
|
|
||||||
local_offset += (ratio - 1.0) * center_offset(axis);
|
|
||||||
|
|
||||||
switch (axis)
|
switch (axis)
|
||||||
{
|
{
|
||||||
case X: { m_offset = local_offset * Vec3d::UnitX(); break; }
|
case X: { m_offset = local_offset * Vec3d::UnitX(); break; }
|
||||||
@ -785,10 +764,6 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
|
|||||||
default: { m_offset = Vec3d::Zero(); break; }
|
default: { m_offset = Vec3d::Zero(); break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local)
|
|
||||||
// from instance coordinates to world coordinates
|
|
||||||
m_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix() * m_offset;
|
|
||||||
|
|
||||||
if (selection.is_single_volume_or_modifier()) {
|
if (selection.is_single_volume_or_modifier()) {
|
||||||
if (coordinates_type == ECoordinatesType::Instance)
|
if (coordinates_type == ECoordinatesType::Instance)
|
||||||
m_offset = selection.get_first_volume()->get_instance_transformation().get_scaling_factor_matrix().inverse() * m_offset;
|
m_offset = selection.get_first_volume()->get_instance_transformation().get_scaling_factor_matrix().inverse() * m_offset;
|
||||||
@ -849,18 +824,6 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData & data)
|
|||||||
if (m_hover_id == 6 || m_hover_id == 7)
|
if (m_hover_id == 6 || m_hover_id == 7)
|
||||||
m_offset.y() *= -1.0;
|
m_offset.y() *= -1.0;
|
||||||
|
|
||||||
Vec3d center_offset = m_starting.instance_center - m_starting.center; // world coordinates (== Vec3d::Zero() for single volume selection)
|
|
||||||
|
|
||||||
if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local)
|
|
||||||
// from world coordinates to instance coordinates
|
|
||||||
center_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix().inverse() * center_offset;
|
|
||||||
|
|
||||||
m_offset += (ratio - 1.0) * center_offset;
|
|
||||||
|
|
||||||
if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local)
|
|
||||||
// from instance coordinates to world coordinates
|
|
||||||
m_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix() * m_offset;
|
|
||||||
|
|
||||||
if (selection.is_single_volume_or_modifier()) {
|
if (selection.is_single_volume_or_modifier()) {
|
||||||
if (coordinates_type == ECoordinatesType::Instance)
|
if (coordinates_type == ECoordinatesType::Instance)
|
||||||
m_offset = selection.get_first_volume()->get_instance_transformation().get_scaling_factor_matrix().inverse() * m_offset;
|
m_offset = selection.get_first_volume()->get_instance_transformation().get_scaling_factor_matrix().inverse() * m_offset;
|
||||||
@ -904,7 +867,7 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const
|
|||||||
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
|
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
|
||||||
// in our case plane normal and ray direction are the same (orthogonal view)
|
// in our case plane normal and ray direction are the same (orthogonal view)
|
||||||
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
|
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
|
||||||
const Vec3d inters = data.mouse_ray.a + (m_starting.drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
|
const Vec3d inters = data.mouse_ray.a + (m_starting.drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir;
|
||||||
// vector from the starting position to the found intersection
|
// vector from the starting position to the found intersection
|
||||||
const Vec3d inters_vec = inters - m_starting.drag_position;
|
const Vec3d inters_vec = inters - m_starting.drag_position;
|
||||||
|
|
||||||
@ -920,20 +883,5 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const
|
|||||||
return ratio;
|
return ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_WORLD_COORDINATE
|
|
||||||
Transform3d GLGizmoScale3D::local_transform(const Selection& selection) const
|
|
||||||
{
|
|
||||||
Transform3d ret = Geometry::translation_transform(m_center);
|
|
||||||
if (!wxGetApp().obj_manipul()->is_world_coordinates()) {
|
|
||||||
const GLVolume& v = *selection.get_first_volume();
|
|
||||||
Transform3d orient_matrix = v.get_instance_transformation().get_rotation_matrix();
|
|
||||||
if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates())
|
|
||||||
orient_matrix = orient_matrix * v.get_volume_transformation().get_rotation_matrix();
|
|
||||||
ret = ret * orient_matrix;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
@ -102,9 +102,6 @@ private:
|
|||||||
void do_scale_uniform(const UpdateData& data);
|
void do_scale_uniform(const UpdateData& data);
|
||||||
|
|
||||||
double calc_ratio(const UpdateData& data) const;
|
double calc_ratio(const UpdateData& data) const;
|
||||||
#if ENABLE_WORLD_COORDINATE
|
|
||||||
Transform3d local_transform(const Selection& selection) const;
|
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -740,6 +740,7 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t
|
|||||||
const float icons_size_x = 2.0f * m_layout.scaled_icons_size() * inv_cnv_w;
|
const float icons_size_x = 2.0f * m_layout.scaled_icons_size() * inv_cnv_w;
|
||||||
const float icons_size_y = 2.0f * m_layout.scaled_icons_size() * inv_cnv_h;
|
const float icons_size_y = 2.0f * m_layout.scaled_icons_size() * inv_cnv_h;
|
||||||
const float stride_y = 2.0f * m_layout.scaled_stride_y() * inv_cnv_h;
|
const float stride_y = 2.0f * m_layout.scaled_stride_y() * inv_cnv_h;
|
||||||
|
top_y -= stride_y;
|
||||||
|
|
||||||
for (size_t idx : selectable_idxs) {
|
for (size_t idx : selectable_idxs) {
|
||||||
if (idx == highlighted_type) {
|
if (idx == highlighted_type) {
|
||||||
|
@ -120,7 +120,9 @@ enum class NotificationType
|
|||||||
// Short meesage to fill space between start and finish of export
|
// Short meesage to fill space between start and finish of export
|
||||||
ExportOngoing,
|
ExportOngoing,
|
||||||
// Progressbar of download from prusaslicer:// url
|
// Progressbar of download from prusaslicer:// url
|
||||||
URLDownload
|
URLDownload,
|
||||||
|
// MacOS specific - PS comes forward even when downloader is not allowed
|
||||||
|
URLNotRegistered,
|
||||||
};
|
};
|
||||||
|
|
||||||
class NotificationManager
|
class NotificationManager
|
||||||
@ -916,6 +918,16 @@ private:
|
|||||||
{NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotificationLevel, 10,
|
{NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotificationLevel, 10,
|
||||||
_u8L("Undo desktop integration failed.") },
|
_u8L("Undo desktop integration failed.") },
|
||||||
{NotificationType::ExportOngoing, NotificationLevel::RegularNotificationLevel, 0, _u8L("Exporting.") },
|
{NotificationType::ExportOngoing, NotificationLevel::RegularNotificationLevel, 0, _u8L("Exporting.") },
|
||||||
|
{NotificationType::URLNotRegistered
|
||||||
|
, NotificationLevel::RegularNotificationLevel
|
||||||
|
, 10
|
||||||
|
, _u8L("PrusaSlicer recieved a download request from Printables.com, but it's not allowed. You can allow it")
|
||||||
|
, _u8L("here.")
|
||||||
|
, [](wxEvtHandler* evnthndlr) {
|
||||||
|
wxGetApp().open_preferences("downloader_url_registered", "Other");
|
||||||
|
return true;
|
||||||
|
} },
|
||||||
|
|
||||||
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
|
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
|
||||||
// wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
|
// wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
|
||||||
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
|
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
|
||||||
|
@ -1038,6 +1038,15 @@ void ogStaticText::SetText(const wxString& value, bool wrap/* = true*/)
|
|||||||
|
|
||||||
void ogStaticText::SetPathEnd(const std::string& link)
|
void ogStaticText::SetPathEnd(const std::string& link)
|
||||||
{
|
{
|
||||||
|
#ifndef __linux__
|
||||||
|
|
||||||
|
Bind(wxEVT_ENTER_WINDOW, [this, link](wxMouseEvent& event) {
|
||||||
|
SetToolTip(OptionsGroup::get_url(get_app_config()->get("suppress_hyperlinks") != "1" ? link : std::string()));
|
||||||
|
FocusText(true);
|
||||||
|
event.Skip();
|
||||||
|
});
|
||||||
|
Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) { FocusText(false); event.Skip(); });
|
||||||
|
|
||||||
Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& event) {
|
Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& event) {
|
||||||
if (HasCapture())
|
if (HasCapture())
|
||||||
return;
|
return;
|
||||||
@ -1051,12 +1060,28 @@ void ogStaticText::SetPathEnd(const std::string& link)
|
|||||||
OptionsGroup::launch_browser(link);
|
OptionsGroup::launch_browser(link);
|
||||||
event.Skip();
|
event.Skip();
|
||||||
});
|
});
|
||||||
Bind(wxEVT_ENTER_WINDOW, [this, link](wxMouseEvent& event) {
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Workaround: On Linux wxStaticText doesn't receive wxEVT_ENTER(LEAVE)_WINDOW events,
|
||||||
|
// so implement this behaviour trough wxEVT_MOTION events for this control and it's parent
|
||||||
|
Bind(wxEVT_MOTION, [link, this](wxMouseEvent& event) {
|
||||||
SetToolTip(OptionsGroup::get_url(!get_app_config()->get_bool("suppress_hyperlinks") ? link : std::string()));
|
SetToolTip(OptionsGroup::get_url(!get_app_config()->get_bool("suppress_hyperlinks") ? link : std::string()));
|
||||||
FocusText(true);
|
FocusText(true);
|
||||||
event.Skip();
|
event.Skip();
|
||||||
});
|
});
|
||||||
Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) { FocusText(false); event.Skip(); });
|
GetParent()->Bind(wxEVT_MOTION, [this](wxMouseEvent& event) {
|
||||||
|
FocusText(false);
|
||||||
|
event.Skip();
|
||||||
|
});
|
||||||
|
|
||||||
|
// On Linux a mouse capturing causes a totally application freeze
|
||||||
|
Bind(wxEVT_LEFT_UP, [link, this](wxMouseEvent& event) {
|
||||||
|
OptionsGroup::launch_browser(link);
|
||||||
|
event.Skip();
|
||||||
|
});
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ogStaticText::FocusText(bool focus)
|
void ogStaticText::FocusText(bool focus)
|
||||||
@ -1066,6 +1091,9 @@ void ogStaticText::FocusText(bool focus)
|
|||||||
|
|
||||||
SetFont(focus ? Slic3r::GUI::wxGetApp().link_font() :
|
SetFont(focus ? Slic3r::GUI::wxGetApp().link_font() :
|
||||||
Slic3r::GUI::wxGetApp().normal_font());
|
Slic3r::GUI::wxGetApp().normal_font());
|
||||||
|
#ifdef __linux__
|
||||||
|
this->GetContainingSizer()->Layout();
|
||||||
|
#endif
|
||||||
Refresh();
|
Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include "ButtonsDescription.hpp"
|
#include "ButtonsDescription.hpp"
|
||||||
#include "OG_CustomCtrl.hpp"
|
#include "OG_CustomCtrl.hpp"
|
||||||
#include "GLCanvas3D.hpp"
|
#include "GLCanvas3D.hpp"
|
||||||
#include "ConfigWizard_private.hpp"
|
#include "ConfigWizard.hpp"
|
||||||
|
|
||||||
#include <boost/dll/runtime_symbol_info.hpp>
|
#include <boost/dll/runtime_symbol_info.hpp>
|
||||||
|
|
||||||
@ -712,7 +712,7 @@ void PreferencesDialog::accept(wxEvent&)
|
|||||||
return;
|
return;
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
if( downloader->get_perform_registration_linux())
|
if( downloader->get_perform_registration_linux())
|
||||||
DesktopIntegrationDialog::perform_desktop_integration(true);
|
DesktopIntegrationDialog::perform_downloader_desktop_integration();
|
||||||
#endif // __linux__
|
#endif // __linux__
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -836,108 +836,57 @@ const std::pair<BoundingBoxf3, Transform3d>& Selection::get_bounding_box_in_curr
|
|||||||
|
|
||||||
std::pair<BoundingBoxf3, Transform3d> Selection::get_bounding_box_in_reference_system(ECoordinatesType type) const
|
std::pair<BoundingBoxf3, Transform3d> Selection::get_bounding_box_in_reference_system(ECoordinatesType type) const
|
||||||
{
|
{
|
||||||
BoundingBoxf3 original_box;
|
//
|
||||||
|
// trafo to current reference system
|
||||||
|
//
|
||||||
Transform3d trafo;
|
Transform3d trafo;
|
||||||
|
|
||||||
//
|
|
||||||
// calculate box aligned to current reference system
|
|
||||||
//
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case ECoordinatesType::World:
|
case ECoordinatesType::World: { trafo = Transform3d::Identity(); break; }
|
||||||
{
|
case ECoordinatesType::Instance: { trafo = get_first_volume()->get_instance_transformation().get_matrix(); break; }
|
||||||
original_box = get_bounding_box();
|
case ECoordinatesType::Local: { trafo = get_first_volume()->world_matrix(); break; }
|
||||||
trafo = Transform3d::Identity();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ECoordinatesType::Instance: {
|
|
||||||
for (unsigned int id : m_list) {
|
|
||||||
const GLVolume& v = *get_volume(id);
|
|
||||||
original_box.merge(v.transformed_convex_hull_bounding_box(v.get_volume_transformation().get_matrix()));
|
|
||||||
}
|
|
||||||
trafo = get_first_volume()->get_instance_transformation().get_matrix();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ECoordinatesType::Local: {
|
|
||||||
assert(is_single_volume_or_modifier() || is_single_volume_instance());
|
|
||||||
const GLVolume& v = *get_first_volume();
|
|
||||||
original_box = v.bounding_box();
|
|
||||||
trafo = v.world_matrix();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// calculate box size in world coordinates
|
// trafo basis in world coordinates
|
||||||
//
|
//
|
||||||
auto point_to_Vec4d = [](const Vec3d& p) { return Vec4d(p.x(), p.y(), p.z(), 1.0); };
|
Geometry::Transformation t(trafo);
|
||||||
auto Vec4d_to_Vec3d = [](const Vec4d& v) { return Vec3d(v.x(), v.y(), v.z()); };
|
t.reset_scaling_factor();
|
||||||
|
const Transform3d basis_trafo = t.get_matrix_no_offset();
|
||||||
auto apply_transform = [](const std::vector<Vec4d>& original, const Transform3d& trafo, bool normalize) {
|
std::vector<Vec3d> axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() };
|
||||||
std::vector<Vec4d> transformed(original.size());
|
for (size_t i = 0; i < axes.size(); ++i) {
|
||||||
for (size_t i = 0; i < original.size(); ++i) {
|
axes[i] = basis_trafo * axes[i];
|
||||||
transformed[i] = trafo * original[i];
|
|
||||||
if (normalize)
|
|
||||||
transformed[i].normalize();
|
|
||||||
}
|
|
||||||
return transformed;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto calc_box_size = [point_to_Vec4d, Vec4d_to_Vec3d, apply_transform](const BoundingBoxf3& box, const Transform3d& trafo) {
|
|
||||||
Geometry::Transformation transformation(trafo);
|
|
||||||
|
|
||||||
// box aligned to current reference system
|
|
||||||
std::vector<Vec4d> homo_vertices = {
|
|
||||||
point_to_Vec4d({ box.min.x(), box.min.y(), box.min.z() }),
|
|
||||||
point_to_Vec4d({ box.max.x(), box.min.y(), box.min.z() }),
|
|
||||||
point_to_Vec4d({ box.max.x(), box.max.y(), box.min.z() }),
|
|
||||||
point_to_Vec4d({ box.min.x(), box.max.y(), box.min.z() }),
|
|
||||||
point_to_Vec4d({ box.min.x(), box.min.y(), box.max.z() }),
|
|
||||||
point_to_Vec4d({ box.max.x(), box.min.y(), box.max.z() }),
|
|
||||||
point_to_Vec4d({ box.max.x(), box.max.y(), box.max.z() }),
|
|
||||||
point_to_Vec4d({ box.min.x(), box.max.y(), box.max.z() })
|
|
||||||
};
|
|
||||||
|
|
||||||
// box vertices in world coordinates
|
|
||||||
std::vector<Vec4d> transformed_homo_vertices = apply_transform(homo_vertices, trafo, false);
|
|
||||||
|
|
||||||
// project back to current reference system
|
|
||||||
const std::vector<Vec4d> homo_axes = { Vec4d::UnitX(), Vec4d::UnitY(), Vec4d::UnitZ() };
|
|
||||||
std::vector<Vec4d> transformed_homo_axes = apply_transform(homo_axes, Geometry::Transformation(trafo).get_matrix_no_scaling_factor(), true);
|
|
||||||
std::vector<Vec3d> transformed_axes(transformed_homo_axes.size());
|
|
||||||
for (size_t i = 0; i < transformed_homo_axes.size(); ++i) {
|
|
||||||
transformed_axes[i] = Vec4d_to_Vec3d(transformed_homo_axes[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// calculate bounding box aligned to trafo basis
|
||||||
|
//
|
||||||
Vec3d min = { DBL_MAX, DBL_MAX, DBL_MAX };
|
Vec3d min = { DBL_MAX, DBL_MAX, DBL_MAX };
|
||||||
Vec3d max = { -DBL_MAX, -DBL_MAX, -DBL_MAX };
|
Vec3d max = { -DBL_MAX, -DBL_MAX, -DBL_MAX };
|
||||||
|
for (unsigned int id : m_list) {
|
||||||
for (const Vec4d& v_homo : transformed_homo_vertices) {
|
const GLVolume& vol = *get_volume(id);
|
||||||
const Vec3d v = Vec4d_to_Vec3d(v_homo);
|
const Transform3d vol_world_rafo = vol.world_matrix();
|
||||||
|
const TriangleMesh* mesh = vol.convex_hull();
|
||||||
|
if (mesh == nullptr)
|
||||||
|
mesh = &m_model->objects[vol.object_idx()]->volumes[vol.volume_idx()]->mesh();
|
||||||
|
assert(mesh != nullptr);
|
||||||
|
for (const stl_vertex& v : mesh->its.vertices) {
|
||||||
|
const Vec3d world_v = vol_world_rafo * v.cast<double>();
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
const double dot_i = v.dot(transformed_axes[i]);
|
const double i_comp = world_v.dot(axes[i]);
|
||||||
min(i) = std::min(min(i), dot_i);
|
min(i) = std::min(min(i), i_comp);
|
||||||
max(i) = std::max(max(i), dot_i);
|
max(i) = std::max(max(i), i_comp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// return size
|
const Vec3d box_size = max - min;
|
||||||
const Vec3d size = max - min;
|
|
||||||
return size;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Vec3d box_size = calc_box_size(original_box, trafo);
|
|
||||||
const std::vector<Vec4d> box_center = { point_to_Vec4d(original_box.center()) };
|
|
||||||
std::vector<Vec4d> transformed_box_center = apply_transform(box_center, trafo, false);
|
|
||||||
|
|
||||||
//
|
|
||||||
// return box centered at 0, 0, 0
|
|
||||||
//
|
|
||||||
const Vec3d half_box_size = 0.5 * box_size;
|
const Vec3d half_box_size = 0.5 * box_size;
|
||||||
BoundingBoxf3 out_box(-half_box_size, half_box_size);
|
BoundingBoxf3 out_box(-half_box_size, half_box_size);
|
||||||
Geometry::Transformation out_trafo(trafo);
|
Geometry::Transformation out_trafo(trafo);
|
||||||
out_trafo.set_offset(Vec4d_to_Vec3d(transformed_box_center[0]));
|
const Vec3d center = 0.5 * (min + max);
|
||||||
|
out_trafo.set_offset(basis_trafo * center);
|
||||||
return { out_box, out_trafo.get_matrix_no_scaling_factor() };
|
return { out_box, out_trafo.get_matrix_no_scaling_factor() };
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
|
|
||||||
@ -1049,7 +998,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
|||||||
|
|
||||||
assert(transformation_type.relative() || (transformation_type.absolute() && transformation_type.local()));
|
assert(transformation_type.relative() || (transformation_type.absolute() && transformation_type.local()));
|
||||||
|
|
||||||
const Transform3d rotation_matrix = Geometry::rotation_transform(rotation);
|
Transform3d rotation_matrix = Geometry::rotation_transform(rotation);
|
||||||
|
|
||||||
for (unsigned int i : m_list) {
|
for (unsigned int i : m_list) {
|
||||||
GLVolume& v = *(*m_volumes)[i];
|
GLVolume& v = *(*m_volumes)[i];
|
||||||
@ -1074,31 +1023,50 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
|||||||
transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.dragging_center);
|
transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.dragging_center);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (transformation_type.local() && transformation_type.absolute()) {
|
if (transformation_type.instance()) {
|
||||||
const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform();
|
const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform();
|
||||||
Matrix3d vol_rotation, vol_scale;
|
const Geometry::Transformation world_trafo = inst_trafo * vol_trafo;
|
||||||
vol_trafo.get_matrix().computeRotationScaling(&vol_rotation, &vol_scale);
|
// ensure proper sign of rotation for mirrored objects
|
||||||
const Transform3d trafo = vol_trafo.get_rotation_matrix() * rotation_matrix;
|
if (world_trafo.is_left_handed() && !rotation.normalized().isApprox(Vec3d::UnitX()))
|
||||||
v.set_volume_transformation(vol_trafo.get_offset_matrix() * trafo * Transform3d(vol_scale));
|
rotation_matrix = rotation_matrix.inverse();
|
||||||
|
|
||||||
|
// ensure that the volume rotates as a rigid body
|
||||||
|
const Geometry::TransformationSVD world_svd(world_trafo);
|
||||||
|
if (world_svd.anisotropic_scale) {
|
||||||
|
const Transform3d vol_scale_matrix = vol_trafo.get_scaling_factor_matrix();
|
||||||
|
rotation_matrix = vol_scale_matrix.inverse() * rotation_matrix * vol_scale_matrix;
|
||||||
|
}
|
||||||
|
const Transform3d vol_rotation_matrix = vol_trafo.get_rotation_matrix();
|
||||||
|
rotation_matrix = vol_rotation_matrix.inverse() * rotation_matrix * vol_rotation_matrix;
|
||||||
|
|
||||||
|
v.set_volume_transformation(vol_trafo.get_matrix() * rotation_matrix);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (transformation_type.local()) {
|
||||||
|
const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform();
|
||||||
|
const Geometry::Transformation world_trafo = inst_trafo * vol_trafo;
|
||||||
|
// ensure proper sign of rotation for mirrored objects
|
||||||
|
if (world_trafo.is_left_handed() && !rotation.normalized().isApprox(Vec3d::UnitX()))
|
||||||
|
rotation_matrix = rotation_matrix.inverse();
|
||||||
|
|
||||||
|
// ensure that the volume rotates as a rigid body
|
||||||
|
const Geometry::TransformationSVD svd(world_trafo);
|
||||||
|
if (svd.anisotropic_scale) {
|
||||||
|
const Transform3d vol_scale_matrix = vol_trafo.get_scaling_factor_matrix();
|
||||||
|
rotation_matrix = vol_scale_matrix.inverse() * rotation_matrix * vol_scale_matrix;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.dragging_center);
|
transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.dragging_center);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if !DISABLE_INSTANCES_SYNCH
|
#if !DISABLE_INSTANCES_SYNCH
|
||||||
if (m_mode == Instance) {
|
if (m_mode == Instance) {
|
||||||
int rot_axis_max = 0;
|
int rot_axis_max = 0;
|
||||||
rotation.cwiseAbs().maxCoeff(&rot_axis_max);
|
rotation.cwiseAbs().maxCoeff(&rot_axis_max);
|
||||||
SyncRotationType synch;
|
synchronize_unselected_instances((transformation_type.world() && rot_axis_max == 2) ? SyncRotationType::NONE : SyncRotationType::GENERAL);
|
||||||
if (transformation_type.world() && rot_axis_max == 2)
|
|
||||||
synch = SyncRotationType::NONE;
|
|
||||||
else if (transformation_type.instance())
|
|
||||||
synch = SyncRotationType::FULL;
|
|
||||||
else
|
|
||||||
synch = SyncRotationType::GENERAL;
|
|
||||||
synchronize_unselected_instances(synch);
|
|
||||||
}
|
}
|
||||||
else if (m_mode == Volume)
|
else if (m_mode == Volume)
|
||||||
synchronize_unselected_volumes();
|
synchronize_unselected_volumes();
|
||||||
@ -1466,17 +1434,12 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& translation
|
|||||||
if (!m_valid)
|
if (!m_valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (unsigned int i : m_list) {
|
|
||||||
GLVolume& v = *(*m_volumes)[i];
|
|
||||||
const VolumeCache& volume_data = m_cache.volumes_data[i];
|
|
||||||
const Geometry::Transformation& inst_trafo = volume_data.get_instance_transform();
|
|
||||||
|
|
||||||
Vec3d relative_scale = scale;
|
Vec3d relative_scale = scale;
|
||||||
|
|
||||||
if (transformation_type.absolute()) {
|
if (transformation_type.absolute()) {
|
||||||
|
// converts to relative scale
|
||||||
if (m_mode == Instance) {
|
if (m_mode == Instance) {
|
||||||
if (is_single_full_instance()) {
|
if (is_single_full_instance()) {
|
||||||
BoundingBoxf3 current_box = m_box.get_bounding_box();
|
BoundingBoxf3 current_box = get_bounding_box_in_current_reference_system().first;
|
||||||
BoundingBoxf3 original_box;
|
BoundingBoxf3 original_box;
|
||||||
if (transformation_type.world())
|
if (transformation_type.world())
|
||||||
original_box = get_full_unscaled_instance_bounding_box();
|
original_box = get_full_unscaled_instance_bounding_box();
|
||||||
@ -1484,12 +1447,15 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& translation
|
|||||||
original_box = get_full_unscaled_instance_local_bounding_box();
|
original_box = get_full_unscaled_instance_local_bounding_box();
|
||||||
|
|
||||||
relative_scale = original_box.size().cwiseProduct(scale).cwiseQuotient(current_box.size());
|
relative_scale = original_box.size().cwiseProduct(scale).cwiseQuotient(current_box.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
transformation_type.set_relative();
|
transformation_type.set_relative();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
for (unsigned int i : m_list) {
|
||||||
}
|
GLVolume& v = *(*m_volumes)[i];
|
||||||
}
|
const VolumeCache& volume_data = m_cache.volumes_data[i];
|
||||||
|
const Geometry::Transformation& inst_trafo = volume_data.get_instance_transform();
|
||||||
|
|
||||||
if (m_mode == Instance) {
|
if (m_mode == Instance) {
|
||||||
if (transformation_type.instance()) {
|
if (transformation_type.instance()) {
|
||||||
@ -1529,7 +1495,9 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& translation
|
|||||||
|
|
||||||
#if !DISABLE_INSTANCES_SYNCH
|
#if !DISABLE_INSTANCES_SYNCH
|
||||||
if (m_mode == Instance)
|
if (m_mode == Instance)
|
||||||
synchronize_unselected_instances(SyncRotationType::NONE);
|
// even if there is no rotation, we pass SyncRotationType::GENERAL to force
|
||||||
|
// synchronize_unselected_instances() to apply the scale to the other instances
|
||||||
|
synchronize_unselected_instances(SyncRotationType::GENERAL);
|
||||||
else if (m_mode == Volume)
|
else if (m_mode == Volume)
|
||||||
synchronize_unselected_volumes();
|
synchronize_unselected_volumes();
|
||||||
#endif // !DISABLE_INSTANCES_SYNCH
|
#endif // !DISABLE_INSTANCES_SYNCH
|
||||||
@ -2196,7 +2164,6 @@ void Selection::update_type()
|
|||||||
unsigned int volumes_count = (unsigned int)model_object->volumes.size();
|
unsigned int volumes_count = (unsigned int)model_object->volumes.size();
|
||||||
unsigned int instances_count = (unsigned int)model_object->instances.size();
|
unsigned int instances_count = (unsigned int)model_object->instances.size();
|
||||||
if (volumes_count * instances_count == 1) {
|
if (volumes_count * instances_count == 1) {
|
||||||
const ModelVolume* model_volume = model_object->volumes[first->volume_idx()];
|
|
||||||
m_type = SingleFullObject;
|
m_type = SingleFullObject;
|
||||||
// ensures the correct mode is selected
|
// ensures the correct mode is selected
|
||||||
m_mode = Instance;
|
m_mode = Instance;
|
||||||
@ -2834,7 +2801,7 @@ void Selection::render_debug_window() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int current_method_idx = 0;
|
static int current_method_idx = 0;
|
||||||
ImGui::Combo("Decomposition method", ¤t_method_idx, "computeRotationScaling\0computeScalingRotation\0");
|
ImGui::Combo("Decomposition method", ¤t_method_idx, "computeRotationScaling\0computeScalingRotation\0SVD\0");
|
||||||
|
|
||||||
const GLVolume& v = *get_volume(current_vol_idx);
|
const GLVolume& v = *get_volume(current_vol_idx);
|
||||||
|
|
||||||
@ -2854,12 +2821,13 @@ void Selection::render_debug_window() const
|
|||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
};
|
};
|
||||||
|
|
||||||
auto add_matrices_set = [add_matrix](const std::string& name, const Transform3d& m, size_t method) {
|
auto add_matrices_set = [&imgui, add_matrix](const std::string& name, const Transform3d& m, size_t method) {
|
||||||
static unsigned int counter = 0;
|
static unsigned int counter = 0;
|
||||||
++counter;
|
++counter;
|
||||||
if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
|
if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
add_matrix("Full", m, 4);
|
add_matrix("Full", m, 4);
|
||||||
|
|
||||||
|
if (method == 0 || method == 1) {
|
||||||
Matrix3d rotation;
|
Matrix3d rotation;
|
||||||
Matrix3d scale;
|
Matrix3d scale;
|
||||||
if (method == 0)
|
if (method == 0)
|
||||||
@ -2872,6 +2840,38 @@ void Selection::render_debug_window() const
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
add_matrix("Scale component", Transform3d(scale), 3);
|
add_matrix("Scale component", Transform3d(scale), 3);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
const Geometry::TransformationSVD svd(m);
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
add_matrix("U", Transform3d(svd.u), 3);
|
||||||
|
ImGui::SameLine();
|
||||||
|
add_matrix("S", Transform3d(svd.s), 3);
|
||||||
|
ImGui::SameLine();
|
||||||
|
add_matrix("V", Transform3d(svd.v), 3);
|
||||||
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||||
|
float spacing = 0.0f;
|
||||||
|
if (svd.rotation) {
|
||||||
|
ImGui::SameLine(0.0f, spacing);
|
||||||
|
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, svd.rotation_90_degrees ? "Rotation 90 degs" : "Rotation");
|
||||||
|
spacing = 10.0f;
|
||||||
|
}
|
||||||
|
if (svd.scale) {
|
||||||
|
ImGui::SameLine(0.0f, spacing);
|
||||||
|
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, svd.anisotropic_scale ? "Anisotropic scale" : "Isotropic scale");
|
||||||
|
spacing = 10.0f;
|
||||||
|
}
|
||||||
|
if (svd.mirror) {
|
||||||
|
ImGui::SameLine(0.0f, spacing);
|
||||||
|
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Mirror");
|
||||||
|
spacing = 10.0f;
|
||||||
|
}
|
||||||
|
if (svd.skew) {
|
||||||
|
ImGui::SameLine(0.0f, spacing);
|
||||||
|
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Skew");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
add_matrices_set("World", v.world_matrix(), current_method_idx);
|
add_matrices_set("World", v.world_matrix(), current_method_idx);
|
||||||
@ -2994,7 +2994,6 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_
|
|||||||
const int object_idx = volume_i->object_idx();
|
const int object_idx = volume_i->object_idx();
|
||||||
const int instance_idx = volume_i->instance_idx();
|
const int instance_idx = volume_i->instance_idx();
|
||||||
const Transform3d& curr_inst_trafo_i = volume_i->get_instance_transformation().get_matrix();
|
const Transform3d& curr_inst_trafo_i = volume_i->get_instance_transformation().get_matrix();
|
||||||
const bool curr_inst_left_handed = is_left_handed(curr_inst_trafo_i);
|
|
||||||
const Transform3d& old_inst_trafo_i = m_cache.volumes_data[i].get_instance_transform().get_matrix();
|
const Transform3d& old_inst_trafo_i = m_cache.volumes_data[i].get_instance_transform().get_matrix();
|
||||||
bool mirrored = is_left_handed(curr_inst_trafo_i) != is_left_handed(old_inst_trafo_i);
|
bool mirrored = is_left_handed(curr_inst_trafo_i) != is_left_handed(old_inst_trafo_i);
|
||||||
// bool mirrored = curr_inst_trafo_i.linear().determinant() * old_inst_trafo_i.linear().determinant() < 0;
|
// bool mirrored = curr_inst_trafo_i.linear().determinant() * old_inst_trafo_i.linear().determinant() < 0;
|
||||||
|
@ -499,10 +499,6 @@ public:
|
|||||||
NONE = 0,
|
NONE = 0,
|
||||||
// Synchronize after rotation by an axis not parallel with Z.
|
// Synchronize after rotation by an axis not parallel with Z.
|
||||||
GENERAL = 1,
|
GENERAL = 1,
|
||||||
#if ENABLE_WORLD_COORDINATE
|
|
||||||
// Fully synchronize rotation.
|
|
||||||
FULL = 2,
|
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
|
||||||
};
|
};
|
||||||
void synchronize_unselected_instances(SyncRotationType sync_rotation_type);
|
void synchronize_unselected_instances(SyncRotationType sync_rotation_type);
|
||||||
void synchronize_unselected_volumes();
|
void synchronize_unselected_volumes();
|
||||||
|
@ -1641,9 +1641,6 @@ void TabPrint::build()
|
|||||||
optgroup->append_single_option_line("xy_size_compensation");
|
optgroup->append_single_option_line("xy_size_compensation");
|
||||||
optgroup->append_single_option_line("elefant_foot_compensation", "elephant-foot-compensation_114487");
|
optgroup->append_single_option_line("elefant_foot_compensation", "elephant-foot-compensation_114487");
|
||||||
|
|
||||||
optgroup = page->new_optgroup(L("Other"));
|
|
||||||
optgroup->append_single_option_line("clip_multipart_objects");
|
|
||||||
|
|
||||||
optgroup = page->new_optgroup(L("Arachne perimeter generator"));
|
optgroup = page->new_optgroup(L("Arachne perimeter generator"));
|
||||||
optgroup->append_single_option_line("wall_transition_angle");
|
optgroup->append_single_option_line("wall_transition_angle");
|
||||||
optgroup->append_single_option_line("wall_transition_filter_deviation");
|
optgroup->append_single_option_line("wall_transition_filter_deviation");
|
||||||
@ -1732,9 +1729,7 @@ void TabPrint::update_description_lines()
|
|||||||
if (m_post_process_explanation) {
|
if (m_post_process_explanation) {
|
||||||
m_post_process_explanation->SetText(
|
m_post_process_explanation->SetText(
|
||||||
_L("Post processing scripts shall modify G-code file in place."));
|
_L("Post processing scripts shall modify G-code file in place."));
|
||||||
#ifndef __linux__
|
|
||||||
m_post_process_explanation->SetPathEnd("post-processing-scripts_283913");
|
m_post_process_explanation->SetPathEnd("post-processing-scripts_283913");
|
||||||
#endif // __linux__
|
|
||||||
}
|
}
|
||||||
// upadte G-code substitutions from the current configuration
|
// upadte G-code substitutions from the current configuration
|
||||||
{
|
{
|
||||||
|
@ -734,11 +734,14 @@ bool PrusaLink::get_storage(wxArrayString& output) const
|
|||||||
const auto path = section.second.get_optional<std::string>("path");
|
const auto path = section.second.get_optional<std::string>("path");
|
||||||
const auto space = section.second.get_optional<std::string>("free_space");
|
const auto space = section.second.get_optional<std::string>("free_space");
|
||||||
const auto read_only = section.second.get_optional<bool>("read_only");
|
const auto read_only = section.second.get_optional<bool>("read_only");
|
||||||
|
const auto ro = section.second.get_optional<bool>("ro"); // In PrusaLink 0.7.0RC2 "read_only" value is stored under "ro".
|
||||||
const auto available = section.second.get_optional<bool>("available");
|
const auto available = section.second.get_optional<bool>("available");
|
||||||
if (path && (!available || *available)) {
|
if (path && (!available || *available)) {
|
||||||
StorageInfo si;
|
StorageInfo si;
|
||||||
si.name = boost::nowide::widen(*path);
|
si.name = boost::nowide::widen(*path);
|
||||||
si.read_only = read_only ? *read_only : false; // If read_only is missing, assume it is NOT read only.
|
// If read_only is missing, assume it is NOT read only.
|
||||||
|
// si.read_only = read_only ? *read_only : false; // version without "ro"
|
||||||
|
si.read_only = (read_only ? *read_only : (ro ? *ro : false));
|
||||||
si.free_space = space ? std::stoll(*space) : 1; // If free_space is missing, assume there is free space.
|
si.free_space = space ? std::stoll(*space) : 1; // If free_space is missing, assume there is free space.
|
||||||
storage.emplace_back(std::move(si));
|
storage.emplace_back(std::move(si));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user