Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
on:
workflow_dispatch:
workflow_call:
pull_request:

jobs:
test-windows:
Expand Down
8 changes: 7 additions & 1 deletion src/pstack/gui/controls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,23 @@ void controls::initialize(main_window* parent) {
minimize_text = new wxStaticText(panel, wxID_ANY, "Minimize box:");
quantity_spinner = new wxSpinCtrl(panel);
quantity_spinner->SetRange(0, 200);
quantity_spinner->Disable();
min_hole_spinner = new wxSpinCtrl(panel);
min_hole_spinner->SetRange(0, 100);
minimize_checkbox = new wxCheckBox(panel, wxID_ANY, "");
min_hole_spinner->Disable();
minimize_checkbox = new wxCheckBox(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxCHK_3STATE);
minimize_checkbox->Disable();
wxArrayString rotation_choices;
rotation_choices.Add("None");
rotation_choices.Add("Cubic");
rotation_choices.Add("Arbitrary");
rotation_text = new wxStaticText(panel, wxID_ANY, "Rotations:");
rotation_dropdown = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, rotation_choices);
rotation_dropdown->Disable();
preview_voxelization_button = new wxButton(panel, wxID_ANY, "Preview voxelization");
preview_voxelization_button->Disable();
preview_bounding_box_button = new wxButton(panel, wxID_ANY, "Preview bounding box");
preview_bounding_box_button->Disable();
}

{
Expand Down
136 changes: 93 additions & 43 deletions src/pstack/gui/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ main_window::main_window(const wxString& title)
_parts_list.initialize(_controls.notebook_panels[0]);
_results_list.initialize(_controls.notebook_panels[2]);
bind_all_controls();
enable_part_settings(false);

wxGLAttributes attrs;
attrs.PlatformDefaults().Defaults().EndList();
Expand All @@ -45,33 +44,71 @@ main_window::main_window(const wxString& title)
}

void main_window::on_select_parts(const std::vector<std::size_t>& indices) {
const auto size = indices.size();
_controls.delete_part_button->Enable(size != 0);
_controls.reload_part_button->Enable(size != 0);
_controls.copy_part_button->Enable(size == 1);
_controls.mirror_part_button->Enable(size == 1);
if (size == 1) {
set_part(indices[0]);
} else {
unset_part();
const bool any_selected = not indices.empty();
_controls.delete_part_button->Enable(any_selected);
_controls.reload_part_button->Enable(any_selected);
_controls.copy_part_button->Enable(any_selected);
_controls.mirror_part_button->Enable(any_selected);
enable_part_settings(any_selected);
_current_parts.clear();
if (not any_selected) {
return;
}
}

void main_window::set_part(const std::size_t index) {
enable_part_settings(true);
_current_part = _parts_list.at(index);
_current_part_index.emplace(index);
_controls.quantity_spinner->SetValue(_current_part->quantity);
_controls.min_hole_spinner->SetValue(_current_part->min_hole);
_controls.minimize_checkbox->SetValue(_current_part->rotate_min_box);
_controls.rotation_dropdown->SetSelection(_current_part->rotation_index);
_viewport->set_mesh(_current_part->mesh, _current_part->centroid);
}
std::optional<int> quantity{};
std::optional<int> min_hole{};
std::optional<bool> rotate_min_box{};
std::optional<int> rotation_index{};
bool first_time = true;
for (const std::size_t index : indices) {
calc::part& part = _parts_list.at(index);
_current_parts.emplace_back(&part, index);
if (first_time) {
first_time = false;
quantity.emplace(part.quantity);
min_hole.emplace(part.min_hole);
rotate_min_box.emplace(part.rotate_min_box);
rotation_index.emplace(part.rotation_index);
} else {
if (quantity.has_value() and *quantity != part.quantity) {
quantity.reset();
}
if (min_hole.has_value() and *min_hole != part.min_hole) {
min_hole.reset();
}
if (rotate_min_box.has_value() and *rotate_min_box != part.rotate_min_box) {
rotate_min_box.reset();
}
if (rotation_index.has_value() and *rotation_index != part.rotation_index) {
rotation_index.reset();
}
}
}
if (quantity.has_value()) {
_controls.quantity_spinner->SetValue(*quantity);
} else {
_controls.quantity_spinner->SetValue("");
}
if (min_hole.has_value()) {
_controls.min_hole_spinner->SetValue(*min_hole);
} else {
_controls.min_hole_spinner->SetValue("");
}
if (rotate_min_box.has_value()) {
_controls.minimize_checkbox->SetValue(*rotate_min_box);
} else {
_controls.minimize_checkbox->Set3StateValue(wxCHK_UNDETERMINED);
}
if (rotation_index.has_value()) {
_controls.rotation_dropdown->SetSelection(*rotation_index);
} else {
_controls.rotation_dropdown->SetSelection(wxNOT_FOUND);
}

void main_window::unset_part() {
enable_part_settings(false);
_current_part.reset();
_current_part_index.reset();
if (_current_parts.size() == 1) {
const calc::part& part = *_current_parts[0].part;
_viewport->set_mesh(part.mesh, part.centroid);
}
}

void main_window::enable_part_settings(bool enable) {
Expand Down Expand Up @@ -113,9 +150,7 @@ void main_window::on_switch_tab(wxBookCtrlEvent& event) {
switch (event.GetSelection()) {
case 0: {
_parts_list.get_selected(selected);
if (selected.size() == 1) {
set_part(selected[0]);
}
on_select_parts(selected);
break;
}
case 2: {
Expand Down Expand Up @@ -207,7 +242,7 @@ void main_window::on_stacking_success(calc::stack_result result, const std::chro

void main_window::enable_on_stacking(const bool starting) {
const bool enable = not starting;
enable_part_settings(enable and _current_part_index.has_value());
enable_part_settings(enable and not _current_parts.empty());
_parts_list.control()->Enable(enable);
for (wxMenuItem* item : _disableable_menu_items) {
item->Enable(enable);
Expand Down Expand Up @@ -328,7 +363,7 @@ wxMenuBar* main_window::make_menu_bar() {

auto preferences_menu = new wxMenu();
preferences_menu->AppendCheckItem((int)menu_item::pref_scroll, "Invert &scroll", "Change the viewport scroll direction");
preferences_menu->AppendCheckItem((int)menu_item::pref_extra, "Display &extra parts", "Display the extra part quantity separately");
preferences_menu->AppendCheckItem((int)menu_item::pref_extra, "Display &extra parts", "Display the quantity of extra parts separately");
menu_bar->Append(preferences_menu, "&Preferences");

auto help_menu = new wxMenu();
Expand All @@ -354,16 +389,23 @@ void main_window::bind_all_controls() {
_controls.delete_part_button->Bind(wxEVT_BUTTON, &main_window::on_delete_part, this);
_controls.reload_part_button->Bind(wxEVT_BUTTON, &main_window::on_reload_part, this);
_controls.copy_part_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) {
_parts_list.append(*_current_part);
for (auto& current_part : _current_parts) {
_parts_list.append(*current_part.part);
}
_parts_list.update_label();
event.Skip();
});
_controls.mirror_part_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) {
_current_part->mirrored = not _current_part->mirrored;
_current_part->mesh.mirror_x();
_current_part->mesh.set_baseline({ 0, 0, 0 });
_parts_list.reload_text(_current_part_index.value());
set_part(_current_part_index.value());
static thread_local std::vector<std::size_t> indices{};
indices.clear();
for (auto& current_part : _current_parts) {
indices.push_back(current_part.index);
current_part.part->mirrored = not current_part.part->mirrored;
current_part.part->mesh.mirror_x();
current_part.part->mesh.set_baseline({ 0, 0, 0 });
_parts_list.reload_text(current_part.index);
}
on_select_parts(indices);
event.Skip();
});

Expand All @@ -373,21 +415,29 @@ void main_window::bind_all_controls() {
_controls.sinterbox_result_button->Bind(wxEVT_BUTTON, &main_window::on_sinterbox_result, this);

_controls.quantity_spinner->Bind(wxEVT_SPINCTRL, [this](wxSpinEvent& event) {
_current_part->quantity = event.GetPosition();
_parts_list.reload_quantity(_current_part_index.value());
for (auto& current_part : _current_parts) {
current_part.part->quantity = event.GetPosition();
_parts_list.reload_quantity(current_part.index);
}
event.Skip();
});
_controls.min_hole_spinner->Bind(wxEVT_SPINCTRL, [this](wxSpinEvent& event) {
_current_part->min_hole = event.GetPosition();
for (auto& current_part : _current_parts) {
current_part.part->min_hole = event.GetPosition();
}
event.Skip();
});
_controls.minimize_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) {
_current_part->rotate_min_box = event.IsChecked();
for (auto& current_part : _current_parts) {
current_part.part->rotate_min_box = event.IsChecked();
}
event.Skip();
});

_controls.rotation_dropdown->Bind(wxEVT_CHOICE, [this](wxCommandEvent& event) {
_current_part->rotation_index = _controls.rotation_dropdown->GetSelection();
for (auto& current_part : _current_parts) {
current_part.part->rotation_index = _controls.rotation_dropdown->GetSelection();
}
event.Skip();
});

Expand All @@ -411,7 +461,7 @@ void main_window::on_new(wxCommandEvent& event) {
{
_controls.reset_values();
_parts_list.delete_all();
unset_part();
on_select_parts({});
_results_list.delete_all();
unset_result();
_viewport->remove_mesh();
Expand Down Expand Up @@ -450,7 +500,7 @@ void main_window::on_import_part(wxCommandEvent& event) {
}
_parts_list.update_label();
if (paths.size() == 1) {
const calc::part& part = *_parts_list.at(_parts_list.rows() - 1);
const calc::part& part = _parts_list.at(_parts_list.rows() - 1);
_viewport->set_mesh(part.mesh, part.centroid);
}

Expand Down
9 changes: 5 additions & 4 deletions src/pstack/gui/main_window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ class main_window : public wxFrame {
preferences _preferences;

void on_select_parts(const std::vector<std::size_t>& indices);
void set_part(std::size_t index);
void unset_part();
parts_list _parts_list{};
std::shared_ptr<calc::part> _current_part = nullptr;
std::optional<std::size_t> _current_part_index = std::nullopt;
struct _current_part_t {
calc::part* part;
std::size_t index;
};
std::vector<_current_part_t> _current_parts{};
void enable_part_settings(bool enable);

void on_select_results(const std::vector<std::size_t>& indices);
Expand Down
20 changes: 14 additions & 6 deletions src/pstack/gui/parts_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,21 @@ calc::part make_part(std::string mesh_file, bool mirrored) {
}

wxString quantity_string(const calc::part& part, const bool show_extra) {
if (show_extra and part.base_quantity.has_value()) {
const int diff = part.quantity - *part.base_quantity;
if (diff > 0) {
return wxString::Format("%d + %d", *part.base_quantity, diff);
}
if (not part.base_quantity.has_value()) {
return wxString::Format("%d", part.quantity);
}

static const wxString up_arrow = wxString::FromUTF8(" \xe2\x86\x91"); // ↑
static const wxString down_arrow = wxString::FromUTF8(" \xe2\x86\x93"); // ↓
static const wxString empty = "";

const int diff = part.quantity - *part.base_quantity;
if (diff > 0 and show_extra) {
return wxString::Format("%d + %d%s", *part.base_quantity, diff, up_arrow);
} else {
auto& arrow_suffix = (diff > 0) ? up_arrow : (diff < 0) ? down_arrow : empty;
return wxString::Format("%d%s", part.quantity, arrow_suffix);
}
return wxString::Format("%d", part.quantity);
}

} // namespace
Expand Down
4 changes: 2 additions & 2 deletions src/pstack/gui/parts_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class parts_list : public list_view {
void reload_quantity(std::size_t row);
void delete_all();
void delete_selected();
std::shared_ptr<calc::part> at(std::size_t row) {
return _parts.at(row);
calc::part& at(std::size_t row) {
return *_parts.at(row);
}
std::vector<std::shared_ptr<const calc::part>> get_all() const;

Expand Down