mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-22 08:11:11 -06:00
Merge branch 'master' of https://github.com/prusa3d/Slic3r into svg_icons
This commit is contained in:
commit
3c7ec5f7c6
50 changed files with 2399 additions and 15430 deletions
|
|
@ -1004,7 +1004,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason)
|
|||
p->btn_prev = new wxButton(this, wxID_ANY, _(L("< &Back")));
|
||||
p->btn_next = new wxButton(this, wxID_ANY, _(L("&Next >")));
|
||||
p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish")));
|
||||
p->btn_cancel = new wxButton(this, wxID_CANCEL);
|
||||
p->btn_cancel = new wxButton(this, wxID_CANCEL, _(L("Cancel"))); // Note: The label needs to be present, otherwise we get accelerator bugs on Mac
|
||||
p->btnsizer->AddStretchSpacer();
|
||||
p->btnsizer->Add(p->btn_prev, 0, wxLEFT, BTN_SPACING);
|
||||
p->btnsizer->Add(p->btn_next, 0, wxLEFT, BTN_SPACING);
|
||||
|
|
|
|||
|
|
@ -122,7 +122,6 @@ struct FirmwareDialog::priv
|
|||
// This is a shared pointer holding the background AvrDude task
|
||||
// also serves as a status indication (it is set _iff_ the background task is running, otherwise it is reset).
|
||||
AvrDude::Ptr avrdude;
|
||||
std::string avrdude_config;
|
||||
unsigned progress_tasks_done;
|
||||
unsigned progress_tasks_bar;
|
||||
bool user_cancelled;
|
||||
|
|
@ -134,7 +133,6 @@ struct FirmwareDialog::priv
|
|||
btn_flash_label_flashing(_(L("Cancel"))),
|
||||
label_status_flashing(_(L("Flashing in progress. Please do not disconnect the printer!"))),
|
||||
timer_pulse(q),
|
||||
avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()),
|
||||
progress_tasks_done(0),
|
||||
progress_tasks_bar(0),
|
||||
user_cancelled(false),
|
||||
|
|
@ -553,7 +551,7 @@ void FirmwareDialog::priv::perform_upload()
|
|||
flashing_start(hex_file.device == HexFile::DEV_MK3 ? 2 : 1);
|
||||
|
||||
// Init the avrdude object
|
||||
AvrDude avrdude(avrdude_config);
|
||||
AvrDude avrdude;
|
||||
|
||||
// It is ok here to use the q-pointer to the FirmwareDialog
|
||||
// because the dialog ensures it doesn't exit before the background thread is done.
|
||||
|
|
@ -722,7 +720,7 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
|
|||
panel->SetSizer(vsizer);
|
||||
|
||||
auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
|
||||
p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY, wxEmptyString, wxFileSelectorPromptStr,
|
||||
p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY, wxEmptyString, wxFileSelectorPromptStr,
|
||||
"Hex files (*.hex)|*.hex|All files|*.*");
|
||||
|
||||
auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:")));
|
||||
|
|
@ -770,7 +768,7 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
|
|||
// Experience says it needs to be 1, otherwise things won't get sized properly.
|
||||
vsizer->Add(p->spoiler, 1, wxEXPAND | wxBOTTOM, SPACING);
|
||||
|
||||
p->btn_close = new wxButton(panel, wxID_CLOSE);
|
||||
p->btn_close = new wxButton(panel, wxID_CLOSE, _(L("Close"))); // Note: The label needs to be present, otherwise we get accelerator bugs on Mac
|
||||
p->btn_flash = new wxButton(panel, wxID_ANY, p->btn_flash_label_ready);
|
||||
p->btn_flash->Disable();
|
||||
auto *bsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
|
|
|||
|
|
@ -896,8 +896,7 @@ void GLCanvas3D::Selection::add(unsigned int volume_idx, bool as_single_selectio
|
|||
if (needs_reset)
|
||||
clear();
|
||||
|
||||
if (volume->is_modifier)
|
||||
m_mode = Volume;
|
||||
m_mode = volume->is_modifier ? Volume : Instance;
|
||||
|
||||
switch (m_mode)
|
||||
{
|
||||
|
|
@ -1261,17 +1260,22 @@ static double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to
|
|||
return (axis.z() < 0) ? -angle : angle;
|
||||
}
|
||||
|
||||
void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local)
|
||||
// Rotate an object around one of the axes. Only one rotation component is expected to be changing.
|
||||
void GLCanvas3D::Selection::rotate(const Vec3d& rotation, GLCanvas3D::TransformationType transformation_type)
|
||||
{
|
||||
if (!m_valid)
|
||||
return;
|
||||
|
||||
// Only relative rotation values are allowed in the world coordinate system.
|
||||
assert(! transformation_type.world() || transformation_type.relative());
|
||||
|
||||
int rot_axis_max;
|
||||
//FIXME this does not work for absolute rotations (transformation_type.absolute() is true)
|
||||
rotation.cwiseAbs().maxCoeff(&rot_axis_max);
|
||||
|
||||
// For generic rotation, we want to rotate the first volume in selection, and then to synchronize the other volumes with it.
|
||||
std::vector<int> object_instance_first(m_model->objects.size(), -1);
|
||||
auto rotate_instance = [this, &rotation, &object_instance_first, rot_axis_max, local](GLVolume &volume, int i) {
|
||||
auto rotate_instance = [this, &rotation, &object_instance_first, rot_axis_max, transformation_type](GLVolume &volume, int i) {
|
||||
int first_volume_idx = object_instance_first[volume.object_idx()];
|
||||
if (rot_axis_max != 2 && first_volume_idx != -1) {
|
||||
// Generic rotation, but no rotation around the Z axis.
|
||||
|
|
@ -1283,11 +1287,14 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local)
|
|||
volume.set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff));
|
||||
} else {
|
||||
// extracts rotations from the composed transformation
|
||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix());
|
||||
if (rot_axis_max == 2 && !local)
|
||||
Vec3d new_rotation = transformation_type.world() ?
|
||||
Geometry::extract_euler_angles(Geometry::assemble_transform(Vec3d::Zero(), rotation) * m_cache.volumes_data[i].get_instance_rotation_matrix()) :
|
||||
transformation_type.absolute() ? rotation : rotation + m_cache.volumes_data[i].get_instance_rotation();
|
||||
if (rot_axis_max == 2 && transformation_type.joint()) {
|
||||
// Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis.
|
||||
volume.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
|
||||
double z_diff = rotation_diff_z(new_rotation, m_cache.volumes_data[i].get_instance_rotation());
|
||||
volume.set_instance_offset(m_cache.dragging_center + Eigen::AngleAxisd(z_diff, Vec3d::UnitZ()) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
|
||||
}
|
||||
volume.set_instance_rotation(new_rotation);
|
||||
object_instance_first[volume.object_idx()] = i;
|
||||
}
|
||||
|
|
@ -1300,7 +1307,7 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local)
|
|||
rotate_instance(volume, i);
|
||||
else if (is_single_volume() || is_single_modifier())
|
||||
{
|
||||
if (local)
|
||||
if (transformation_type.independent())
|
||||
volume.set_volume_rotation(volume.get_volume_rotation() + rotation);
|
||||
else
|
||||
{
|
||||
|
|
@ -1318,7 +1325,7 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local)
|
|||
// extracts rotations from the composed transformation
|
||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix());
|
||||
if (!local)
|
||||
if (transformation_type.joint())
|
||||
{
|
||||
Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center);
|
||||
volume.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset);
|
||||
|
|
@ -5136,6 +5143,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
{
|
||||
// to remove hover on objects when the mouse goes out of this canvas
|
||||
m_mouse.position = Vec2d(-1.0, -1.0);
|
||||
// ensure m_mouse.left_down is reset (it may happen when switching canvas)
|
||||
m_mouse.left_down = false;
|
||||
m_dirty = true;
|
||||
}
|
||||
else if (evt.LeftDClick() && (toolbar_contains_mouse != -1))
|
||||
|
|
@ -5203,12 +5212,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
{
|
||||
// event was taken care of by the SlaSupports gizmo
|
||||
}
|
||||
else if (view_toolbar_contains_mouse != -1)
|
||||
else if (evt.LeftDown() && (view_toolbar_contains_mouse != -1))
|
||||
{
|
||||
if (m_view_toolbar != nullptr)
|
||||
m_view_toolbar->do_action((unsigned int)view_toolbar_contains_mouse, *this);
|
||||
}
|
||||
else if (toolbar_contains_mouse != -1)
|
||||
else if (evt.LeftDown() && (toolbar_contains_mouse != -1))
|
||||
{
|
||||
m_toolbar_action_running = true;
|
||||
m_mouse.set_start_position_3D_as_invalid();
|
||||
|
|
@ -5388,7 +5397,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
case Gizmos::Rotate:
|
||||
{
|
||||
// Apply new temporary rotations
|
||||
m_selection.rotate(m_gizmos.get_rotation(), evt.AltDown());
|
||||
TransformationType transformation_type(TransformationType::World_Relative_Joint);
|
||||
if (evt.AltDown())
|
||||
transformation_type.set_independent();
|
||||
m_selection.rotate(m_gizmos.get_rotation(), transformation_type);
|
||||
wxGetApp().obj_manipul()->update_settings_value(m_selection);
|
||||
break;
|
||||
}
|
||||
|
|
@ -5403,7 +5415,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
// the gizmo got the event and took some action, no need to do anything more here
|
||||
m_dirty = true;
|
||||
}
|
||||
else if (evt.Dragging() && !gizmos_overlay_contains_mouse)
|
||||
// do not process dragging if the mouse is into any of the HUD elements
|
||||
else if (evt.Dragging() && !gizmos_overlay_contains_mouse && (toolbar_contains_mouse == -1) && (view_toolbar_contains_mouse == -1))
|
||||
{
|
||||
m_mouse.dragging = true;
|
||||
|
||||
|
|
@ -5412,7 +5425,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
if (m_layers_editing.state == LayersEditing::Editing)
|
||||
_perform_layer_editing_action(&evt);
|
||||
}
|
||||
else if (evt.LeftIsDown())
|
||||
// do not process the dragging if the left mouse was set down in another canvas
|
||||
else if (m_mouse.left_down && evt.LeftIsDown())
|
||||
{
|
||||
// if dragging over blank area with left button, rotate
|
||||
#if ENABLE_MOVE_MIN_THRESHOLD
|
||||
|
|
|
|||
|
|
@ -375,6 +375,59 @@ class GLCanvas3D
|
|||
};
|
||||
|
||||
public:
|
||||
class TransformationType
|
||||
{
|
||||
public:
|
||||
enum Enum {
|
||||
// Transforming in a world coordinate system
|
||||
World = 0,
|
||||
// Transforming in a local coordinate system
|
||||
Local = 1,
|
||||
// Absolute transformations, allowed in local coordinate system only.
|
||||
Absolute = 0,
|
||||
// Relative transformations, allowed in both local and world coordinate system.
|
||||
Relative = 2,
|
||||
// For group selection, the transformation is performed as if the group made a single solid body.
|
||||
Joint = 0,
|
||||
// For group selection, the transformation is performed on each object independently.
|
||||
Independent = 4,
|
||||
|
||||
World_Relative_Joint = World | Relative | Joint,
|
||||
World_Relative_Independent = World | Relative | Independent,
|
||||
Local_Absolute_Joint = Local | Absolute | Joint,
|
||||
Local_Absolute_Independent = Local | Absolute | Independent,
|
||||
Local_Relative_Joint = Local | Relative | Joint,
|
||||
Local_Relative_Independent = Local | Relative | Independent,
|
||||
};
|
||||
|
||||
TransformationType() : m_value(World) {}
|
||||
TransformationType(Enum value) : m_value(value) {}
|
||||
TransformationType& operator=(Enum value) { m_value = value; return *this; }
|
||||
|
||||
Enum operator()() const { return m_value; }
|
||||
bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; }
|
||||
|
||||
void set_world() { this->remove(Local); }
|
||||
void set_local() { this->add(Local); }
|
||||
void set_absolute() { this->remove(Relative); }
|
||||
void set_relative() { this->add(Relative); }
|
||||
void set_joint() { this->remove(Independent); }
|
||||
void set_independent() { this->add(Independent); }
|
||||
|
||||
bool world() const { return ! this->has(Local); }
|
||||
bool local() const { return this->has(Local); }
|
||||
bool absolute() const { return ! this->has(Relative); }
|
||||
bool relative() const { return this->has(Relative); }
|
||||
bool joint() const { return ! this->has(Independent); }
|
||||
bool independent() const { return this->has(Independent); }
|
||||
|
||||
private:
|
||||
void add(Enum v) { m_value = Enum((unsigned int)m_value | (unsigned int)v); }
|
||||
void remove(Enum v) { m_value = Enum((unsigned int)m_value & (~(unsigned int)v)); }
|
||||
|
||||
Enum m_value;
|
||||
};
|
||||
|
||||
class Selection
|
||||
{
|
||||
public:
|
||||
|
|
@ -553,7 +606,7 @@ public:
|
|||
void start_dragging();
|
||||
|
||||
void translate(const Vec3d& displacement, bool local = false);
|
||||
void rotate(const Vec3d& rotation, bool local);
|
||||
void rotate(const Vec3d& rotation, TransformationType transformation_type);
|
||||
void flattening_rotate(const Vec3d& normal);
|
||||
void scale(const Vec3d& scale, bool local);
|
||||
void mirror(Axis axis);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
#include "GUI.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_ObjectSettings.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "PresetBundle.hpp"
|
||||
|
||||
|
|
@ -1922,7 +1924,7 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b
|
|||
|
||||
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
|
||||
{
|
||||
return (m_state == On) && (m_model_object != nullptr) && (m_model_object != m_old_model_object) && !m_model_object->instances.empty();
|
||||
return (m_state == On) && (m_model_object != m_old_model_object) && (m_model_object != nullptr) && !m_model_object->instances.empty();
|
||||
|
||||
//if (m_state != On || !m_model_object || m_model_object->instances.empty() || ! m_instance_matrix.isApprox(m_source_data.matrix))
|
||||
// return false;
|
||||
|
|
@ -2161,13 +2163,10 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
return false;
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::delete_selected_points()
|
||||
void GLGizmoSlaSupports::delete_selected_points(bool force)
|
||||
{
|
||||
if (!m_editing_mode)
|
||||
return;
|
||||
|
||||
for (unsigned int idx=0; idx<m_editing_mode_cache.size(); ++idx) {
|
||||
if (m_editing_mode_cache[idx].second && (!m_editing_mode_cache[idx].first.is_new_island || !m_lock_unique_islands)) {
|
||||
if (m_editing_mode_cache[idx].second && (!m_editing_mode_cache[idx].first.is_new_island || !m_lock_unique_islands || force)) {
|
||||
m_editing_mode_cache.erase(m_editing_mode_cache.begin() + (idx--));
|
||||
m_unsaved_changes = true;
|
||||
}
|
||||
|
|
@ -2228,9 +2227,41 @@ void GLGizmoSlaSupports::render_tooltip_texture() const {
|
|||
#endif // not ENABLE_IMGUI
|
||||
|
||||
|
||||
std::vector<ConfigOption*> GLGizmoSlaSupports::get_config_options(const std::vector<std::string>& keys) const
|
||||
{
|
||||
std::vector<ConfigOption*> out;
|
||||
|
||||
if (!m_model_object)
|
||||
return out;
|
||||
|
||||
DynamicPrintConfig& object_cfg = m_model_object->config;
|
||||
DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr;
|
||||
|
||||
for (const std::string& key : keys) {
|
||||
if (object_cfg.has(key))
|
||||
out.push_back(object_cfg.option(key));
|
||||
else
|
||||
if (print_cfg.has(key))
|
||||
out.push_back(print_cfg.option(key));
|
||||
else { // we must get it from defaults
|
||||
if (default_cfg == nullptr)
|
||||
default_cfg.reset(DynamicPrintConfig::new_from_defaults_keys(keys));
|
||||
out.push_back(default_cfg->option(key));
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if ENABLE_IMGUI
|
||||
void GLGizmoSlaSupports::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection)
|
||||
{
|
||||
if (!m_model_object)
|
||||
return;
|
||||
|
||||
bool first_run = true; // This is a hack to redraw the button when all points are removed,
|
||||
// so it is not delayed until the background process finishes.
|
||||
RENDER_AGAIN:
|
||||
|
|
@ -2242,6 +2273,7 @@ RENDER_AGAIN:
|
|||
|
||||
bool force_refresh = false;
|
||||
bool remove_selected = false;
|
||||
bool remove_all = false;
|
||||
|
||||
if (m_editing_mode) {
|
||||
m_imgui->text(_(L("Left mouse click - add point")));
|
||||
|
|
@ -2255,7 +2287,8 @@ RENDER_AGAIN:
|
|||
wxString str = ss.str();
|
||||
|
||||
bool old_combo_state = m_combo_box_open;
|
||||
m_combo_box_open = m_imgui->combo(_(L("Head diameter")), options, str);
|
||||
// The combo is commented out for now, until the feature is supported by backend.
|
||||
// m_combo_box_open = m_imgui->combo(_(L("Head diameter")), options, str);
|
||||
force_refresh |= (old_combo_state != m_combo_box_open);
|
||||
|
||||
float current_number = atof(str);
|
||||
|
|
@ -2279,6 +2312,10 @@ RENDER_AGAIN:
|
|||
remove_selected = m_imgui->button(_(L("Remove selected points")));
|
||||
m_imgui->disabled_end();
|
||||
|
||||
m_imgui->disabled_begin(m_editing_mode_cache.empty());
|
||||
remove_all = m_imgui->button(_(L("Remove all points")));
|
||||
m_imgui->disabled_end();
|
||||
|
||||
m_imgui->text(" "); // vertical gap
|
||||
|
||||
if (m_imgui->button(_(L("Apply changes")))) {
|
||||
|
|
@ -2293,13 +2330,29 @@ RENDER_AGAIN:
|
|||
}
|
||||
}
|
||||
else { // not in editing mode:
|
||||
/*ImGui::PushItemWidth(100.0f);
|
||||
ImGui::PushItemWidth(100.0f);
|
||||
m_imgui->text(_(L("Minimal points distance: ")));
|
||||
ImGui::SameLine();
|
||||
bool value_changed = ImGui::SliderFloat("", &m_minimal_point_distance, 0.f, 20.f, "%.f mm");
|
||||
|
||||
std::vector<ConfigOption*> opts = get_config_options({"support_points_density_relative", "support_points_minimal_distance"});
|
||||
float density = static_cast<ConfigOptionInt*>(opts[0])->value;
|
||||
float minimal_point_distance = static_cast<ConfigOptionFloat*>(opts[1])->value;
|
||||
|
||||
bool value_changed = ImGui::SliderFloat("", &minimal_point_distance, 0.f, 20.f, "%.f mm");
|
||||
if (value_changed)
|
||||
m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
|
||||
|
||||
m_imgui->text(_(L("Support points density: ")));
|
||||
ImGui::SameLine();
|
||||
value_changed |= ImGui::SliderFloat(" ", &m_density, 0.f, 200.f, "%.f %%");*/
|
||||
if (ImGui::SliderFloat(" ", &density, 0.f, 200.f, "%.f %%")) {
|
||||
value_changed = true;
|
||||
m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
|
||||
}
|
||||
|
||||
if (value_changed) { // Update side panel
|
||||
wxGetApp().obj_settings()->UpdateAndShow(true);
|
||||
wxGetApp().obj_list()->update_settings_items();
|
||||
}
|
||||
|
||||
bool generate = m_imgui->button(_(L("Auto-generate points [A]")));
|
||||
|
||||
|
|
@ -2310,6 +2363,12 @@ RENDER_AGAIN:
|
|||
if (m_imgui->button(_(L("Manual editing [M]"))))
|
||||
switch_to_editing_mode();
|
||||
|
||||
m_imgui->disabled_begin(m_editing_mode_cache.empty());
|
||||
remove_all = m_imgui->button(_(L("Remove all points")));
|
||||
m_imgui->disabled_end();
|
||||
|
||||
m_imgui->text("");
|
||||
|
||||
m_imgui->text(m_model_object->sla_points_status == sla::PointsStatus::None ? "No points (will be autogenerated)" :
|
||||
(m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated ? "Autogenerated points (no modifications)" :
|
||||
(m_model_object->sla_points_status == sla::PointsStatus::UserModified ? "User-modified points" :
|
||||
|
|
@ -2324,10 +2383,14 @@ RENDER_AGAIN:
|
|||
}
|
||||
m_old_editing_state = m_editing_mode;
|
||||
|
||||
if (remove_selected) {
|
||||
if (remove_selected || remove_all) {
|
||||
force_refresh = false;
|
||||
m_parent.reload_scene(true);
|
||||
delete_selected_points();
|
||||
if (remove_all)
|
||||
select_point(AllPoints);
|
||||
delete_selected_points(remove_all);
|
||||
if (remove_all && !m_editing_mode)
|
||||
editing_mode_apply_changes();
|
||||
if (first_run) {
|
||||
first_run = false;
|
||||
goto RENDER_AGAIN;
|
||||
|
|
@ -2375,19 +2438,22 @@ void GLGizmoSlaSupports::on_set_state()
|
|||
m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
|
||||
}
|
||||
if (m_state == Off) {
|
||||
if (m_old_state != Off && m_model_object) { // the gizmo was just turned Off
|
||||
if (m_old_state != Off) { // the gizmo was just turned Off
|
||||
|
||||
if (m_unsaved_changes) {
|
||||
wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L("Do you want to save your manually edited support points ?\n")),
|
||||
_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
|
||||
if (dlg.ShowModal() == wxID_YES)
|
||||
editing_mode_apply_changes();
|
||||
else
|
||||
editing_mode_discard_changes();
|
||||
if (m_model_object) {
|
||||
if (m_unsaved_changes) {
|
||||
wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L("Do you want to save your manually edited support points ?\n")),
|
||||
_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
|
||||
if (dlg.ShowModal() == wxID_YES)
|
||||
editing_mode_apply_changes();
|
||||
else
|
||||
editing_mode_discard_changes();
|
||||
}
|
||||
}
|
||||
|
||||
m_parent.toggle_model_objects_visibility(true);
|
||||
m_editing_mode = false; // so it is not active next time the gizmo opens
|
||||
m_editing_mode_cache.clear();
|
||||
}
|
||||
}
|
||||
m_old_state = m_state;
|
||||
|
|
@ -2492,7 +2558,7 @@ void GLGizmoSlaSupports::auto_generate()
|
|||
"Are you sure you want to do it?\n"
|
||||
)), _(L("Warning")), wxICON_WARNING | wxYES | wxNO);
|
||||
|
||||
if (m_model_object->sla_points_status != sla::PointsStatus::UserModified || dlg.ShowModal() == wxID_YES) {
|
||||
if (m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_editing_mode_cache.empty() || dlg.ShowModal() == wxID_YES) {
|
||||
m_model_object->sla_support_points.clear();
|
||||
m_model_object->sla_points_status = sla::PointsStatus::Generating;
|
||||
m_editing_mode_cache.clear();
|
||||
|
|
|
|||
|
|
@ -498,7 +498,7 @@ public:
|
|||
virtual ~GLGizmoSlaSupports();
|
||||
void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection);
|
||||
bool mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down);
|
||||
void delete_selected_points();
|
||||
void delete_selected_points(bool force = false);
|
||||
|
||||
private:
|
||||
bool on_init();
|
||||
|
|
@ -536,6 +536,8 @@ private:
|
|||
int m_canvas_width;
|
||||
int m_canvas_height;
|
||||
|
||||
std::vector<ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
|
||||
|
||||
// Methods that do the model_object and editing cache synchronization,
|
||||
// editing mode selection, etc:
|
||||
enum {
|
||||
|
|
|
|||
|
|
@ -177,33 +177,40 @@ bool GUI_App::OnInit()
|
|||
|
||||
if (this->plater() != nullptr)
|
||||
this->obj_manipul()->update_if_dirty();
|
||||
});
|
||||
|
||||
// On OS X the UI tends to freeze in weird ways if modal dialogs(config wizard, update notifications, ...)
|
||||
// are shown before or in the same event callback with the main frame creation.
|
||||
// Therefore we schedule them for later using CallAfter.
|
||||
CallAfter([this]() {
|
||||
try {
|
||||
if (!preset_updater->config_update())
|
||||
mainframe->Close();
|
||||
} catch (const std::exception &ex) {
|
||||
show_error(nullptr, ex.what());
|
||||
// Preset updating & Configwizard are done after the above initializations,
|
||||
// and after MainFrame is created & shown.
|
||||
// The extra CallAfter() is needed because of Mac, where this is the only way
|
||||
// to popup a modal dialog on start without screwing combo boxes.
|
||||
// This is ugly but I honestly found not better way to do it.
|
||||
// Neither wxShowEvent nor wxWindowCreateEvent work reliably.
|
||||
static bool once = true;
|
||||
if (once) {
|
||||
once = false;
|
||||
|
||||
try {
|
||||
if (!preset_updater->config_update()) {
|
||||
mainframe->Close();
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
show_error(nullptr, ex.what());
|
||||
}
|
||||
|
||||
CallAfter([this] {
|
||||
if (!config_wizard_startup(app_conf_exists)) {
|
||||
// Only notify if there was not wizard so as not to bother too much ...
|
||||
preset_updater->slic3r_update_notify();
|
||||
}
|
||||
preset_updater->sync(preset_bundle);
|
||||
});
|
||||
|
||||
load_current_presets();
|
||||
}
|
||||
});
|
||||
|
||||
CallAfter([this]() {
|
||||
if (!config_wizard_startup(app_conf_exists)) {
|
||||
// Only notify if there was not wizard so as not to bother too much ...
|
||||
preset_updater->slic3r_update_notify();
|
||||
}
|
||||
preset_updater->sync(preset_bundle);
|
||||
|
||||
load_current_presets();
|
||||
});
|
||||
|
||||
|
||||
mainframe->Show(true);
|
||||
return m_initialized = true;
|
||||
m_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned GUI_App::get_colour_approx_luma(const wxColour &colour)
|
||||
|
|
|
|||
|
|
@ -1180,25 +1180,29 @@ Geometry::Transformation volume_to_bed_transformation(const Geometry::Transforma
|
|||
{
|
||||
Geometry::Transformation out;
|
||||
|
||||
// Is the angle close to a multiple of 90 degrees?
|
||||
auto ninety_degrees = [](double a) {
|
||||
a = fmod(std::abs(a), 0.5 * PI);
|
||||
if (a > 0.25 * PI)
|
||||
a = 0.5 * PI - a;
|
||||
return a < 0.001;
|
||||
};
|
||||
if (instance_transformation.is_scaling_uniform()) {
|
||||
// No need to run the non-linear least squares fitting for uniform scaling.
|
||||
// Just set the inverse.
|
||||
out.set_from_transform(instance_transformation.get_matrix(true).inverse());
|
||||
}
|
||||
else
|
||||
{
|
||||
else if (ninety_degrees(instance_transformation.get_rotation().x()) && ninety_degrees(instance_transformation.get_rotation().y()) && ninety_degrees(instance_transformation.get_rotation().z()))
|
||||
{
|
||||
// Anisotropic scaling, rotation by multiples of ninety degrees.
|
||||
Eigen::Matrix3d instance_rotation_trafo =
|
||||
(Eigen::AngleAxisd(instance_transformation.get_rotation().z(), Vec3d::UnitZ()) *
|
||||
Eigen::AngleAxisd(instance_transformation.get_rotation().y(), Vec3d::UnitY()) *
|
||||
Eigen::AngleAxisd(instance_transformation.get_rotation().x(), Vec3d::UnitX())).toRotationMatrix();
|
||||
Eigen::Matrix3d instance_rotation_trafo_inv =
|
||||
(Eigen::AngleAxisd(- instance_transformation.get_rotation().x(), Vec3d::UnitX()) *
|
||||
Eigen::AngleAxisd(- instance_transformation.get_rotation().y(), Vec3d::UnitY()) *
|
||||
Eigen::AngleAxisd(- instance_transformation.get_rotation().z(), Vec3d::UnitZ())).toRotationMatrix();
|
||||
Vec3d euler_angles_inv = Geometry::extract_euler_angles(instance_rotation_trafo_inv);
|
||||
|
||||
Eigen::Matrix3d instance_trafo = instance_rotation_trafo *
|
||||
Eigen::Scaling(instance_transformation.get_scaling_factor().cwiseProduct(instance_transformation.get_mirror()));
|
||||
Eigen::Matrix3d volume_rotation_trafo =
|
||||
(Eigen::AngleAxisd(-instance_transformation.get_rotation().x(), Vec3d::UnitX()) *
|
||||
Eigen::AngleAxisd(-instance_transformation.get_rotation().y(), Vec3d::UnitY()) *
|
||||
Eigen::AngleAxisd(-instance_transformation.get_rotation().z(), Vec3d::UnitZ())).toRotationMatrix();
|
||||
|
||||
// 8 corners of the bounding box.
|
||||
auto pts = Eigen::MatrixXd(8, 3);
|
||||
|
|
@ -1211,101 +1215,27 @@ Geometry::Transformation volume_to_bed_transformation(const Geometry::Transforma
|
|||
pts(6, 0) = bbox.max.x(); pts(6, 1) = bbox.max.y(); pts(6, 2) = bbox.min.z();
|
||||
pts(7, 0) = bbox.max.x(); pts(7, 1) = bbox.max.y(); pts(7, 2) = bbox.max.z();
|
||||
|
||||
// Current parameters: 3x scale, 3x rotation
|
||||
auto beta = Eigen::MatrixXd(3 + 3, 1);
|
||||
beta << 1., 1., 1., euler_angles_inv(0), euler_angles_inv(1), euler_angles_inv(2);
|
||||
// Corners of the bounding box transformed into the modifier mesh coordinate space, with inverse rotation applied to the modifier.
|
||||
auto qs = pts *
|
||||
(instance_rotation_trafo *
|
||||
Eigen::Scaling(instance_transformation.get_scaling_factor().cwiseProduct(instance_transformation.get_mirror())) *
|
||||
volume_rotation_trafo).inverse().transpose();
|
||||
// Fill in scaling based on least squares fitting of the bounding box corners.
|
||||
Vec3d scale;
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
scale(i) = pts.col(i).dot(qs.col(i)) / pts.col(i).dot(pts.col(i));
|
||||
|
||||
{
|
||||
// Trafo from world to the coordinate system of the modifier mesh, with the inverse rotation applied to the modifier.
|
||||
Eigen::Matrix3d A_scaling = instance_trafo * instance_rotation_trafo_inv;
|
||||
// Corners of the bounding box transformed into the modifier mesh coordinate space, with inverse rotation applied to the modifier.
|
||||
auto qs = pts * A_scaling.inverse().transpose();
|
||||
// Fill in scaling based on least squares fitting of the bounding box corners.
|
||||
for (int i = 0; i < 3; ++i)
|
||||
beta(i) = pts.col(i).dot(qs.col(i)) / pts.col(i).dot(pts.col(i));
|
||||
}
|
||||
|
||||
// Jacobian
|
||||
// rows: 8 corners of a cube times 3 dimensions,
|
||||
// cols: 3x scale, 3x rotation
|
||||
auto J = Eigen::MatrixXd(8 * 3, 3 + 3);
|
||||
|
||||
// Until convergence:
|
||||
Eigen::Matrix3d s, dsx, dsy, dsz;
|
||||
Eigen::Matrix3d rx, drx, ry, dry, rz, drz;
|
||||
s.setIdentity();
|
||||
rx.setIdentity(); ry.setIdentity(); rz.setIdentity();
|
||||
dsx.setZero(); dsy.setZero(); dsz.setZero();
|
||||
drx.setZero(); dry.setZero(); drz.setZero();
|
||||
dsx(0, 0) = 1.; dsy(1, 1) = 1.; dsz(2, 2) = 1.;
|
||||
|
||||
// Solve the non-linear Least Squares problem by Levenberg–Marquardt algorithm (modified Gauss–Newton iteration)
|
||||
const double eps = 1.e-7;
|
||||
auto beta_best = beta;
|
||||
double beta_best_error = 1e10;
|
||||
for (size_t iter = 0; iter < 200; ++ iter) {
|
||||
// Current rotation & scaling transformation.
|
||||
auto trafo = instance_trafo *
|
||||
Eigen::AngleAxisd(beta(5), Vec3d::UnitZ()) *
|
||||
Eigen::AngleAxisd(beta(4), Vec3d::UnitY()) *
|
||||
Eigen::AngleAxisd(beta(3), Vec3d::UnitX()) *
|
||||
Eigen::Scaling(Vec3d(beta(0), beta(1), beta(2)));
|
||||
// Current error after rotation & scaling.
|
||||
auto dy = (pts - pts * trafo.transpose()).eval();
|
||||
double err = 0;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
err += dy.row(i).norm();
|
||||
if (err < beta_best_error) {
|
||||
beta_best = beta;
|
||||
beta_best_error = err;
|
||||
}
|
||||
// Fill in the Jacobian at current beta.
|
||||
double cos_rx = cos(beta(3));
|
||||
double sin_rx = sin(beta(3));
|
||||
double cos_ry = cos(beta(4));
|
||||
double sin_ry = sin(beta(4));
|
||||
double cos_rz = cos(beta(5));
|
||||
double sin_rz = sin(beta(5));
|
||||
rx << 1., 0., 0., 0., cos_rx, -sin_rx, 0., sin_rx, cos_rx;
|
||||
drx << 0., 0., 0., 0., -sin_rx, -cos_rx, 0., cos_rx, -sin_rx;
|
||||
ry << cos_ry, 0., sin_ry, 0., 1., 0., -sin_ry, 0., cos_ry;
|
||||
dry << -sin_ry, 0., cos_ry, 0., 0., 0., -cos_ry, 0., -sin_ry;
|
||||
rz << cos_rz, -sin_rz, 0., sin_rz, cos_rz, 0., 0., 0., 1.;
|
||||
drz << -sin_rz, -cos_rz, 0., cos_rz, -sin_rz, 0., 0., 0., 0.;
|
||||
s(0, 0) = beta(0);
|
||||
s(1, 1) = beta(1);
|
||||
s(2, 2) = beta(2);
|
||||
auto rot = (instance_trafo * rz * ry * rx).eval();
|
||||
auto jrx = pts * (instance_trafo * rz * ry * drx * s).transpose();
|
||||
auto jry = pts * (instance_trafo * rz * dry * rx * s).transpose();
|
||||
auto jrz = pts * (instance_trafo * drz * ry * rx * s).transpose();
|
||||
for (int r = 0; r < 8; ++ r) {
|
||||
for (int i = 0; i < 3; ++ i) {
|
||||
J(r * 3 + i, 0) = rot(i, 0) * pts(r, 0);
|
||||
J(r * 3 + i, 1) = rot(i, 1) * pts(r, 1);
|
||||
J(r * 3 + i, 2) = rot(i, 2) * pts(r, 2);
|
||||
J(r * 3 + i, 3) = jrx(r, i);
|
||||
J(r * 3 + i, 4) = jry(r, i);
|
||||
J(r * 3 + i, 5) = jrz(r, i);
|
||||
}
|
||||
}
|
||||
// Solving the normal equations for delta beta.
|
||||
auto rhs = (J.transpose() * Eigen::Map<Eigen::VectorXd>(dy.data(), dy.size())).eval();
|
||||
double lambda = 1.; // 0.01;
|
||||
auto A = (J.transpose() * J + Eigen::Matrix<double, 6, 6>::Identity() * lambda).eval();
|
||||
auto L = A.ldlt();
|
||||
auto delta_beta = L.solve(rhs).eval();
|
||||
// Check for convergence.
|
||||
auto delta_beta_max = delta_beta.cwiseAbs().maxCoeff();
|
||||
if (delta_beta_max < eps)
|
||||
break;
|
||||
beta = beta + delta_beta;
|
||||
}
|
||||
|
||||
out.set_rotation(Vec3d(beta_best(3), beta_best(4), beta_best(5)));
|
||||
out.set_scaling_factor(Vec3d(std::abs(beta_best(0)), std::abs(beta_best(1)), std::abs(beta_best(2))));
|
||||
out.set_mirror(Vec3d(beta_best(0) > 0 ? 1. : -1, beta_best(1) > 0 ? 1. : -1, beta_best(2) > 0 ? 1. : -1));
|
||||
out.set_rotation(Geometry::extract_euler_angles(volume_rotation_trafo));
|
||||
out.set_scaling_factor(Vec3d(std::abs(scale(0)), std::abs(scale(1)), std::abs(scale(2))));
|
||||
out.set_mirror(Vec3d(scale(0) > 0 ? 1. : -1, scale(1) > 0 ? 1. : -1, scale(2) > 0 ? 1. : -1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// General anisotropic scaling, general rotation.
|
||||
// Keep the modifier mesh in the instance coordinate system, so the modifier mesh will not be aligned with the world.
|
||||
// Scale it to get the required size.
|
||||
out.set_scaling_factor(instance_transformation.get_scaling_factor().cwiseInverse());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -361,16 +361,21 @@ void ObjectManipulation::change_rotation_value(const Vec3d& rotation)
|
|||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
const GLCanvas3D::Selection& selection = canvas->get_selection();
|
||||
|
||||
Vec3d delta_rotation = rotation - m_cache.rotation;
|
||||
GLCanvas3D::TransformationType transformation_type(GLCanvas3D::TransformationType::World_Relative_Joint);
|
||||
if (selection.is_single_full_instance() || selection.requires_local_axes())
|
||||
transformation_type.set_independent();
|
||||
if (selection.is_single_full_instance()) {
|
||||
//FIXME GLCanvas3D::Selection::rotate() does not process absoulte rotations correctly: It does not recognize the axis index, which was changed.
|
||||
// transformation_type.set_absolute();
|
||||
transformation_type.set_local();
|
||||
}
|
||||
|
||||
Vec3d rad_rotation;
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
rad_rotation(i) = Geometry::deg2rad(delta_rotation(i));
|
||||
}
|
||||
rad_rotation(i) = Geometry::deg2rad((transformation_type.absolute()) ? rotation(i) : rotation(i) - m_cache.rotation(i));
|
||||
|
||||
canvas->get_selection().start_dragging();
|
||||
canvas->get_selection().rotate(rad_rotation, selection.is_single_full_instance() || selection.requires_local_axes());
|
||||
canvas->get_selection().rotate(rad_rotation, transformation_type);
|
||||
canvas->do_rotate();
|
||||
|
||||
m_cache.rotation = rotation;
|
||||
|
|
|
|||
|
|
@ -416,11 +416,14 @@ void Preview::load_print()
|
|||
load_print_as_sla();
|
||||
}
|
||||
|
||||
void Preview::reload_print(bool force)
|
||||
void Preview::reload_print(bool force, bool keep_volumes)
|
||||
{
|
||||
m_canvas->reset_volumes();
|
||||
m_canvas->reset_legend_texture();
|
||||
m_loaded = false;
|
||||
if (!keep_volumes)
|
||||
{
|
||||
m_canvas->reset_volumes();
|
||||
m_canvas->reset_legend_texture();
|
||||
m_loaded = false;
|
||||
}
|
||||
|
||||
if (!IsShown() && !force)
|
||||
return;
|
||||
|
|
@ -644,7 +647,7 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool for
|
|||
|
||||
bool color_print_enable = (wxGetApp().plater()->printer_technology() == ptFFF);
|
||||
if (color_print_enable) {
|
||||
const auto& cfg = wxGetApp().preset_bundle->full_config();
|
||||
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
if (cfg.opt<ConfigOptionFloats>("nozzle_diameter")->values.size() > 1)
|
||||
color_print_enable = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ public:
|
|||
void set_drop_target(wxDropTarget* target);
|
||||
|
||||
void load_print();
|
||||
void reload_print(bool force = false);
|
||||
void reload_print(bool force = false, bool keep_volumes = false);
|
||||
void refresh_print();
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -2001,6 +2001,9 @@ void Plater::priv::schedule_background_process()
|
|||
this->background_process_timer.Start(500, wxTIMER_ONE_SHOT);
|
||||
// Notify the Canvas3D that something has changed, so it may invalidate some of the layer editing stuff.
|
||||
this->view3D->get_canvas3d()->set_config(this->config);
|
||||
// Reset gcode preview
|
||||
this->preview->get_canvas3d()->reset_volumes();
|
||||
this->preview->get_canvas3d()->reset_legend_texture();
|
||||
}
|
||||
|
||||
void Plater::priv::update_print_volume_state()
|
||||
|
|
@ -2265,7 +2268,8 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
|||
else if (current_panel == preview)
|
||||
{
|
||||
this->q->reslice();
|
||||
preview->reload_print();
|
||||
// keeps current gcode preview, if any
|
||||
preview->reload_print(false, true);
|
||||
preview->set_canvas_as_dirty();
|
||||
view_toolbar.select_item("Preview");
|
||||
}
|
||||
|
|
@ -2984,7 +2988,7 @@ void Plater::export_gcode()
|
|||
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
|
||||
auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string());
|
||||
|
||||
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save Zip file as:")),
|
||||
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save SL1 file as:")),
|
||||
start_dir,
|
||||
from_path(default_output_file.filename()),
|
||||
GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP, default_output_file.extension().string()),
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
|
|||
const std::vector<std::string>& Preset::print_options()
|
||||
{
|
||||
static std::vector<std::string> s_opts {
|
||||
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "top_solid_layers", "bottom_solid_layers",
|
||||
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "top_solid_layers", "bottom_solid_layers",
|
||||
"extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",
|
||||
"seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
|
||||
"infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",
|
||||
|
|
@ -460,6 +460,7 @@ const std::vector<std::string>& Preset::sla_print_options()
|
|||
"support_object_elevation",
|
||||
"support_points_density_relative",
|
||||
"support_points_minimal_distance",
|
||||
"slice_closing_radius",
|
||||
"pad_enable",
|
||||
"pad_wall_thickness",
|
||||
"pad_wall_height",
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
|||
btn_cancel->Disable();
|
||||
btn_error = new wxButton(this, wxID_ANY, _(L("Show error message")));
|
||||
btn_error->Disable();
|
||||
auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close")));
|
||||
auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close"))); // Note: The label needs to be present, otherwise we get accelerator bugs on Mac
|
||||
btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING);
|
||||
btnsizer->Add(btn_error, 0);
|
||||
btnsizer->AddStretchSpacer();
|
||||
|
|
|
|||
|
|
@ -1108,12 +1108,14 @@ void TabPrint::build()
|
|||
optgroup = page->new_optgroup(_(L("Flow")));
|
||||
optgroup->append_single_option_line("bridge_flow_ratio");
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Slicing")));
|
||||
optgroup->append_single_option_line("slice_closing_radius");
|
||||
optgroup->append_single_option_line("resolution");
|
||||
optgroup->append_single_option_line("xy_size_compensation");
|
||||
optgroup->append_single_option_line("elefant_foot_compensation");
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Other")));
|
||||
optgroup->append_single_option_line("clip_multipart_objects");
|
||||
optgroup->append_single_option_line("elefant_foot_compensation");
|
||||
optgroup->append_single_option_line("xy_size_compensation");
|
||||
// # optgroup->append_single_option_line("threads");
|
||||
optgroup->append_single_option_line("resolution");
|
||||
|
||||
page = add_options_page(_(L("Output options")), "page_white_go.png");
|
||||
optgroup = page->new_optgroup(_(L("Sequential printing")));
|
||||
|
|
@ -3292,6 +3294,10 @@ void TabSLAPrint::build()
|
|||
// optgroup->append_single_option_line("pad_edge_radius");
|
||||
optgroup->append_single_option_line("pad_wall_slope");
|
||||
|
||||
page = add_options_page(_(L("Advanced")), "wrench.png");
|
||||
optgroup = page->new_optgroup(_(L("Slicing")));
|
||||
optgroup->append_single_option_line("slice_closing_radius");
|
||||
|
||||
page = add_options_page(_(L("Output options")), "page_white_go.png");
|
||||
optgroup = page->new_optgroup(_(L("Output file")));
|
||||
Option option = optgroup->get_option("output_filename_format");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue