Merge branch 'master' of https://github.com/Prusa3d/PrusaSlicer
This commit is contained in:
commit
0bdfb6ab92
@ -35,14 +35,15 @@ void TriangleSelector::Triangle::set_division(int sides_to_split, int special_si
|
||||
|
||||
void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
||||
const Vec3f& source, const Vec3f& dir,
|
||||
float radius, EnforcerBlockerType new_state)
|
||||
float radius, CursorType cursor_type,
|
||||
EnforcerBlockerType new_state)
|
||||
{
|
||||
assert(facet_start < m_orig_size_indices);
|
||||
assert(is_approx(dir.norm(), 1.f));
|
||||
|
||||
// Save current cursor center, squared radius and camera direction,
|
||||
// so we don't have to pass it around.
|
||||
m_cursor = {hit, source, dir, radius*radius};
|
||||
m_cursor = {hit, source, dir, radius*radius, cursor_type};
|
||||
|
||||
// In case user changed cursor size since last time, update triangle edge limit.
|
||||
if (m_old_cursor_radius != radius) {
|
||||
@ -61,7 +62,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
||||
// add neighboring facets to list to be proccessed later
|
||||
for (int n=0; n<3; ++n) {
|
||||
int neighbor_idx = m_mesh->stl.neighbors_start[facet].neighbor[n];
|
||||
if (neighbor_idx >=0 && faces_camera(neighbor_idx))
|
||||
if (neighbor_idx >=0 && (m_cursor.type == SPHERE || faces_camera(neighbor_idx)))
|
||||
facets_to_check.push_back(neighbor_idx);
|
||||
}
|
||||
}
|
||||
@ -206,8 +207,12 @@ void TriangleSelector::split_triangle(int facet_idx)
|
||||
// Calculate distance of a point from a line.
|
||||
bool TriangleSelector::is_point_inside_cursor(const Vec3f& point) const
|
||||
{
|
||||
Vec3f diff = m_cursor.center - point;
|
||||
return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr;
|
||||
Vec3f diff = m_cursor.center - point;
|
||||
|
||||
if (m_cursor.type == CIRCLE)
|
||||
return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr;
|
||||
else // SPHERE
|
||||
return diff.squaredNorm() < m_cursor.radius_sqr;
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,6 +17,11 @@ enum class EnforcerBlockerType : int8_t;
|
||||
// to recursively subdivide the triangles and make the selection finer.
|
||||
class TriangleSelector {
|
||||
public:
|
||||
enum CursorType {
|
||||
CIRCLE,
|
||||
SPHERE
|
||||
};
|
||||
|
||||
void set_edge_limit(float edge_limit);
|
||||
|
||||
// Create new object on a TriangleMesh. The referenced mesh must
|
||||
@ -29,6 +34,7 @@ public:
|
||||
const Vec3f& source, // camera position (mesh coords)
|
||||
const Vec3f& dir, // direction of the ray (mesh coords)
|
||||
float radius, // radius of the cursor
|
||||
CursorType type, // current type of cursor
|
||||
EnforcerBlockerType new_state); // enforcer or blocker?
|
||||
|
||||
// Get facets currently in the given state.
|
||||
@ -127,6 +133,7 @@ protected:
|
||||
Vec3f source;
|
||||
Vec3f dir;
|
||||
float radius_sqr;
|
||||
CursorType type;
|
||||
};
|
||||
|
||||
Cursor m_cursor;
|
||||
|
@ -566,20 +566,20 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin
|
||||
, list_type(new StringList(this))
|
||||
, list_vendor(new StringList(this))
|
||||
, list_profile(new PresetList(this))
|
||||
, compatible_printers(new wxStaticText(this, wxID_ANY, _(L(""))))
|
||||
{
|
||||
append_spacer(VERTICAL_SPACING);
|
||||
|
||||
const int em = parent->em_unit();
|
||||
const int list_h = 30*em;
|
||||
|
||||
list_printer->SetWindowStyle(wxLB_EXTENDED);
|
||||
|
||||
list_printer->SetMinSize(wxSize(23*em, list_h));
|
||||
list_type->SetMinSize(wxSize(8*em, list_h));
|
||||
list_vendor->SetMinSize(wxSize(13*em, list_h));
|
||||
list_profile->SetMinSize(wxSize(23*em, list_h));
|
||||
|
||||
|
||||
|
||||
grid = new wxFlexGridSizer(4, em/2, em);
|
||||
grid->AddGrowableCol(3, 1);
|
||||
grid->AddGrowableRow(1, 1);
|
||||
@ -601,17 +601,19 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin
|
||||
btn_sizer->Add(sel_none);
|
||||
|
||||
|
||||
grid->Add(new wxBoxSizer(wxHORIZONTAL));
|
||||
grid->Add(new wxBoxSizer(wxHORIZONTAL));
|
||||
grid->Add(new wxBoxSizer(wxHORIZONTAL));
|
||||
grid->Add(btn_sizer, 0, wxALIGN_RIGHT);
|
||||
|
||||
auto* notes_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
notes_sizer->Add(compatible_printers);
|
||||
grid->Add(notes_sizer);
|
||||
|
||||
|
||||
append(grid, 1, wxEXPAND);
|
||||
|
||||
append_spacer(VERTICAL_SPACING);
|
||||
|
||||
html_window = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition,
|
||||
wxSize(60 * em, 20 * em), wxHW_SCROLLBAR_AUTO);
|
||||
append(html_window, 0, wxEXPAND);
|
||||
|
||||
list_printer->Bind(wxEVT_LISTBOX, [this](wxCommandEvent& evt) {
|
||||
update_lists(evt.GetInt(), list_type->GetSelection(), list_vendor->GetSelection());
|
||||
});
|
||||
@ -627,28 +629,25 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin
|
||||
|
||||
sel_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(true); });
|
||||
sel_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(false); });
|
||||
|
||||
/*
|
||||
Bind(wxEVT_PAINT, [this](wxPaintEvent& evt) {on_paint();});
|
||||
|
||||
list_profile->Bind(wxEVT_MOTION, [this](wxMouseEvent& evt) { on_mouse_move_on_profiles(evt); });
|
||||
list_profile->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent& evt) { on_mouse_enter_profiles(evt); });
|
||||
list_profile->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& evt) { on_mouse_leave_profiles(evt); });
|
||||
|
||||
*/
|
||||
reload_presets();
|
||||
set_compatible_printers_html_window(std::vector<std::string>(), false);
|
||||
}
|
||||
void PageMaterials::on_paint()
|
||||
{
|
||||
if (first_paint) {
|
||||
first_paint = false;
|
||||
prepare_compatible_printers_label();
|
||||
}
|
||||
}
|
||||
void PageMaterials::on_mouse_move_on_profiles(wxMouseEvent& evt)
|
||||
{
|
||||
const wxClientDC dc(list_profile);
|
||||
const wxPoint pos = evt.GetLogicalPosition(dc);
|
||||
int item = list_profile->HitTest(pos);
|
||||
BOOST_LOG_TRIVIAL(error) << "hit test: " << item;
|
||||
//BOOST_LOG_TRIVIAL(debug) << "hit test: " << item;
|
||||
on_material_hovered(item);
|
||||
}
|
||||
void PageMaterials::on_mouse_enter_profiles(wxMouseEvent& evt)
|
||||
@ -666,10 +665,11 @@ void PageMaterials::reload_presets()
|
||||
for (const Preset* printer : materials->printers) {
|
||||
list_printer->append(printer->name, &printer->name);
|
||||
}
|
||||
|
||||
sort_list_data(list_printer, true, false);
|
||||
if (list_printer->GetCount() > 0) {
|
||||
list_printer->SetSelection(0);
|
||||
sel_printer_prev = wxNOT_FOUND;
|
||||
sel_printer_count_prev = wxNOT_FOUND;
|
||||
sel_printer_item_prev = wxNOT_FOUND;
|
||||
sel_type_prev = wxNOT_FOUND;
|
||||
sel_vendor_prev = wxNOT_FOUND;
|
||||
update_lists(0, 0, 0);
|
||||
@ -678,34 +678,105 @@ void PageMaterials::reload_presets()
|
||||
presets_loaded = true;
|
||||
}
|
||||
|
||||
void PageMaterials::prepare_compatible_printers_label()
|
||||
void PageMaterials::set_compatible_printers_html_window(const std::vector<std::string>& printer_names, bool all_printers)
|
||||
{
|
||||
assert(grid->GetColWidths().size() == 4);
|
||||
compatible_printers_width = grid->GetColWidths()[3];
|
||||
empty_printers_label = "Compatible printers:";
|
||||
for (const Preset* printer : materials->printers) {
|
||||
empty_printers_label += "\n";
|
||||
//Slic3r::GUI::wxGetApp().dark_mode()
|
||||
const auto bgr_clr =
|
||||
#if defined(__APPLE__)
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
#else
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_MENU);
|
||||
#endif
|
||||
const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
|
||||
const auto text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
|
||||
wxString first_line = L"Profiles marked with * are not compatible with all installed printers.";
|
||||
wxString text;
|
||||
if (all_printers) {
|
||||
text = wxString::Format(
|
||||
"<html>"
|
||||
"<style>"
|
||||
"table{border-spacing: 1px;}"
|
||||
"</style>"
|
||||
"<body bgcolor= %s>"
|
||||
"<font color=%s>"
|
||||
"<font size=\"3\">"
|
||||
"%s<br /><br /> All installed printers are compatible with selected profile."
|
||||
"</font>"
|
||||
"</font>"
|
||||
"</body>"
|
||||
"</html>"
|
||||
, bgr_clr_str
|
||||
, text_clr_str
|
||||
, first_line
|
||||
);
|
||||
} else {
|
||||
wxString second_line = L"Compatible printers:";
|
||||
text = wxString::Format(
|
||||
"<html>"
|
||||
"<style>"
|
||||
"table{border-spacing: 1px;}"
|
||||
"</style>"
|
||||
"<body bgcolor= %s>"
|
||||
"<font color=%s>"
|
||||
"<font size=\"3\">"
|
||||
"%s<br /><br />%s"
|
||||
"<table>"
|
||||
"<tr>"
|
||||
, bgr_clr_str
|
||||
, text_clr_str
|
||||
, first_line
|
||||
, second_line);
|
||||
for (int i = 0; i < printer_names.size(); ++i)
|
||||
{
|
||||
text += wxString::Format("<td>%s</td>", boost::nowide::widen(printer_names[i]));
|
||||
if (i % 3 == 2) {
|
||||
text += wxString::Format(
|
||||
"</tr>"
|
||||
"<tr>");
|
||||
}
|
||||
}
|
||||
text += wxString::Format(
|
||||
"</tr>"
|
||||
"</table>"
|
||||
"</font>"
|
||||
"</font>"
|
||||
"</body>"
|
||||
"</html>"
|
||||
);
|
||||
}
|
||||
clear_compatible_printers_label();
|
||||
|
||||
wxFont font = get_default_font_for_dpi(this, get_dpi_for_window(this));
|
||||
const int fs = font.GetPointSize();
|
||||
int size[] = { fs,fs,fs,fs,fs,fs,fs };
|
||||
html_window->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
|
||||
html_window->SetPage(text);
|
||||
}
|
||||
|
||||
void PageMaterials::clear_compatible_printers_label()
|
||||
{
|
||||
compatible_printers->SetLabel(boost::nowide::widen(empty_printers_label));
|
||||
compatible_printers->Wrap(compatible_printers_width);
|
||||
Layout();
|
||||
set_compatible_printers_html_window(std::vector<std::string>(), false);
|
||||
}
|
||||
|
||||
void PageMaterials::on_material_hovered(int sel_material)
|
||||
{
|
||||
if ( sel_material == last_hovered_item)
|
||||
|
||||
}
|
||||
|
||||
void PageMaterials::on_material_highlighted(int sel_material)
|
||||
{
|
||||
if (sel_material == last_hovered_item)
|
||||
return;
|
||||
if (sel_material == -1) {
|
||||
clear_compatible_printers_label();
|
||||
return;
|
||||
}
|
||||
last_hovered_item = sel_material;
|
||||
std::string compatible_printers_label = "compatible printers:\n";
|
||||
std::string compatible_printers_label = "Compatible printers:\n";
|
||||
std::vector<std::string> tabs;
|
||||
tabs.push_back(std::string());
|
||||
tabs.push_back(std::string());
|
||||
tabs.push_back(std::string());
|
||||
//selected material string
|
||||
std::string material_name = list_profile->get_data(sel_material);
|
||||
// get material preset
|
||||
@ -716,70 +787,16 @@ void PageMaterials::on_material_hovered(int sel_material)
|
||||
return;
|
||||
}
|
||||
//find matching printers
|
||||
bool first = true;
|
||||
std::vector<std::string> names;
|
||||
for (const Preset* printer : materials->printers) {
|
||||
bool compatible = false;
|
||||
for (const Preset* material : matching_materials) {
|
||||
if (is_compatible_with_printer(PresetWithVendorProfile(*material, material->vendor), PresetWithVendorProfile(*printer, printer->vendor))) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
compatible_printers_label += "\n";//", ";
|
||||
compatible_printers_label += printer->name;
|
||||
compatible = true;
|
||||
names.push_back(printer->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this->compatible_printers->SetLabel(boost::nowide::widen(compatible_printers_label));
|
||||
this->compatible_printers->Wrap(compatible_printers_width);
|
||||
}
|
||||
|
||||
void PageMaterials::on_material_highlighted(int sel_material)
|
||||
{
|
||||
wxWindowUpdateLocker freeze_guard(this);
|
||||
(void)freeze_guard;
|
||||
|
||||
//std::string compatible_printers_label = "compatible printers:\n";
|
||||
//std::string empty_suplement = std::string();
|
||||
//unselect all printers
|
||||
list_printer->SetSelection(wxNOT_FOUND);
|
||||
//selected material string
|
||||
std::string material_name = list_profile->get_data(sel_material);
|
||||
// get material preset
|
||||
const std::vector<const Preset*> matching_materials = materials->get_presets_by_alias(material_name);
|
||||
if (matching_materials.empty())
|
||||
return;
|
||||
//find matching printers
|
||||
//bool first = true;
|
||||
for (const Preset* printer : materials->printers) {
|
||||
bool compatible = false;
|
||||
for (const Preset* material : matching_materials) {
|
||||
if (is_compatible_with_printer(PresetWithVendorProfile(*material, material->vendor), PresetWithVendorProfile(*printer, printer->vendor))) {
|
||||
//select printer
|
||||
int index = list_printer->find(printer->name);
|
||||
list_printer->SetSelection(index);
|
||||
/*if (first)
|
||||
first = false;
|
||||
else
|
||||
compatible_printers_label += "\n";//", ";
|
||||
compatible_printers_label += printer->name;
|
||||
compatible = true;
|
||||
break;*/
|
||||
}
|
||||
}
|
||||
//if(!compatible)
|
||||
// empty_suplement += std::string(printer->name.length() + 2, ' ');
|
||||
}
|
||||
// fill rest of label with blanks so it maintains legth
|
||||
//compatible_printers_label += empty_suplement;
|
||||
|
||||
update_lists(0,0,0);
|
||||
list_profile->SetSelection(list_profile->find(material_name));
|
||||
|
||||
//this->compatible_printers->SetLabel(boost::nowide::widen(compatible_printers_label));
|
||||
//this->compatible_printers->Wrap(compatible_printers_width);
|
||||
//Refresh();
|
||||
set_compatible_printers_html_window(names, names.size() == materials->printers.size());
|
||||
}
|
||||
|
||||
void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
|
||||
@ -790,7 +807,7 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
|
||||
wxArrayInt sel_printers;
|
||||
int sel_printers_count = list_printer->GetSelections(sel_printers);
|
||||
|
||||
if (sel_printers_count != sel_printer_prev) {
|
||||
if (sel_printers_count != sel_printer_count_prev || (sel_printers_count == 1 && sel_printer_item_prev != sel_printer && sel_printer != -1)) {
|
||||
// Refresh type list
|
||||
list_type->Clear();
|
||||
list_type->append(_(L("(All)")), &EMPTY);
|
||||
@ -827,6 +844,7 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
|
||||
//clear selection except "ALL"
|
||||
list_printer->SetSelection(wxNOT_FOUND);
|
||||
list_printer->SetSelection(0);
|
||||
sel_printers_count = list_printer->GetSelections(sel_printers);
|
||||
|
||||
materials->filter_presets(nullptr, EMPTY, EMPTY, [this](const Preset* p) {
|
||||
const std::string& type = this->materials->get_type(p);
|
||||
@ -835,10 +853,11 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sort_list_data(list_type, true, true);
|
||||
}
|
||||
|
||||
sel_printer_prev = sel_printers_count;
|
||||
sel_printer_count_prev = sel_printers_count;
|
||||
sel_printer_item_prev = sel_printer;
|
||||
sel_type = 0;
|
||||
sel_type_prev = wxNOT_FOUND;
|
||||
list_type->SetSelection(sel_type);
|
||||
@ -872,6 +891,7 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
|
||||
}
|
||||
});
|
||||
}
|
||||
sort_list_data(list_vendor, true, false);
|
||||
}
|
||||
|
||||
sel_type_prev = sel_type;
|
||||
@ -905,7 +925,6 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
|
||||
//size_t printer_counter = materials->get_printer_counter(p);
|
||||
int cur_i = list_profile->find(p->alias);
|
||||
if (cur_i == wxNOT_FOUND)
|
||||
//cur_i = list_profile->append(p->alias + " " + std::to_string(printer_counter)/*+ (omnipresent ? "" : " ONLY SOME PRINTERS")*/, &p->alias);
|
||||
cur_i = list_profile->append(p->alias + (materials->get_omnipresent(p) ? "" : " *"), &p->alias);
|
||||
else
|
||||
was_checked = list_profile->IsChecked(cur_i);
|
||||
@ -925,12 +944,103 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
|
||||
wizard_p()->appconfig_new.set(section, p->name, "1");
|
||||
});
|
||||
}
|
||||
sort_list_data(list_profile);
|
||||
}
|
||||
|
||||
sel_vendor_prev = sel_vendor;
|
||||
}
|
||||
}
|
||||
|
||||
void PageMaterials::sort_list_data(StringList* list, bool add_All_item, bool material_type_ordering)
|
||||
{
|
||||
// get data from list
|
||||
// sort data
|
||||
// first should be <all>
|
||||
// then prusa profiles
|
||||
// then the rest
|
||||
// in alphabetical order
|
||||
|
||||
std::vector<std::reference_wrapper<const std::string>> prusa_profiles;
|
||||
std::vector<std::reference_wrapper<const std::string>> other_profiles;
|
||||
for (int i = 0 ; i < list->size(); ++i) {
|
||||
const std::string& data = list->get_data(i);
|
||||
if (data == EMPTY) // do not sort <all> item
|
||||
continue;
|
||||
if (!material_type_ordering && data.find("Prusa") != std::string::npos)
|
||||
prusa_profiles.push_back(data);
|
||||
else
|
||||
other_profiles.push_back(data);
|
||||
}
|
||||
if(material_type_ordering) {
|
||||
|
||||
const ConfigOptionDef* def = print_config_def.get("filament_type");
|
||||
std::vector<std::string>enum_values = def->enum_values;
|
||||
int end_of_sorted = 0;
|
||||
for (size_t vals = 0; vals < enum_values.size(); vals++) {
|
||||
for (size_t profs = end_of_sorted; profs < other_profiles.size(); profs++)
|
||||
{
|
||||
// find instead compare because PET vs PETG
|
||||
if (other_profiles[profs].get().find(enum_values[vals]) != std::string::npos) {
|
||||
//swap
|
||||
if(profs != end_of_sorted) {
|
||||
std::reference_wrapper<const std::string> aux = other_profiles[end_of_sorted];
|
||||
other_profiles[end_of_sorted] = other_profiles[profs];
|
||||
other_profiles[profs] = aux;
|
||||
}
|
||||
end_of_sorted++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::sort(prusa_profiles.begin(), prusa_profiles.end(), [](std::reference_wrapper<const std::string> a, std::reference_wrapper<const std::string> b) {
|
||||
return a.get() < b.get();
|
||||
});
|
||||
std::sort(other_profiles.begin(), other_profiles.end(), [](std::reference_wrapper<const std::string> a, std::reference_wrapper<const std::string> b) {
|
||||
return a.get() < b.get();
|
||||
});
|
||||
}
|
||||
|
||||
list->Clear();
|
||||
if (add_All_item)
|
||||
list->append(_(L("(All)")), &EMPTY);
|
||||
for (const auto& item : prusa_profiles)
|
||||
list->append(item, &const_cast<std::string&>(item.get()));
|
||||
for (const auto& item : other_profiles)
|
||||
list->append(item, &const_cast<std::string&>(item.get()));
|
||||
}
|
||||
|
||||
void PageMaterials::sort_list_data(PresetList* list)
|
||||
{
|
||||
// sort data
|
||||
// then prusa profiles
|
||||
// then the rest
|
||||
// in alphabetical order
|
||||
std::vector<std::reference_wrapper<const std::string>> prusa_profiles;
|
||||
std::vector<std::reference_wrapper<const std::string>> other_profiles;
|
||||
for (int i = 0; i < list->size(); ++i) {
|
||||
const std::string& data = list->get_data(i);
|
||||
if (data == EMPTY) // do not sort <all> item
|
||||
continue;
|
||||
if (data.find("Prusa") != std::string::npos)
|
||||
prusa_profiles.push_back(data);
|
||||
else
|
||||
other_profiles.push_back(data);
|
||||
}
|
||||
std::sort(prusa_profiles.begin(), prusa_profiles.end(), [](std::reference_wrapper<const std::string> a, std::reference_wrapper<const std::string> b) {
|
||||
return a.get() < b.get();
|
||||
});
|
||||
std::sort(other_profiles.begin(), other_profiles.end(), [](std::reference_wrapper<const std::string> a, std::reference_wrapper<const std::string> b) {
|
||||
return a.get() < b.get();
|
||||
});
|
||||
list->Clear();
|
||||
for (const auto& item : prusa_profiles)
|
||||
list->append(item, &const_cast<std::string&>(item.get()));
|
||||
for (const auto& item : other_profiles)
|
||||
list->append(item, &const_cast<std::string&>(item.get()));
|
||||
|
||||
}
|
||||
|
||||
void PageMaterials::select_material(int i)
|
||||
{
|
||||
const bool checked = list_profile->IsChecked(i);
|
||||
@ -959,7 +1069,8 @@ void PageMaterials::clear()
|
||||
list_type->Clear();
|
||||
list_vendor->Clear();
|
||||
list_profile->Clear();
|
||||
sel_printer_prev = wxNOT_FOUND;
|
||||
sel_printer_count_prev = wxNOT_FOUND;
|
||||
sel_printer_item_prev = wxNOT_FOUND;
|
||||
sel_type_prev = wxNOT_FOUND;
|
||||
sel_vendor_prev = wxNOT_FOUND;
|
||||
presets_loaded = false;
|
||||
@ -1546,7 +1657,7 @@ const std::string Materials::UNKNOWN = "(Unknown)";
|
||||
|
||||
void Materials::push(const Preset *preset)
|
||||
{
|
||||
presets.emplace_back(preset, 0);
|
||||
presets.emplace_back(preset);
|
||||
types.insert(technology & T_FFF
|
||||
? Materials::get_filament_type(preset)
|
||||
: Materials::get_material_type(preset));
|
||||
@ -1562,6 +1673,7 @@ void Materials::clear()
|
||||
presets.clear();
|
||||
types.clear();
|
||||
printers.clear();
|
||||
compatibility_counter.clear();
|
||||
}
|
||||
|
||||
const std::string& Materials::appconfig_section() const
|
||||
@ -1855,13 +1967,45 @@ void ConfigWizard::priv::update_materials(Technology technology)
|
||||
if (!filament.alias.empty())
|
||||
aliases_fff[filament.alias].insert(filament.name);
|
||||
}
|
||||
filaments.add_printer_counter(&filament);
|
||||
filaments.add_printer(&printer);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// count compatible printers
|
||||
for (const auto& preset : filaments.presets) {
|
||||
|
||||
const auto filter = [preset](const std::pair<std::string, size_t> element) {
|
||||
return preset->alias == element.first;
|
||||
};
|
||||
if (std::find_if(filaments.compatibility_counter.begin(), filaments.compatibility_counter.end(), filter) != filaments.compatibility_counter.end()) {
|
||||
continue;
|
||||
}
|
||||
std::vector<size_t> idx_with_same_alias;
|
||||
for (size_t i = 0; i < filaments.presets.size(); ++i) {
|
||||
if (preset->alias == filaments.presets[i]->alias)
|
||||
idx_with_same_alias.push_back(i);
|
||||
}
|
||||
size_t counter = 0;
|
||||
for (const auto& printer : filaments.printers) {
|
||||
if (!(*printer).is_visible || (*printer).printer_technology() != ptFFF)
|
||||
continue;
|
||||
bool compatible = false;
|
||||
// Test otrher materials with same alias
|
||||
for (size_t i = 0; i < idx_with_same_alias.size() && !compatible; ++i) {
|
||||
const Preset& prst = *(filaments.presets[idx_with_same_alias[i]]);
|
||||
const Preset& prntr = *printer;
|
||||
if (is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) {
|
||||
compatible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (compatible)
|
||||
counter++;
|
||||
}
|
||||
filaments.compatibility_counter.emplace_back(preset->alias, counter);
|
||||
}
|
||||
}
|
||||
|
||||
if (any_sla_selected && (technology & T_SLA)) {
|
||||
@ -1887,12 +2031,44 @@ void ConfigWizard::priv::update_materials(Technology technology)
|
||||
if (!material.alias.empty())
|
||||
aliases_sla[material.alias].insert(material.name);
|
||||
}
|
||||
sla_materials.add_printer_counter(&material);
|
||||
sla_materials.add_printer(&printer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// count compatible printers
|
||||
for (const auto& preset : sla_materials.presets) {
|
||||
|
||||
const auto filter = [preset](const std::pair<std::string, size_t> element) {
|
||||
return preset->alias == element.first;
|
||||
};
|
||||
if (std::find_if(sla_materials.compatibility_counter.begin(), sla_materials.compatibility_counter.end(), filter) != sla_materials.compatibility_counter.end()) {
|
||||
continue;
|
||||
}
|
||||
std::vector<size_t> idx_with_same_alias;
|
||||
for (size_t i = 0; i < sla_materials.presets.size(); ++i) {
|
||||
if(preset->alias == sla_materials.presets[i]->alias)
|
||||
idx_with_same_alias.push_back(i);
|
||||
}
|
||||
size_t counter = 0;
|
||||
for (const auto& printer : sla_materials.printers) {
|
||||
if (!(*printer).is_visible || (*printer).printer_technology() != ptSLA)
|
||||
continue;
|
||||
bool compatible = false;
|
||||
// Test otrher materials with same alias
|
||||
for (size_t i = 0; i < idx_with_same_alias.size() && !compatible; ++i) {
|
||||
const Preset& prst = *(sla_materials.presets[idx_with_same_alias[i]]);
|
||||
const Preset& prntr = *printer;
|
||||
if (is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) {
|
||||
compatible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (compatible)
|
||||
counter++;
|
||||
}
|
||||
sla_materials.compatibility_counter.emplace_back(preset->alias, counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <wx/listbox.h>
|
||||
#include <wx/checklst.h>
|
||||
#include <wx/radiobut.h>
|
||||
#include <wx/html/htmlwin.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
@ -86,9 +87,9 @@ struct Materials
|
||||
{
|
||||
Technology technology;
|
||||
// use vector for the presets to purpose of save of presets sorting in the bundle
|
||||
// bool is true if material is present in all printers (omnipresent)
|
||||
// size_t is counter of printers compatible with material
|
||||
std::vector<std::pair<const Preset*, size_t>> presets;
|
||||
std::vector<const Preset*> presets;
|
||||
// String is alias of material, size_t number of compatible counters
|
||||
std::vector<std::pair<std::string, size_t>> compatibility_counter;
|
||||
std::set<std::string> types;
|
||||
std::set<const Preset*> printers;
|
||||
|
||||
@ -100,7 +101,7 @@ struct Materials
|
||||
bool containts(const Preset *preset) const {
|
||||
//return std::find(presets.begin(), presets.end(), preset) != presets.end();
|
||||
return std::find_if(presets.begin(), presets.end(),
|
||||
[preset](const std::pair<const Preset*, bool>& element) { return element.first == preset; }) != presets.end();
|
||||
[preset](const Preset* element) { return element == preset; }) != presets.end();
|
||||
|
||||
}
|
||||
|
||||
@ -111,42 +112,35 @@ struct Materials
|
||||
const std::vector<const Preset*> get_presets_by_alias(const std::string name) {
|
||||
std::vector<const Preset*> ret_vec;
|
||||
for (auto it = presets.begin(); it != presets.end(); ++it) {
|
||||
if ((*it).first->alias == name)
|
||||
ret_vec.push_back((*it).first);
|
||||
if ((*it)->alias == name)
|
||||
ret_vec.push_back((*it));
|
||||
}
|
||||
return ret_vec;
|
||||
}
|
||||
|
||||
void add_printer_counter(const Preset* preset) {
|
||||
for (auto it = presets.begin(); it != presets.end(); ++it) {
|
||||
if ((*it).first->alias == preset->alias)
|
||||
(*it).second += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t get_printer_counter(const Preset* preset) {
|
||||
size_t highest = 0;
|
||||
for (auto it : presets) {
|
||||
if (it.first->alias == preset->alias && it.second > highest)
|
||||
highest = it.second;
|
||||
}
|
||||
return highest;
|
||||
for (auto it : compatibility_counter) {
|
||||
if (it.first == preset->alias)
|
||||
return it.second;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::string& appconfig_section() const;
|
||||
const std::string& get_type(const Preset *preset) const;
|
||||
const std::string& get_vendor(const Preset *preset) const;
|
||||
|
||||
|
||||
template<class F> void filter_presets(const Preset* printer, const std::string& type, const std::string& vendor, F cb) {
|
||||
for (auto preset : presets) {
|
||||
const Preset& prst = *(preset.first);
|
||||
const Preset& prst = *(preset);
|
||||
const Preset& prntr = *printer;
|
||||
if ((printer == nullptr || is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) &&
|
||||
(type.empty() || get_type(preset.first) == type) &&
|
||||
(vendor.empty() || get_vendor(preset.first) == vendor)) {
|
||||
(type.empty() || get_type(preset) == type) &&
|
||||
(vendor.empty() || get_vendor(preset) == vendor)) {
|
||||
|
||||
cb(preset.first);
|
||||
cb(preset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -325,11 +319,12 @@ struct PageMaterials: ConfigWizardPage
|
||||
Materials *materials;
|
||||
StringList *list_printer, *list_type, *list_vendor;
|
||||
PresetList *list_profile;
|
||||
int sel_printer_prev, sel_type_prev, sel_vendor_prev;
|
||||
int sel_printer_count_prev, sel_printer_item_prev, sel_type_prev, sel_vendor_prev;
|
||||
bool presets_loaded;
|
||||
|
||||
wxFlexGridSizer *grid;
|
||||
wxStaticText *compatible_printers;
|
||||
wxHtmlWindow* html_window;
|
||||
|
||||
int compatible_printers_width = { 100 };
|
||||
std::string empty_printers_label;
|
||||
bool first_paint = { false };
|
||||
@ -345,9 +340,12 @@ struct PageMaterials: ConfigWizardPage
|
||||
void select_material(int i);
|
||||
void select_all(bool select);
|
||||
void clear();
|
||||
void prepare_compatible_printers_label();
|
||||
void set_compatible_printers_html_window(const std::vector<std::string>& printer_names, bool all_printers = false);
|
||||
void clear_compatible_printers_label();
|
||||
|
||||
void sort_list_data(StringList* list, bool add_All_item, bool material_type_ordering);
|
||||
void sort_list_data(PresetList* list);
|
||||
|
||||
void on_paint();
|
||||
void on_mouse_move_on_profiles(wxMouseEvent& evt);
|
||||
void on_mouse_enter_profiles(wxMouseEvent& evt);
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include <GL/glew.h>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/numformatter.h>
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
@ -861,6 +863,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
if (m_moves_count == 0)
|
||||
return;
|
||||
|
||||
unsigned int progress_count = 0;
|
||||
static const unsigned int progress_threshold = 1000;
|
||||
wxProgressDialog progress_dialog(_L("Generating toolpaths"), "...",
|
||||
100, wxGetApp().plater(), wxPD_AUTO_HIDE | wxPD_APP_MODAL);
|
||||
|
||||
@ -1243,10 +1247,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
if (i == 0)
|
||||
continue;
|
||||
|
||||
progress_dialog.Update(int(100.0f * float(i) / (2.0f * float(m_moves_count))),
|
||||
_L("Generating vertex buffer") + " (" + wxNumberFormatter::ToString((long)i, wxNumberFormatter::Style_None) + "/" +
|
||||
wxNumberFormatter::ToString((long)m_moves_count, wxNumberFormatter::Style_None) + ")");
|
||||
progress_dialog.Fit();
|
||||
++progress_count;
|
||||
if (progress_count % progress_threshold == 0) {
|
||||
progress_dialog.Update(int(100.0f * float(i) / (2.0f * float(m_moves_count))),
|
||||
_L("Generating vertex buffer") + ": " + wxNumberFormatter::ToString(100.0 * double(i) / double(m_moves_count), 0, wxNumberFormatter::Style_None) + "%");
|
||||
progress_dialog.Fit();
|
||||
progress_count = 0;
|
||||
}
|
||||
|
||||
const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1];
|
||||
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
|
||||
@ -1314,10 +1321,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
if (i == 0)
|
||||
continue;
|
||||
|
||||
progress_dialog.Update(int(100.0f * float(m_moves_count + i) / (2.0f * float(m_moves_count))),
|
||||
_L("Generating index buffers") + " (" + wxNumberFormatter::ToString((long)i, wxNumberFormatter::Style_None) + "/" +
|
||||
wxNumberFormatter::ToString((long)m_moves_count, wxNumberFormatter::Style_None) + ")");
|
||||
progress_dialog.Fit();
|
||||
++progress_count;
|
||||
if (progress_count % progress_threshold == 0) {
|
||||
progress_dialog.Update(int(100.0f * float(m_moves_count + i) / (2.0f * float(m_moves_count))),
|
||||
_L("Generating index buffers") + ": " + wxNumberFormatter::ToString(100.0 * double(i) / double(m_moves_count), 0, wxNumberFormatter::Style_None) + "%");
|
||||
progress_dialog.Fit();
|
||||
progress_count = 0;
|
||||
}
|
||||
|
||||
const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1];
|
||||
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
|
||||
|
@ -628,8 +628,8 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
|
||||
bool error = false;
|
||||
switch (warning) {
|
||||
case ObjectOutside: text = L("An object outside the print area was detected."); break;
|
||||
case ToolpathOutside: text = L("A toolpath outside the print area was detected."); break;
|
||||
case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); break;
|
||||
case ToolpathOutside: text = L("A toolpath outside the print area was detected."); error = true; break;
|
||||
case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); error = true; break;
|
||||
case SomethingNotShown: text = L("Some objects are not visible."); break;
|
||||
case ObjectClashed:
|
||||
text = L( "An object outside the print area was detected.\n"
|
||||
@ -644,7 +644,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
|
||||
wxGetApp().plater()->get_notification_manager()->push_plater_warning_notification(text, *(wxGetApp().plater()->get_current_canvas3D()));
|
||||
} else {
|
||||
if (error)
|
||||
wxGetApp().plater()->get_notification_manager()->close_plater_error_notification();
|
||||
wxGetApp().plater()->get_notification_manager()->close_plater_error_notification(text);
|
||||
else
|
||||
wxGetApp().plater()->get_notification_manager()->close_plater_warning_notification(text);
|
||||
}
|
||||
|
@ -65,8 +65,8 @@ static wxBitmapComboBox* create_word_local_combo(wxWindow *parent)
|
||||
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
|
||||
temp->Append(_(L("World coordinates")));
|
||||
temp->Append(_(L("Local coordinates")));
|
||||
temp->Append(_L("World coordinates"));
|
||||
temp->Append(_L("Local coordinates"));
|
||||
temp->SetSelection(0);
|
||||
temp->SetValue(temp->GetString(0));
|
||||
|
||||
@ -85,7 +85,7 @@ static wxBitmapComboBox* create_word_local_combo(wxWindow *parent)
|
||||
temp->SetItemBitmap(0, empty_bmp);
|
||||
#endif
|
||||
|
||||
temp->SetToolTip(_(L("Select coordinate space, in which the transformation will be performed.")));
|
||||
temp->SetToolTip(_L("Select coordinate space, in which the transformation will be performed."));
|
||||
return temp;
|
||||
}
|
||||
|
||||
@ -108,8 +108,8 @@ void msw_rescale_word_local_combo(wxBitmapComboBox* combo)
|
||||
// Set rescaled size
|
||||
combo->SetSize(size);
|
||||
|
||||
combo->Append(_(L("World coordinates")));
|
||||
combo->Append(_(L("Local coordinates")));
|
||||
combo->Append(_L("World coordinates"));
|
||||
combo->Append(_L("Local coordinates"));
|
||||
|
||||
wxBitmap empty_bmp(1, combo->GetFont().GetPixelSize().y + 2);
|
||||
empty_bmp.SetWidth(0);
|
||||
@ -158,9 +158,9 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
|
||||
sizer->Add(m_fix_throught_netfab_bitmap);
|
||||
|
||||
auto name_label = new wxStaticText(m_parent, wxID_ANY, _(L("Name"))+":");
|
||||
auto name_label = new wxStaticText(m_parent, wxID_ANY, _L("Name")+":");
|
||||
set_font_and_background_style(name_label, wxGetApp().normal_font());
|
||||
name_label->SetToolTip(_(L("Object name")));
|
||||
name_label->SetToolTip(_L("Object name"));
|
||||
sizer->Add(name_label);
|
||||
|
||||
m_main_grid_sizer->Add(sizer);
|
||||
@ -268,7 +268,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
|
||||
// We will add a button to toggle mirroring to each axis:
|
||||
auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW);
|
||||
btn->SetToolTip(wxString::Format(_(L("Toggle %c axis mirroring")), (int)label));
|
||||
btn->SetToolTip(wxString::Format(_L("Toggle %c axis mirroring"), (int)label));
|
||||
btn->SetBitmapDisabled_(m_mirror_bitmap_hidden);
|
||||
|
||||
m_mirror_buttons[axis_idx].first = btn;
|
||||
@ -342,7 +342,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
|
||||
// Add drop to bed button
|
||||
m_drop_to_bed_button = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "drop_to_bed"));
|
||||
m_drop_to_bed_button->SetToolTip(_(L("Drop to bed")));
|
||||
m_drop_to_bed_button->SetToolTip(_L("Drop to bed"));
|
||||
m_drop_to_bed_button->Bind(wxEVT_BUTTON, [=](wxCommandEvent& e) {
|
||||
// ???
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
@ -354,7 +354,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
const Geometry::Transformation& instance_trafo = volume->get_instance_transformation();
|
||||
Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(volume));
|
||||
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Drop to bed")));
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed"));
|
||||
change_position_value(0, diff.x());
|
||||
change_position_value(1, diff.y());
|
||||
change_position_value(2, diff.z());
|
||||
@ -369,7 +369,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
|
||||
// Add reset rotation button
|
||||
m_reset_rotation_button = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
|
||||
m_reset_rotation_button->SetToolTip(_(L("Reset rotation")));
|
||||
m_reset_rotation_button->SetToolTip(_L("Reset rotation"));
|
||||
m_reset_rotation_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) {
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
Selection& selection = canvas->get_selection();
|
||||
@ -404,9 +404,9 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
|
||||
// Add reset scale button
|
||||
m_reset_scale_button = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
|
||||
m_reset_scale_button->SetToolTip(_(L("Reset scale")));
|
||||
m_reset_scale_button->SetToolTip(_L("Reset scale"));
|
||||
m_reset_scale_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) {
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Reset scale")));
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset scale"));
|
||||
change_scale_value(0, 100.);
|
||||
change_scale_value(1, 100.);
|
||||
change_scale_value(2, 100.);
|
||||
@ -509,8 +509,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
|
||||
m_world_coordinates = true;
|
||||
|
||||
ObjectList* obj_list = wxGetApp().obj_list();
|
||||
if (selection.is_single_full_instance())
|
||||
{
|
||||
if (selection.is_single_full_instance()) {
|
||||
// all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
m_new_position = volume->get_instance_offset();
|
||||
@ -528,7 +527,8 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
|
||||
m_new_rotation = Vec3d::Zero();
|
||||
m_new_size = selection.get_scaled_instance_bounding_box().size();
|
||||
m_new_scale = m_new_size.cwiseProduct(selection.get_unscaled_instance_bounding_box().size().cwiseInverse()) * 100.;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
m_new_rotation = volume->get_instance_rotation() * (180. / M_PI);
|
||||
m_new_size = volume->get_instance_transformation().get_scaling_factor().cwiseProduct(wxGetApp().model().objects[volume->object_idx()]->raw_mesh_bounding_box().size());
|
||||
m_new_scale = volume->get_instance_scaling_factor() * 100.;
|
||||
@ -536,8 +536,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
|
||||
|
||||
m_new_enabled = true;
|
||||
}
|
||||
else if (selection.is_single_full_object() && obj_list->is_selected(itObject))
|
||||
{
|
||||
else if (selection.is_single_full_object() && obj_list->is_selected(itObject)) {
|
||||
const BoundingBoxf3& box = selection.get_bounding_box();
|
||||
m_new_position = box.center();
|
||||
m_new_rotation = Vec3d::Zero();
|
||||
@ -547,18 +546,16 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
|
||||
m_new_scale_label_string = L("Scale");
|
||||
m_new_enabled = true;
|
||||
}
|
||||
else if (selection.is_single_modifier() || selection.is_single_volume())
|
||||
{
|
||||
else if (selection.is_single_modifier() || selection.is_single_volume()) {
|
||||
// the selection contains a single volume
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
m_new_position = volume->get_volume_offset();
|
||||
m_new_rotation = volume->get_volume_rotation() * (180. / M_PI);
|
||||
m_new_scale = volume->get_volume_scaling_factor() * 100.;
|
||||
m_new_size = volume->get_volume_transformation().get_scaling_factor().cwiseProduct(volume->bounding_box().size());
|
||||
m_new_size = volume->get_instance_transformation().get_scaling_factor().cwiseProduct(volume->get_volume_transformation().get_scaling_factor().cwiseProduct(volume->bounding_box().size()));
|
||||
m_new_enabled = true;
|
||||
}
|
||||
else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot))
|
||||
{
|
||||
else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot)) {
|
||||
reset_settings_value();
|
||||
m_new_move_label_string = L("Translate");
|
||||
m_new_rotate_label_string = L("Rotate");
|
||||
@ -624,7 +621,7 @@ void ObjectManipulation::update_if_dirty()
|
||||
|
||||
if (selection.requires_uniform_scale()) {
|
||||
m_lock_bnt->SetLock(true);
|
||||
m_lock_bnt->SetToolTip(_(L("You cannot use non-uniform scaling mode for multiple objects/parts selection")));
|
||||
m_lock_bnt->SetToolTip(_L("You cannot use non-uniform scaling mode for multiple objects/parts selection"));
|
||||
m_lock_bnt->disable();
|
||||
}
|
||||
else {
|
||||
@ -924,11 +921,11 @@ void ObjectManipulation::set_uniform_scaling(const bool new_value)
|
||||
if (! Geometry::is_rotation_ninety_degrees(volume->get_instance_rotation())) {
|
||||
// Cannot apply scaling in the world coordinate system.
|
||||
wxMessageDialog dlg(GUI::wxGetApp().mainframe,
|
||||
_(L("The currently manipulated object is tilted (rotation angles are not multiples of 90°).\n"
|
||||
_L("The currently manipulated object is tilted (rotation angles are not multiples of 90°).\n"
|
||||
"Non-uniform scaling of tilted objects is only possible in the World coordinate system,\n"
|
||||
"once the rotation is embedded into the object coordinates.")) + "\n" +
|
||||
_(L("This operation is irreversible.\n"
|
||||
"Do you want to proceed?")),
|
||||
"once the rotation is embedded into the object coordinates.") + "\n" +
|
||||
_L("This operation is irreversible.\n"
|
||||
"Do you want to proceed?"),
|
||||
SLIC3R_APP_NAME,
|
||||
wxYES_NO | wxCANCEL | wxCANCEL_DEFAULT | wxICON_QUESTION);
|
||||
if (dlg.ShowModal() != wxID_YES) {
|
||||
|
@ -116,7 +116,7 @@ public:
|
||||
this->Bind(EVT_DPI_CHANGED_SLICER, [this](const DpiChangedEvent& evt) {
|
||||
m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT;
|
||||
|
||||
m_new_font_point_size = get_default_font_for_dpi(evt.dpi).GetPointSize();
|
||||
m_new_font_point_size = get_default_font_for_dpi(this, evt.dpi).GetPointSize();
|
||||
|
||||
if (!m_can_rescale)
|
||||
return;
|
||||
|
@ -42,6 +42,7 @@ bool GLGizmoFdmSupports::on_init()
|
||||
m_desc["clipping_of_view"] = _L("Clipping of view") + ": ";
|
||||
m_desc["reset_direction"] = _L("Reset direction");
|
||||
m_desc["cursor_size"] = _L("Cursor size") + ": ";
|
||||
m_desc["cursor_type"] = _L("Cursor type") + ": ";
|
||||
m_desc["enforce_caption"] = _L("Left mouse button") + ": ";
|
||||
m_desc["enforce"] = _L("Enforce supports");
|
||||
m_desc["block_caption"] = _L("Right mouse button") + " ";
|
||||
@ -66,7 +67,7 @@ void GLGizmoFdmSupports::on_render() const
|
||||
render_triangles(selection);
|
||||
|
||||
m_c->object_clipper()->render_cut();
|
||||
render_cursor_circle();
|
||||
render_cursor();
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
@ -78,16 +79,26 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
||||
if (! m_c->selection_info()->model_object())
|
||||
return;
|
||||
|
||||
const float approx_height = m_imgui->scaled(18.0f);
|
||||
const float approx_height = m_imgui->scaled(14.0f);
|
||||
y = std::min(y, bottom_limit - approx_height);
|
||||
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
|
||||
|
||||
if (! m_setting_angle) {
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
std::vector<std::string> cursor_types;
|
||||
cursor_types.push_back(_L("Circle").ToUTF8().data());
|
||||
cursor_types.push_back(_L("Sphere").ToUTF8().data());
|
||||
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("reset_direction")).x)
|
||||
+ m_imgui->scaled(1.5f);
|
||||
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
|
||||
const float cursor_type_combo_left = m_imgui->calc_text_size(m_desc.at("cursor_type")).x + m_imgui->scaled(1.f);
|
||||
const float cursor_type_combo_width = std::max(m_imgui->calc_text_size(wxString::FromUTF8(cursor_types[0])).x,
|
||||
m_imgui->calc_text_size(wxString::FromUTF8(cursor_types[1])).x)
|
||||
+ m_imgui->scaled(2.5f);
|
||||
const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
|
||||
const float minimal_slider_width = m_imgui->scaled(4.f);
|
||||
|
||||
@ -103,6 +114,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
||||
float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left);
|
||||
window_width = std::max(window_width, total_text_max);
|
||||
window_width = std::max(window_width, button_width);
|
||||
window_width = std::max(window_width, cursor_type_combo_left + cursor_type_combo_width);
|
||||
|
||||
auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) {
|
||||
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, caption);
|
||||
@ -139,8 +151,8 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
||||
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||
|
||||
m_imgui->text(m_desc.at("cursor_size"));
|
||||
ImGui::SameLine(clipping_slider_left);
|
||||
ImGui::PushItemWidth(window_width - clipping_slider_left);
|
||||
ImGui::SameLine(cursor_slider_left);
|
||||
ImGui::PushItemWidth(window_width - cursor_slider_left);
|
||||
ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
@ -150,6 +162,23 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
|
||||
m_imgui->text(m_desc.at("cursor_type"));
|
||||
ImGui::SameLine(window_width - cursor_type_combo_width - m_imgui->scaled(0.5f));
|
||||
ImGui::PushItemWidth(cursor_type_combo_width);
|
||||
int selection = int(m_cursor_type);
|
||||
m_imgui->combo("", cursor_types, selection);
|
||||
m_cursor_type = TriangleSelector::CursorType(selection);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(max_tooltip_width);
|
||||
ImGui::TextUnformatted(_L("Sphere paints all facets inside, regardless of their orientation.\n\n"
|
||||
"Circle ignores facets facing away from the camera.").ToUTF8().data());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_c->object_clipper()->get_position() == 0.f)
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
|
@ -21,6 +21,14 @@ GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& ic
|
||||
: GLGizmoBase(parent, icon_filename, sprite_id)
|
||||
{
|
||||
m_clipping_plane.reset(new ClippingPlane());
|
||||
// Make sphere and save it into a vertex buffer.
|
||||
const TriangleMesh sphere_mesh = make_sphere(1., (2*M_PI)/24.);
|
||||
for (size_t i=0; i<sphere_mesh.its.vertices.size(); ++i)
|
||||
m_vbo_sphere.push_geometry(sphere_mesh.its.vertices[i].cast<double>(),
|
||||
sphere_mesh.stl.facet_start[i].normal.cast<double>());
|
||||
for (const stl_triangle_vertex_indices& indices : sphere_mesh.its.indices)
|
||||
m_vbo_sphere.push_triangle(indices(0), indices(1), indices(2));
|
||||
m_vbo_sphere.finalize_geometry(true);
|
||||
}
|
||||
|
||||
|
||||
@ -117,6 +125,16 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const
|
||||
}
|
||||
|
||||
|
||||
void GLGizmoPainterBase::render_cursor() const
|
||||
{
|
||||
if (m_cursor_type == TriangleSelector::SPHERE)
|
||||
render_cursor_sphere();
|
||||
else
|
||||
render_cursor_circle();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoPainterBase::render_cursor_circle() const
|
||||
{
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
@ -162,17 +180,61 @@ void GLGizmoPainterBase::render_cursor_circle() const
|
||||
}
|
||||
|
||||
|
||||
void GLGizmoPainterBase::render_cursor_sphere() const
|
||||
{
|
||||
Vec2d mouse_position(m_parent.get_local_mouse_position()(0), m_parent.get_local_mouse_position()(1));
|
||||
|
||||
bool GLGizmoPainterBase::is_mesh_point_clipped(const Vec3d& point) const
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const ModelInstance* mi = mo->instances[selection.get_instance_idx()];
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
|
||||
// Precalculate transformations of individual meshes.
|
||||
std::vector<Transform3d> trafo_matrices;
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
if (mv->is_model_part())
|
||||
trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix());
|
||||
}
|
||||
update_raycast_cache(mouse_position, camera, trafo_matrices);
|
||||
if (m_rr.mesh_id == -1)
|
||||
return;
|
||||
|
||||
const Transform3d& complete_matrix = trafo_matrices[m_rr.mesh_id];
|
||||
const Transform3d complete_scaling_matrix_inverse = Geometry::Transformation(complete_matrix).get_matrix(true, true, false, true).inverse();
|
||||
const bool is_left_handed = Geometry::Transformation(complete_matrix).is_left_handed();
|
||||
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glMultMatrixd(complete_matrix.data()));
|
||||
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
|
||||
glsafe(::glTranslatef(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2)));
|
||||
glsafe(::glMultMatrixd(complete_scaling_matrix_inverse.data()));
|
||||
glsafe(::glScaled(m_cursor_radius, m_cursor_radius, m_cursor_radius));
|
||||
|
||||
if (is_left_handed)
|
||||
glFrontFace(GL_CW);
|
||||
|
||||
float render_color[4] = { 0.f, 0.f, 0.f, 0.25f };
|
||||
if (m_button_down == Button::Left)
|
||||
render_color[2] = 1.f;
|
||||
else if (m_button_down == Button::Right)
|
||||
render_color[0] = 1.f;
|
||||
glsafe(::glColor4fv(render_color));
|
||||
|
||||
m_vbo_sphere.render();
|
||||
|
||||
if (is_left_handed)
|
||||
glFrontFace(GL_CCW);
|
||||
|
||||
glsafe(::glPopMatrix());
|
||||
}
|
||||
|
||||
|
||||
bool GLGizmoPainterBase::is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const
|
||||
{
|
||||
if (m_c->object_clipper()->get_position() == 0.)
|
||||
return false;
|
||||
|
||||
auto sel_info = m_c->selection_info();
|
||||
int active_inst = m_c->selection_info()->get_active_instance();
|
||||
const ModelInstance* mi = sel_info->model_object()->instances[active_inst];
|
||||
const Transform3d& trafo = mi->get_transformation().get_matrix();
|
||||
|
||||
Vec3d transformed_point = trafo * point;
|
||||
transformed_point(2) += sel_info->get_sla_shift();
|
||||
return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point);
|
||||
@ -241,104 +303,52 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
// add several positions from between into the list, so there
|
||||
// are no gaps in the painted region.
|
||||
{
|
||||
if (m_last_mouse_position == Vec2d::Zero())
|
||||
m_last_mouse_position = mouse_position;
|
||||
if (m_last_mouse_click == Vec2d::Zero())
|
||||
m_last_mouse_click = mouse_position;
|
||||
// resolution describes minimal distance limit using circle radius
|
||||
// as a unit (e.g., 2 would mean the patches will be touching).
|
||||
double resolution = 0.7;
|
||||
double diameter_px = resolution * m_cursor_radius * camera.get_zoom();
|
||||
int patches_in_between = int(((mouse_position - m_last_mouse_position).norm() - diameter_px) / diameter_px);
|
||||
int patches_in_between = int(((mouse_position - m_last_mouse_click).norm() - diameter_px) / diameter_px);
|
||||
if (patches_in_between > 0) {
|
||||
Vec2d diff = (mouse_position - m_last_mouse_position)/(patches_in_between+1);
|
||||
Vec2d diff = (mouse_position - m_last_mouse_click)/(patches_in_between+1);
|
||||
for (int i=1; i<=patches_in_between; ++i)
|
||||
mouse_positions.emplace_back(m_last_mouse_position + i*diff);
|
||||
mouse_positions.emplace_back(m_last_mouse_click + i*diff);
|
||||
}
|
||||
}
|
||||
m_last_mouse_position = Vec2d::Zero(); // only actual hits should be saved
|
||||
m_last_mouse_click = Vec2d::Zero(); // only actual hits should be saved
|
||||
|
||||
// Precalculate transformations of individual meshes.
|
||||
std::vector<Transform3d> trafo_matrices;
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
if (mv->is_model_part())
|
||||
trafo_matrices.emplace_back(instance_trafo * mv->get_matrix());
|
||||
}
|
||||
|
||||
// Now "click" into all the prepared points and spill paint around them.
|
||||
for (const Vec2d& mp : mouse_positions) {
|
||||
std::vector<std::vector<std::pair<Vec3f, size_t>>> hit_positions_and_facet_ids;
|
||||
bool clipped_mesh_was_hit = false;
|
||||
|
||||
Vec3f normal = Vec3f::Zero();
|
||||
Vec3f hit = Vec3f::Zero();
|
||||
size_t facet = 0;
|
||||
Vec3f closest_hit = Vec3f::Zero();
|
||||
double closest_hit_squared_distance = std::numeric_limits<double>::max();
|
||||
size_t closest_facet = 0;
|
||||
int closest_hit_mesh_id = -1;
|
||||
|
||||
// Transformations of individual meshes
|
||||
std::vector<Transform3d> trafo_matrices;
|
||||
|
||||
int mesh_id = -1;
|
||||
// Cast a ray on all meshes, pick the closest hit and save it for the respective mesh
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
if (! mv->is_model_part())
|
||||
continue;
|
||||
|
||||
++mesh_id;
|
||||
|
||||
trafo_matrices.push_back(instance_trafo * mv->get_matrix());
|
||||
hit_positions_and_facet_ids.push_back(std::vector<std::pair<Vec3f, size_t>>());
|
||||
|
||||
if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(
|
||||
mp,
|
||||
trafo_matrices[mesh_id],
|
||||
camera,
|
||||
hit,
|
||||
normal,
|
||||
m_clipping_plane.get(),
|
||||
&facet))
|
||||
{
|
||||
// In case this hit is clipped, skip it.
|
||||
if (is_mesh_point_clipped(hit.cast<double>())) {
|
||||
clipped_mesh_was_hit = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is this hit the closest to the camera so far?
|
||||
double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast<double>()).squaredNorm();
|
||||
if (hit_squared_distance < closest_hit_squared_distance) {
|
||||
closest_hit_squared_distance = hit_squared_distance;
|
||||
closest_facet = facet;
|
||||
closest_hit_mesh_id = mesh_id;
|
||||
closest_hit = hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
update_raycast_cache(mp, camera, trafo_matrices);
|
||||
|
||||
bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None);
|
||||
|
||||
// The mouse button click detection is enabled when there is a valid hit
|
||||
// or when the user clicks the clipping plane. Missing the object entirely
|
||||
// shall not capture the mouse.
|
||||
if (closest_hit_mesh_id != -1 || clipped_mesh_was_hit) {
|
||||
if (m_rr.mesh_id != -1 || m_rr.clipped_mesh_was_hit) {
|
||||
if (m_button_down == Button::None)
|
||||
m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right);
|
||||
}
|
||||
|
||||
if (closest_hit_mesh_id == -1) {
|
||||
if (m_rr.mesh_id == -1) {
|
||||
// In case we have no valid hit, we can return. The event will
|
||||
// be stopped in following two cases:
|
||||
// 1. clicking the clipping plane
|
||||
// 2. dragging while painting (to prevent scene rotations and moving the object)
|
||||
return clipped_mesh_was_hit
|
||||
return m_rr.clipped_mesh_was_hit
|
||||
|| dragging_while_painting;
|
||||
}
|
||||
|
||||
// Find respective mesh id.
|
||||
mesh_id = -1;
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
if (! mv->is_model_part())
|
||||
continue;
|
||||
++mesh_id;
|
||||
if (mesh_id == closest_hit_mesh_id)
|
||||
break;
|
||||
}
|
||||
|
||||
const Transform3d& trafo_matrix = trafo_matrices[mesh_id];
|
||||
const Transform3d& trafo_matrix = trafo_matrices[m_rr.mesh_id];
|
||||
|
||||
// Calculate how far can a point be from the line (in mesh coords).
|
||||
// FIXME: The scaling of the mesh can be non-uniform.
|
||||
@ -348,12 +358,12 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
|
||||
// Calculate direction from camera to the hit (in mesh coords):
|
||||
Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>();
|
||||
Vec3f dir = (closest_hit - camera_pos).normalized();
|
||||
Vec3f dir = (m_rr.hit - camera_pos).normalized();
|
||||
|
||||
assert(mesh_id < int(m_triangle_selectors.size()));
|
||||
m_triangle_selectors[mesh_id]->select_patch(closest_hit, closest_facet, camera_pos,
|
||||
dir, limit, new_state);
|
||||
m_last_mouse_position = mouse_position;
|
||||
assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
|
||||
m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, m_rr.facet, camera_pos,
|
||||
dir, limit, m_cursor_type, new_state);
|
||||
m_last_mouse_click = mouse_position;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -389,7 +399,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
update_model_object();
|
||||
|
||||
m_button_down = Button::None;
|
||||
m_last_mouse_position = Vec2d::Zero();
|
||||
m_last_mouse_click = Vec2d::Zero();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -398,6 +408,56 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
|
||||
|
||||
|
||||
void GLGizmoPainterBase::update_raycast_cache(const Vec2d& mouse_position,
|
||||
const Camera& camera,
|
||||
const std::vector<Transform3d>& trafo_matrices) const
|
||||
{
|
||||
if (m_rr.mouse_position == mouse_position) {
|
||||
// Same query as last time - the answer is already in the cache.
|
||||
return;
|
||||
}
|
||||
|
||||
bool clipped_mesh_was_hit{false};
|
||||
Vec3f normal = Vec3f::Zero();
|
||||
Vec3f hit = Vec3f::Zero();
|
||||
size_t facet = 0;
|
||||
Vec3f closest_hit = Vec3f::Zero();
|
||||
double closest_hit_squared_distance = std::numeric_limits<double>::max();
|
||||
size_t closest_facet = 0;
|
||||
int closest_hit_mesh_id = -1;
|
||||
|
||||
// Cast a ray on all meshes, pick the closest hit and save it for the respective mesh
|
||||
for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) {
|
||||
|
||||
if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(
|
||||
mouse_position,
|
||||
trafo_matrices[mesh_id],
|
||||
camera,
|
||||
hit,
|
||||
normal,
|
||||
m_clipping_plane.get(),
|
||||
&facet))
|
||||
{
|
||||
// In case this hit is clipped, skip it.
|
||||
if (is_mesh_point_clipped(hit.cast<double>(), trafo_matrices[mesh_id])) {
|
||||
clipped_mesh_was_hit = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is this hit the closest to the camera so far?
|
||||
double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast<double>()).squaredNorm();
|
||||
if (hit_squared_distance < closest_hit_squared_distance) {
|
||||
closest_hit_squared_distance = hit_squared_distance;
|
||||
closest_facet = facet;
|
||||
closest_hit_mesh_id = mesh_id;
|
||||
closest_hit = hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_rr = {mouse_position, closest_hit_mesh_id, closest_hit, closest_facet, clipped_mesh_was_hit};
|
||||
}
|
||||
|
||||
bool GLGizmoPainterBase::on_is_activable() const
|
||||
{
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
@ -504,13 +564,13 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui)
|
||||
m_iva_blockers.finalize_geometry(true);
|
||||
|
||||
if (m_iva_enforcers.has_VBOs()) {
|
||||
::glColor4f(0.f, 0.f, 1.f, 0.3f);
|
||||
::glColor4f(0.f, 0.f, 1.f, 0.4f);
|
||||
m_iva_enforcers.render();
|
||||
}
|
||||
|
||||
|
||||
if (m_iva_blockers.has_VBOs()) {
|
||||
::glColor4f(1.f, 0.f, 0.f, 0.3f);
|
||||
::glColor4f(1.f, 0.f, 0.f, 0.4f);
|
||||
m_iva_blockers.render();
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ namespace GUI {
|
||||
|
||||
enum class SLAGizmoEventType : unsigned char;
|
||||
class ClippingPlane;
|
||||
struct Camera;
|
||||
|
||||
enum class PainterGizmoType {
|
||||
FDM_SUPPORTS,
|
||||
@ -67,10 +68,13 @@ public:
|
||||
|
||||
protected:
|
||||
void render_triangles(const Selection& selection) const;
|
||||
void render_cursor() const;
|
||||
void render_cursor_circle() const;
|
||||
void render_cursor_sphere() const;
|
||||
virtual void update_model_object() const = 0;
|
||||
virtual void update_from_model_object() = 0;
|
||||
void activate_internal_undo_redo_stack(bool activate);
|
||||
void set_cursor_type(TriangleSelector::CursorType);
|
||||
|
||||
float m_cursor_radius = 2.f;
|
||||
static constexpr float CursorRadiusMin = 0.4f; // cannot be zero
|
||||
@ -80,16 +84,22 @@ protected:
|
||||
// For each model-part volume, store status and division of the triangles.
|
||||
std::vector<std::unique_ptr<TriangleSelectorGUI>> m_triangle_selectors;
|
||||
|
||||
TriangleSelector::CursorType m_cursor_type = TriangleSelector::SPHERE;
|
||||
|
||||
|
||||
private:
|
||||
bool is_mesh_point_clipped(const Vec3d& point) const;
|
||||
bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const;
|
||||
void update_raycast_cache(const Vec2d& mouse_position,
|
||||
const Camera& camera,
|
||||
const std::vector<Transform3d>& trafo_matrices) const;
|
||||
|
||||
float m_clipping_plane_distance = 0.f;
|
||||
std::unique_ptr<ClippingPlane> m_clipping_plane;
|
||||
GLIndexedVertexArray m_vbo_sphere;
|
||||
|
||||
bool m_internal_stack_active = false;
|
||||
bool m_schedule_update = false;
|
||||
Vec2d m_last_mouse_position = Vec2d::Zero();
|
||||
Vec2d m_last_mouse_click = Vec2d::Zero();
|
||||
|
||||
enum class Button {
|
||||
None,
|
||||
@ -100,6 +110,18 @@ private:
|
||||
Button m_button_down = Button::None;
|
||||
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
|
||||
|
||||
// Following cache holds result of a raycast query. The queries are asked
|
||||
// during rendering the sphere cursor and painting, this saves repeated
|
||||
// raycasts when the mouse position is the same as before.
|
||||
struct RaycastResult {
|
||||
Vec2d mouse_position;
|
||||
int mesh_id;
|
||||
Vec3f hit;
|
||||
size_t facet;
|
||||
bool clipped_mesh_was_hit;
|
||||
};
|
||||
mutable RaycastResult m_rr;
|
||||
|
||||
protected:
|
||||
void on_set_state() override;
|
||||
void on_start_dragging() override {}
|
||||
|
@ -25,6 +25,7 @@ bool GLGizmoSeam::on_init()
|
||||
m_desc["clipping_of_view"] = _L("Clipping of view") + ": ";
|
||||
m_desc["reset_direction"] = _L("Reset direction");
|
||||
m_desc["cursor_size"] = _L("Cursor size") + ": ";
|
||||
m_desc["cursor_type"] = _L("Cursor type") + ": ";
|
||||
m_desc["enforce_caption"] = _L("Left mouse button") + ": ";
|
||||
m_desc["enforce"] = _L("Enforce seam");
|
||||
m_desc["block_caption"] = _L("Right mouse button") + " ";
|
||||
@ -55,7 +56,7 @@ void GLGizmoSeam::on_render() const
|
||||
render_triangles(selection);
|
||||
|
||||
m_c->object_clipper()->render_cut();
|
||||
render_cursor_circle();
|
||||
render_cursor();
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
@ -67,16 +68,26 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
||||
if (! m_c->selection_info()->model_object())
|
||||
return;
|
||||
|
||||
const float approx_height = m_imgui->scaled(18.0f);
|
||||
const float approx_height = m_imgui->scaled(14.0f);
|
||||
y = std::min(y, bottom_limit - approx_height);
|
||||
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
|
||||
|
||||
std::vector<std::string> cursor_types;
|
||||
cursor_types.push_back(_L("Circle").ToUTF8().data());
|
||||
cursor_types.push_back(_L("Sphere").ToUTF8().data());
|
||||
|
||||
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
|
||||
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("reset_direction")).x)
|
||||
+ m_imgui->scaled(1.5f);
|
||||
const float cursor_size_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
|
||||
const float cursor_type_combo_left = m_imgui->calc_text_size(m_desc.at("cursor_type")).x + m_imgui->scaled(1.f);
|
||||
const float cursor_type_combo_width = std::max(m_imgui->calc_text_size(wxString::FromUTF8(cursor_types[0])).x,
|
||||
m_imgui->calc_text_size(wxString::FromUTF8(cursor_types[1])).x)
|
||||
+ m_imgui->scaled(2.5f);
|
||||
const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
|
||||
const float minimal_slider_width = m_imgui->scaled(4.f);
|
||||
|
||||
@ -89,9 +100,10 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
||||
caption_max += m_imgui->scaled(1.f);
|
||||
total_text_max += m_imgui->scaled(1.f);
|
||||
|
||||
float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left);
|
||||
float window_width = minimal_slider_width + std::max(cursor_size_slider_left, clipping_slider_left);
|
||||
window_width = std::max(window_width, total_text_max);
|
||||
window_width = std::max(window_width, button_width);
|
||||
window_width = std::max(window_width, cursor_type_combo_left + cursor_type_combo_width);
|
||||
|
||||
auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) {
|
||||
static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
|
||||
@ -123,8 +135,8 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
||||
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||
|
||||
m_imgui->text(m_desc.at("cursor_size"));
|
||||
ImGui::SameLine(clipping_slider_left);
|
||||
ImGui::PushItemWidth(window_width - clipping_slider_left);
|
||||
ImGui::SameLine(cursor_size_slider_left);
|
||||
ImGui::PushItemWidth(window_width - cursor_size_slider_left);
|
||||
ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
@ -134,6 +146,24 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
|
||||
m_imgui->text(m_desc.at("cursor_type"));
|
||||
ImGui::SameLine(window_width - cursor_type_combo_width - m_imgui->scaled(0.5f));
|
||||
ImGui::PushItemWidth(cursor_type_combo_width);
|
||||
int selection = int(m_cursor_type);
|
||||
m_imgui->combo("", cursor_types, selection);
|
||||
m_cursor_type = TriangleSelector::CursorType(selection);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(max_tooltip_width);
|
||||
ImGui::TextUnformatted(_L("Sphere paints all facets inside, regardless of their orientation.\n\n"
|
||||
"Circle ignores facets facing away from the camera.").ToUTF8().data());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_c->object_clipper()->get_position() == 0.f)
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
|
@ -56,8 +56,7 @@ NotificationManager::PopNotification::~PopNotification()
|
||||
}
|
||||
NotificationManager::PopNotification::RenderResult NotificationManager::PopNotification::render(GLCanvas3D& canvas, const float& initial_y)
|
||||
{
|
||||
if (!m_initialized)
|
||||
{
|
||||
if (!m_initialized) {
|
||||
init();
|
||||
}
|
||||
if (m_finished)
|
||||
@ -362,7 +361,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
//hover color
|
||||
ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button);
|
||||
ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);//ImGui::GetStyleColorVec4(ImGuiCol_Button);
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly))
|
||||
orange_color.y += 0.2f;
|
||||
|
||||
@ -682,11 +681,13 @@ void NotificationManager::push_plater_error_notification(const std::string& text
|
||||
void NotificationManager::push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas)
|
||||
{
|
||||
push_notification_data({ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }, canvas, 0);
|
||||
// dissaper if in preview
|
||||
set_in_preview(m_in_preview);
|
||||
}
|
||||
void NotificationManager::close_plater_error_notification()
|
||||
void NotificationManager::close_plater_error_notification(const std::string& text)
|
||||
{
|
||||
for (PopNotification* notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::PlaterError) {
|
||||
if (notification->get_type() == NotificationType::PlaterError && notification->compare_text(_u8L("ERROR:") + "\n" + text)) {
|
||||
notification->close();
|
||||
}
|
||||
}
|
||||
|
@ -220,7 +220,8 @@ public:
|
||||
void compare_warning_oids(const std::vector<size_t>& living_oids);
|
||||
void push_plater_error_notification(const std::string& text, GLCanvas3D& canvas);
|
||||
void push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas);
|
||||
void close_plater_error_notification();
|
||||
// Closes error or warning of same text
|
||||
void close_plater_error_notification(const std::string& text);
|
||||
void close_plater_warning_notification(const std::string& text);
|
||||
// creates special notification slicing complete
|
||||
// if large = true prints printing time and export button
|
||||
@ -250,7 +251,7 @@ private:
|
||||
bool m_hovered { false };
|
||||
//timestamps used for slining finished - notification could be gone so it needs to be stored here
|
||||
std::unordered_set<int> m_used_timestamps;
|
||||
bool m_in_preview;
|
||||
bool m_in_preview { false };
|
||||
|
||||
//prepared (basic) notifications
|
||||
const std::vector<NotificationData> basic_notifications = {
|
||||
|
Loading…
Reference in New Issue
Block a user