2023-04-27 14:31:06 +03:00
|
|
|
#include <nanogui/opengl.h>
|
|
|
|
|
#include <nanogui/screen.h>
|
|
|
|
|
#include <nanogui/window.h>
|
|
|
|
|
#include <nanogui/layout.h>
|
|
|
|
|
#include <nanogui/label.h>
|
|
|
|
|
#include <nanogui/button.h>
|
|
|
|
|
#include <nanogui/popupbutton.h>
|
|
|
|
|
#include <nanogui/progressbar.h>
|
|
|
|
|
#include <nanogui/messagedialog.h>
|
|
|
|
|
#include <nanogui/texture.h>
|
2023-04-28 16:08:05 +03:00
|
|
|
#include <nanogui/textarea.h>
|
2023-05-04 14:15:33 +03:00
|
|
|
#include <nanogui/textbox.h>
|
|
|
|
|
#include <nanogui/tabwidget.h>
|
2023-04-27 14:31:06 +03:00
|
|
|
#include <nanogui/shader.h>
|
|
|
|
|
#include <nanogui/renderpass.h>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
#define STB_IMAGE_STATIC
|
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
|
# pragma warning (disable: 4505) // don't warn about dead code in stb_image.h
|
|
|
|
|
#elif defined(__GNUC__)
|
|
|
|
|
# pragma GCC diagnostic ignored "-Wunused-function"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <stb_image.h>
|
|
|
|
|
#include "grbl.h"
|
|
|
|
|
#include <gtest/gtest.h>
|
2023-05-03 12:41:54 +03:00
|
|
|
#include <regex>
|
2023-04-28 18:42:18 +03:00
|
|
|
#include "grbl_machine.h"
|
2023-04-28 16:08:05 +03:00
|
|
|
#include "string_utils.h"
|
2023-05-03 12:41:54 +03:00
|
|
|
#include "render.h"
|
2023-05-03 13:39:47 +03:00
|
|
|
#include "glm/gtx/quaternion.hpp"
|
2023-05-04 14:15:33 +03:00
|
|
|
#include "nanogui/nanogui.h"
|
2023-05-07 23:06:12 +03:00
|
|
|
#include "glm/gtc/matrix_inverse.hpp"
|
2023-05-09 14:30:39 +03:00
|
|
|
#include "heightmap.h"
|
2023-05-03 13:39:47 +03:00
|
|
|
#include <glm/ext/quaternion_float.hpp>
|
|
|
|
|
#include <glm/ext/quaternion_trigonometric.hpp>
|
2023-05-03 12:41:54 +03:00
|
|
|
#include <glm/vec3.hpp> // glm::vec3
|
|
|
|
|
#include <glm/vec4.hpp> // glm::vec4
|
|
|
|
|
#include <glm/mat4x4.hpp> // glm::mat4
|
|
|
|
|
#include <glm/ext/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale
|
|
|
|
|
#include <glm/ext/matrix_clip_space.hpp> // glm::perspective
|
|
|
|
|
#include <glm/ext/scalar_constants.hpp> // glm::pi
|
2023-04-27 14:31:06 +03:00
|
|
|
|
|
|
|
|
using namespace nanogui;
|
|
|
|
|
|
2023-04-27 23:15:06 +03:00
|
|
|
grbl::machine cnc{};
|
|
|
|
|
|
2023-04-28 14:50:58 +03:00
|
|
|
class SenderApp : public Screen, public grbl::machine_listener {
|
2023-04-27 14:31:06 +03:00
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
Window *window;
|
2023-04-27 23:15:06 +03:00
|
|
|
grbl::jog_state jog;
|
2023-05-05 22:53:46 +03:00
|
|
|
TextBox *txt_status;
|
2023-05-05 22:25:19 +03:00
|
|
|
TextBox *txtMessage;
|
2023-05-05 22:53:46 +03:00
|
|
|
nanogui::Color color_red = nanogui::Color(255, 0, 0, 255);
|
2023-04-28 18:42:18 +03:00
|
|
|
nanogui::Color colGreen = nanogui::Color(0, 255, 0, 255);
|
2023-04-28 14:50:58 +03:00
|
|
|
int last_alarm = 0;
|
2023-04-28 18:42:18 +03:00
|
|
|
grbl::program pgm;
|
2023-05-05 22:53:46 +03:00
|
|
|
Button *btn_load_program, *btn_check_program, *btn_run_program;
|
2023-04-27 14:31:06 +03:00
|
|
|
|
2023-05-05 22:25:19 +03:00
|
|
|
std::vector<std::string> jog_distances = {"0.01", "0.1", "1", "10"};
|
|
|
|
|
std::vector<std::string> jog_feed_rates = {"5", "100", "500", "1000"};
|
|
|
|
|
|
2023-05-03 12:41:54 +03:00
|
|
|
grbl::program_renderer renderer;
|
|
|
|
|
|
|
|
|
|
glm::vec3 cam_target = glm::vec3(0);
|
2023-05-03 13:39:47 +03:00
|
|
|
float cam_zoom = 0;
|
|
|
|
|
glm::quat cam_src_rotation = glm::quat(1.0, 0.0, 0.0, 0.0); // identity quaternion
|
|
|
|
|
|
2023-05-07 23:06:12 +03:00
|
|
|
Widget *info_layer, *heightmap_layer;
|
2023-05-04 14:15:33 +03:00
|
|
|
TabWidget *tab_widget;
|
|
|
|
|
VScrollPanel *settings_vscroll;
|
|
|
|
|
Widget *settings_layer;
|
2023-05-03 13:39:47 +03:00
|
|
|
|
2023-05-04 14:15:33 +03:00
|
|
|
VScrollPanel *parameters_vscroll;
|
|
|
|
|
Widget *parameters_layer;
|
|
|
|
|
TextBox *mpos_x_text, *mpos_y_text, *mpos_z_text;
|
2023-05-05 22:25:19 +03:00
|
|
|
ComboBox *cbo_work_offset, *cbo_tool, *cbo_jog_feed_rates, *cbo_jog_distance;
|
2023-05-06 00:19:57 +03:00
|
|
|
|
|
|
|
|
ToolButton *btn_pin_door, *btn_pin_hold, *btn_pin_reset, *btn_pin_cycle_start;
|
|
|
|
|
ToolButton *btn_pin_limit_x, *btn_pin_limit_y, *btn_pin_limit_z, *btn_pin_probe;
|
|
|
|
|
|
2023-05-05 22:25:19 +03:00
|
|
|
Button *btn_keyboard_jog;
|
2023-05-05 22:53:46 +03:00
|
|
|
std::stringstream dro_ss;
|
2023-05-09 14:30:39 +03:00
|
|
|
grbl::heightmap heightmap_grid;
|
2023-05-05 22:53:46 +03:00
|
|
|
|
2023-05-04 14:15:33 +03:00
|
|
|
|
2023-04-27 14:31:06 +03:00
|
|
|
SenderApp() : Screen(Vector2i(1024, 768), "GRBL Sender") {
|
|
|
|
|
inc_ref();
|
2023-05-04 14:15:33 +03:00
|
|
|
|
2023-04-28 14:50:58 +03:00
|
|
|
window = new Window(this, "Machine status");
|
2023-04-27 14:31:06 +03:00
|
|
|
window->set_position(Vector2i(0, 0));
|
2023-05-04 14:15:33 +03:00
|
|
|
window->set_layout(new BoxLayout(nanogui::Orientation::Vertical));
|
2023-04-28 14:50:58 +03:00
|
|
|
|
2023-05-04 14:15:33 +03:00
|
|
|
tab_widget = window->add<TabWidget>();
|
2023-05-07 23:06:12 +03:00
|
|
|
tab_widget->set_tabs_draggable(true);
|
2023-05-04 14:15:33 +03:00
|
|
|
tab_widget->set_callback([&](int index) {
|
|
|
|
|
tab_widget->set_selected_index(index);
|
|
|
|
|
});
|
|
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
info_layer = tab_widget->add<Widget>();
|
|
|
|
|
info_layer->set_layout(new GroupLayout(10, 20, 30, 0));
|
|
|
|
|
info_layer->add<Label>("");
|
|
|
|
|
|
2023-05-07 11:05:01 +03:00
|
|
|
tab_widget->append_tab("Info", info_layer);
|
|
|
|
|
|
|
|
|
|
auto probe_layer = tab_widget->add<Widget>();
|
|
|
|
|
probe_layer->set_layout(new GroupLayout(10, 20, 30, 0));
|
|
|
|
|
probe_layer->add<Label>("");
|
|
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
add_status_markup();
|
|
|
|
|
add_dro_markup();
|
2023-05-07 11:05:01 +03:00
|
|
|
add_program_extents();
|
2023-05-06 00:19:57 +03:00
|
|
|
add_pins_markup();
|
2023-05-05 22:53:46 +03:00
|
|
|
add_jogging_markup();
|
|
|
|
|
add_work_parameters_markup();
|
2023-05-07 11:05:01 +03:00
|
|
|
add_z_probe_markup();
|
2023-05-05 22:53:46 +03:00
|
|
|
|
2023-05-07 23:06:12 +03:00
|
|
|
heightmap_layer = tab_widget->add<Widget>();
|
|
|
|
|
heightmap_layer->set_layout(new GroupLayout(10, 20, 30, 0));
|
|
|
|
|
heightmap_layer->add<Label>("");
|
|
|
|
|
tab_widget->append_tab("Heightmap", heightmap_layer);
|
|
|
|
|
add_heightmap_markup();
|
|
|
|
|
|
|
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
perform_layout();
|
|
|
|
|
|
|
|
|
|
m_render_pass = new RenderPass({this});
|
2023-05-07 23:06:12 +03:00
|
|
|
m_render_pass->set_clear_color(0, Color(0.13f, 0.13f, 0.13f, 1.f));
|
2023-05-09 14:30:39 +03:00
|
|
|
m_render_pass->set_clear_depth(0.0);
|
2023-05-05 22:53:46 +03:00
|
|
|
m_render_pass->set_depth_test(RenderPass::DepthTest::Always, true);
|
|
|
|
|
m_render_pass->set_cull_mode(RenderPass::CullMode::Disabled);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-07 11:05:01 +03:00
|
|
|
TextBox *txt_min_z, *txt_feed;
|
|
|
|
|
|
|
|
|
|
void add_z_probe_markup() {
|
|
|
|
|
info_layer->add<Label>("Z-Probe", "sans-bold", 20);
|
|
|
|
|
auto z_probe_holder = info_layer->add<Widget>();
|
|
|
|
|
z_probe_holder->set_layout(new BoxLayout(Orientation::Vertical, Alignment::Fill, 2, 2));
|
|
|
|
|
|
|
|
|
|
auto another_holder = z_probe_holder->add<Widget>();
|
|
|
|
|
another_holder->set_layout(new GridLayout(Orientation::Horizontal, 2, Alignment::Fill, 2, 2));
|
|
|
|
|
|
|
|
|
|
another_holder->add<Label>("Feed", "sans-bold");
|
|
|
|
|
txt_feed = another_holder->add<TextBox>("100");
|
|
|
|
|
txt_feed->set_editable(true);
|
|
|
|
|
txt_feed->set_fixed_width(100);
|
|
|
|
|
|
|
|
|
|
another_holder->add<Label>("Min Z", "sans-bold");
|
|
|
|
|
txt_min_z = another_holder->add<TextBox>("-65");
|
|
|
|
|
txt_min_z->set_editable(true);
|
|
|
|
|
txt_min_z->set_fixed_width(100);
|
|
|
|
|
|
|
|
|
|
z_probe_holder->add<Label>("");
|
|
|
|
|
auto btn_start_probing = z_probe_holder->add<Button>("Start probing");
|
|
|
|
|
btn_start_probing->set_callback([&]() {
|
|
|
|
|
cnc.start_z_probe(std::stof(txt_min_z->value()), std::stof(txt_feed->value()));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
void add_work_parameters_markup() {
|
|
|
|
|
// work parameters
|
|
|
|
|
info_layer->add<Label>("Work parameters", "sans-bold", 20);
|
|
|
|
|
auto work_holder = info_layer->add<Widget>();
|
|
|
|
|
work_holder->set_layout(new BoxLayout(Orientation::Horizontal, Alignment::Fill, 6, 6));
|
2023-04-28 18:42:18 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
auto work_data_holder = work_holder->add<Widget>();
|
|
|
|
|
work_data_holder->set_layout(new GridLayout(Orientation::Horizontal, 2, Alignment::Fill, 6, 6));
|
2023-04-28 18:42:18 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
work_data_holder->add<Label>("Offset", "sans-bold");
|
|
|
|
|
std::vector<std::string> offset_items = {"G54", "G55", "G56", "G57", "G58", "G59"};
|
|
|
|
|
cbo_work_offset = work_data_holder->add<ComboBox>(offset_items);
|
|
|
|
|
cbo_work_offset->set_callback([&](int idx) {
|
|
|
|
|
refresh_offset();
|
|
|
|
|
update_dro();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
work_data_holder->add<Label>("Tool", "sans-bold");
|
|
|
|
|
std::vector<std::string> tool_items = {"None"};
|
|
|
|
|
cbo_tool = work_data_holder->add<ComboBox>(tool_items);
|
|
|
|
|
|
|
|
|
|
auto work_btn_holder = work_holder->add<Widget>();
|
|
|
|
|
work_btn_holder->set_layout(new GridLayout(Orientation::Horizontal, 3, Alignment::Fill, 0, 6));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
btn_load_program = work_btn_holder->add<Button>("Load");
|
|
|
|
|
btn_load_program->set_callback([&] {
|
|
|
|
|
auto path = file_dialog(
|
|
|
|
|
{{"gcode", "G-Code files"},
|
|
|
|
|
{"nc", "G-Code files"},
|
|
|
|
|
{"ngc", "G-Code files"}}, true);
|
|
|
|
|
|
|
|
|
|
if (pgm.load_from_file(path)) {
|
|
|
|
|
init_program_geometry();
|
2023-05-07 11:05:01 +03:00
|
|
|
set_program_extents();
|
2023-05-07 23:06:12 +03:00
|
|
|
fill_heightmap_from_model();
|
|
|
|
|
update_grid();
|
2023-05-05 22:53:46 +03:00
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
btn_load_program->set_tooltip("Load program");
|
|
|
|
|
|
|
|
|
|
btn_check_program = work_btn_holder->add<Button>("Check");
|
|
|
|
|
// btnCheckProgram->set_enabled(false);
|
|
|
|
|
btn_check_program->set_callback([&] {
|
|
|
|
|
cnc.check_program(pgm);
|
|
|
|
|
});
|
|
|
|
|
btn_check_program->set_tooltip("Check program");
|
|
|
|
|
|
|
|
|
|
btn_run_program = work_btn_holder->add<Button>("Run");
|
|
|
|
|
// btnRunProgram->set_enabled(false);
|
|
|
|
|
btn_run_program->set_callback([&] {
|
|
|
|
|
cnc.run_program(pgm, "G" + std::to_string(cbo_work_offset->selected_index() + 54));
|
|
|
|
|
});
|
|
|
|
|
btn_run_program->set_tooltip("Execute program");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void add_status_markup() {
|
|
|
|
|
auto status_holder = info_layer->add<Widget>();
|
2023-05-05 22:25:19 +03:00
|
|
|
status_holder->set_layout(new BoxLayout(Orientation::Vertical, Alignment::Fill, 0, 0));
|
|
|
|
|
|
|
|
|
|
auto status_text_holder = status_holder->add<Widget>();
|
2023-05-05 22:53:46 +03:00
|
|
|
status_text_holder->set_layout(new GridLayout(Orientation::Horizontal, 3, Alignment::Fill, 0, 0));
|
2023-05-05 22:25:19 +03:00
|
|
|
|
|
|
|
|
status_text_holder->add<Label>("Status", "sans-bold", 20);
|
2023-04-28 14:50:58 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
txt_status = status_text_holder->add<TextBox>();
|
|
|
|
|
txt_status->set_editable(false);
|
|
|
|
|
txt_status->set_fixed_width(300);
|
2023-05-05 22:25:19 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
auto *btn_reset = status_text_holder->add<Button>("Reset");
|
|
|
|
|
btn_reset->set_background_color(color_red);
|
|
|
|
|
btn_reset->set_callback([&] {
|
2023-05-05 22:25:19 +03:00
|
|
|
cnc.request_reset();
|
|
|
|
|
});
|
2023-04-28 16:08:05 +03:00
|
|
|
|
2023-05-05 22:25:19 +03:00
|
|
|
//
|
|
|
|
|
status_text_holder->add<Label>("");
|
|
|
|
|
|
|
|
|
|
txtMessage = status_text_holder->add<TextBox>("");
|
|
|
|
|
txtMessage->set_fixed_width(300);
|
|
|
|
|
|
|
|
|
|
status_text_holder->add<Label>("");
|
|
|
|
|
|
|
|
|
|
// buttons
|
|
|
|
|
auto status_btn_holder = status_holder->add<Widget>();
|
|
|
|
|
status_btn_holder->set_layout(new GridLayout(Orientation::Horizontal, 2, Alignment::Fill, 6, 6));
|
|
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
auto *btn_homing = new Button(status_btn_holder, "Homing", FA_HOME);
|
|
|
|
|
btn_homing->set_callback([&] {
|
2023-05-05 22:25:19 +03:00
|
|
|
cnc.request_home();
|
|
|
|
|
});
|
|
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
auto *btn_unlock = new Button(status_btn_holder, "Unlock", FA_UNLOCK);
|
|
|
|
|
btn_unlock->set_callback([&] {
|
2023-05-05 22:25:19 +03:00
|
|
|
cnc.request_unlock();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// buttons to change state
|
2023-05-05 22:53:46 +03:00
|
|
|
auto *btn_cycle_start = status_btn_holder->add<Button>("Cycle Start", FA_PLAY);
|
|
|
|
|
btn_cycle_start->set_callback([&] {
|
2023-05-05 22:25:19 +03:00
|
|
|
cnc.request_cycle_start();
|
|
|
|
|
});
|
2023-05-05 22:53:46 +03:00
|
|
|
auto *btn_feed_hold = status_btn_holder->add<Button>("Feed Hold", FA_PAUSE);
|
|
|
|
|
btn_feed_hold->set_callback([&] {
|
2023-05-05 22:25:19 +03:00
|
|
|
cnc.request_feed_hold();
|
|
|
|
|
});
|
2023-05-05 22:53:46 +03:00
|
|
|
}
|
2023-04-28 14:50:58 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
void add_jogging_markup() {
|
2023-05-05 16:58:34 +03:00
|
|
|
// jogging
|
2023-05-05 22:53:46 +03:00
|
|
|
info_layer->add<Label>("Jogging", "sans-bold", 20);
|
|
|
|
|
auto holder = new Widget(info_layer);
|
2023-05-05 16:58:34 +03:00
|
|
|
|
2023-05-05 22:25:19 +03:00
|
|
|
holder->set_layout(new GridLayout(Orientation::Horizontal, 3, Alignment::Fill, 0, 0));
|
|
|
|
|
|
|
|
|
|
// feed and distance
|
2023-05-05 22:53:46 +03:00
|
|
|
auto jog_params_holder = holder->add<Widget>();
|
|
|
|
|
jog_params_holder->set_layout(new GridLayout(Orientation::Horizontal, 3, Alignment::Fill, 0, 4));
|
2023-05-05 22:25:19 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_params_holder->add<Label>("Feed", "sans-bold");
|
|
|
|
|
cbo_jog_feed_rates = jog_params_holder->add<ComboBox>(jog_feed_rates);
|
2023-05-05 22:25:19 +03:00
|
|
|
cbo_jog_feed_rates->set_selected_index(2);
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_params_holder->add<Label>("mm/min");
|
2023-05-05 22:25:19 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_params_holder->add<Label>("Distance", "sans-bold");
|
|
|
|
|
cbo_jog_distance = jog_params_holder->add<ComboBox>(jog_distances);
|
2023-05-05 22:25:19 +03:00
|
|
|
cbo_jog_distance->set_selected_index(2);
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_params_holder->add<Label>("mm");
|
2023-05-05 16:58:34 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_params_holder->add<Label>("Keyboard Jog", "sans-bold");
|
|
|
|
|
btn_keyboard_jog = jog_params_holder->add<Button>("", FA_KEYBOARD);
|
|
|
|
|
btn_keyboard_jog->set_flags(Button::ToggleButton);
|
|
|
|
|
jog_params_holder->add<Label>("");
|
2023-05-05 22:25:19 +03:00
|
|
|
|
|
|
|
|
// X and Y
|
2023-05-05 22:53:46 +03:00
|
|
|
auto jog_btn_holder = holder->add<Widget>();
|
|
|
|
|
jog_btn_holder->set_layout(new GridLayout(Orientation::Horizontal, 3, Alignment::Fill, 0, 6));
|
2023-05-05 16:58:34 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_btn_holder->add<Label>("");
|
|
|
|
|
auto jog_btn = jog_btn_holder->add<Button>("", FA_ARROW_ALT_CIRCLE_UP);
|
2023-05-05 22:25:19 +03:00
|
|
|
jog_btn->set_callback([&]() {
|
|
|
|
|
// FIXME: get rid of std::stof
|
|
|
|
|
cnc.request_jog_fixed(grbl::jog_direction::y_up,
|
|
|
|
|
std::stof(jog_distances[cbo_jog_distance->selected_index()]),
|
|
|
|
|
std::stof(jog_feed_rates[cbo_jog_feed_rates->selected_index()]));
|
|
|
|
|
});
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_btn_holder->add<Label>("");
|
2023-05-05 16:58:34 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_btn = jog_btn_holder->add<Button>("", FA_ARROW_ALT_CIRCLE_LEFT);
|
2023-05-05 22:25:19 +03:00
|
|
|
jog_btn->set_callback([&]() {
|
|
|
|
|
// FIXME: get rid of std::stof
|
|
|
|
|
cnc.request_jog_fixed(grbl::jog_direction::x_down,
|
|
|
|
|
std::stof(jog_distances[cbo_jog_distance->selected_index()]),
|
|
|
|
|
std::stof(jog_feed_rates[cbo_jog_feed_rates->selected_index()]));
|
|
|
|
|
});
|
|
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_btn = jog_btn_holder->add<Button>("", FA_HOME);
|
2023-05-05 22:25:19 +03:00
|
|
|
jog_btn->set_callback([&]() {
|
|
|
|
|
cnc.go_to_zero(true, true, false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_btn = jog_btn_holder->add<Button>("", FA_ARROW_ALT_CIRCLE_RIGHT);
|
2023-05-05 22:25:19 +03:00
|
|
|
jog_btn->set_callback([&]() {
|
|
|
|
|
// FIXME: get rid of std::stof
|
|
|
|
|
cnc.request_jog_fixed(grbl::jog_direction::x_up,
|
|
|
|
|
std::stof(jog_distances[cbo_jog_distance->selected_index()]),
|
|
|
|
|
std::stof(jog_feed_rates[cbo_jog_feed_rates->selected_index()]));
|
|
|
|
|
});
|
2023-05-05 16:58:34 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_btn_holder->add<Label>("");
|
|
|
|
|
jog_btn = jog_btn_holder->add<Button>("", FA_ARROW_ALT_CIRCLE_DOWN);
|
2023-05-05 22:25:19 +03:00
|
|
|
jog_btn->set_callback([&]() {
|
|
|
|
|
// FIXME: get rid of std::stof
|
|
|
|
|
cnc.request_jog_fixed(grbl::jog_direction::y_down,
|
|
|
|
|
std::stof(jog_distances[cbo_jog_distance->selected_index()]),
|
|
|
|
|
std::stof(jog_feed_rates[cbo_jog_feed_rates->selected_index()]));
|
|
|
|
|
});
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_btn_holder->add<Label>("");
|
2023-05-05 16:58:34 +03:00
|
|
|
|
2023-05-05 22:25:19 +03:00
|
|
|
|
|
|
|
|
// z up/down
|
2023-05-05 22:53:46 +03:00
|
|
|
auto jog_z_btn_holder = holder->add<Widget>();
|
|
|
|
|
jog_z_btn_holder->set_layout(new BoxLayout(Orientation::Vertical, Alignment::Fill, 0, 6));
|
2023-05-05 22:25:19 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_btn = jog_z_btn_holder->add<Button>("", FA_CHEVRON_UP);
|
2023-05-05 22:25:19 +03:00
|
|
|
jog_btn->set_callback([&]() {
|
|
|
|
|
// FIXME: get rid of std::stof
|
|
|
|
|
cnc.request_jog_fixed(grbl::jog_direction::z_up,
|
|
|
|
|
std::stof(jog_distances[cbo_jog_distance->selected_index()]),
|
|
|
|
|
std::stof(jog_feed_rates[cbo_jog_feed_rates->selected_index()]));
|
|
|
|
|
});
|
2023-05-05 16:58:34 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_btn = jog_z_btn_holder->add<Button>("", FA_HOME);
|
2023-05-05 22:25:19 +03:00
|
|
|
jog_btn->set_callback([&]() {
|
2023-05-05 22:53:46 +03:00
|
|
|
cnc.go_to_zero(false, false, true);
|
2023-05-05 22:25:19 +03:00
|
|
|
});
|
|
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
jog_btn = jog_z_btn_holder->add<Button>("", FA_CHEVRON_DOWN);
|
2023-05-05 22:25:19 +03:00
|
|
|
jog_btn->set_callback([&]() {
|
|
|
|
|
// FIXME: get rid of std::stof
|
|
|
|
|
cnc.request_jog_fixed(grbl::jog_direction::z_down,
|
|
|
|
|
std::stof(jog_distances[cbo_jog_distance->selected_index()]),
|
|
|
|
|
std::stof(jog_feed_rates[cbo_jog_feed_rates->selected_index()]));
|
|
|
|
|
});
|
2023-05-05 22:53:46 +03:00
|
|
|
}
|
2023-05-05 16:58:34 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
void add_dro_markup() {
|
2023-05-06 00:19:57 +03:00
|
|
|
dro_ss << std::setprecision(3) << std::fixed;
|
2023-05-04 15:57:34 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
// DRO
|
|
|
|
|
info_layer->add<Label>("DRO", "sans-bold", 20);
|
|
|
|
|
auto dro_holder = info_layer->add<Widget>();
|
|
|
|
|
dro_holder->set_layout(new GridLayout(Orientation::Horizontal, 1, Alignment::Middle, 0, 6));
|
2023-05-05 22:25:19 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
auto x_holder = dro_holder->add<Widget>();
|
|
|
|
|
x_holder->set_layout(new BoxLayout(Orientation::Horizontal, Alignment::Middle, 0, 6));
|
|
|
|
|
auto lbl = x_holder->add<Label>("X", "sans-bold", 20);
|
|
|
|
|
lbl->set_fixed_width(30);
|
|
|
|
|
mpos_x_text = x_holder->add<TextBox>(std::to_string(cnc.get_status().machine_pos[0]));
|
|
|
|
|
mpos_x_text->set_fixed_width(200);
|
|
|
|
|
auto zero_btn = x_holder->add<ToolButton>(FA_BAN);
|
|
|
|
|
zero_btn->set_flags(Button::NormalButton);
|
|
|
|
|
zero_btn->set_tooltip("Zero axis");
|
|
|
|
|
zero_btn->set_callback([&]() {
|
|
|
|
|
cnc.zero_offset_axis(cbo_work_offset->selected_index(), 0);
|
2023-05-04 15:57:34 +03:00
|
|
|
});
|
|
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
auto y_holder = dro_holder->add<Widget>();
|
|
|
|
|
y_holder->set_layout(new BoxLayout(Orientation::Horizontal, Alignment::Middle, 0, 6));
|
|
|
|
|
lbl = y_holder->add<Label>("Y", "sans-bold", 20);
|
|
|
|
|
lbl->set_fixed_width(30);
|
|
|
|
|
mpos_y_text = y_holder->add<TextBox>(std::to_string(cnc.get_status().machine_pos[1]));
|
|
|
|
|
mpos_y_text->set_fixed_width(200);
|
|
|
|
|
zero_btn = y_holder->add<ToolButton>(FA_BAN);
|
|
|
|
|
zero_btn->set_flags(Button::NormalButton);
|
|
|
|
|
zero_btn->set_tooltip("Zero axis");
|
|
|
|
|
zero_btn->set_callback([&]() {
|
|
|
|
|
cnc.zero_offset_axis(cbo_work_offset->selected_index(), 1);
|
2023-04-27 23:15:06 +03:00
|
|
|
});
|
2023-04-28 18:42:18 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
auto z_holder = dro_holder->add<Widget>();
|
|
|
|
|
z_holder->set_layout(new BoxLayout(Orientation::Horizontal, Alignment::Middle, 0, 6));
|
|
|
|
|
lbl = z_holder->add<Label>("Z", "sans-bold", 20);
|
|
|
|
|
lbl->set_fixed_width(30);
|
|
|
|
|
mpos_z_text = z_holder->add<TextBox>(std::to_string(cnc.get_status().machine_pos[0]));
|
|
|
|
|
mpos_z_text->set_fixed_width(200);
|
|
|
|
|
zero_btn = z_holder->add<ToolButton>(FA_BAN);
|
|
|
|
|
zero_btn->set_flags(Button::NormalButton);
|
|
|
|
|
zero_btn->set_tooltip("Zero axis");
|
|
|
|
|
zero_btn->set_callback([&]() {
|
|
|
|
|
cnc.zero_offset_axis(cbo_work_offset->selected_index(), 2);
|
2023-04-28 18:42:18 +03:00
|
|
|
});
|
|
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
auto axis_btn_holder = dro_holder->add<Widget>();
|
|
|
|
|
axis_btn_holder->set_layout(new BoxLayout(Orientation::Horizontal, Alignment::Middle, 0, 6));
|
2023-04-28 14:50:58 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
auto zero_all_btn = axis_btn_holder->add<Button>("Zero all");
|
|
|
|
|
zero_all_btn->set_callback([&]() {
|
|
|
|
|
cnc.zero_offset(cbo_work_offset->selected_index());
|
|
|
|
|
});
|
2023-04-27 14:31:06 +03:00
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
auto goto_zero_btn = axis_btn_holder->add<Button>("Goto 0");
|
|
|
|
|
goto_zero_btn->set_callback([&]() {
|
|
|
|
|
cnc.go_to_zero(true, true, true);
|
|
|
|
|
});
|
2023-04-27 14:31:06 +03:00
|
|
|
}
|
|
|
|
|
|
2023-05-07 11:05:01 +03:00
|
|
|
TextBox *extents_min_x, *extents_max_x;
|
|
|
|
|
TextBox *extents_min_y, *extents_max_y;
|
|
|
|
|
TextBox *extents_min_z, *extents_max_z;
|
|
|
|
|
|
|
|
|
|
void add_program_extents() {
|
|
|
|
|
dro_ss << std::setprecision(3) << std::fixed;
|
|
|
|
|
|
|
|
|
|
// Program extents
|
|
|
|
|
info_layer->add<Label>("Program extents", "sans-bold", 20);
|
|
|
|
|
auto extents_holder = info_layer->add<Widget>();
|
|
|
|
|
extents_holder->set_layout(new GridLayout(Orientation::Horizontal, 3, Alignment::Fill, 4, 4));
|
|
|
|
|
|
|
|
|
|
extents_holder->add<Label>("X", "sans-bold", 20);
|
|
|
|
|
extents_min_x = extents_holder->add<TextBox>("");
|
|
|
|
|
extents_max_x = extents_holder->add<TextBox>("");
|
|
|
|
|
|
|
|
|
|
extents_holder->add<Label>("Y", "sans-bold", 20);
|
|
|
|
|
extents_min_y = extents_holder->add<TextBox>("");
|
|
|
|
|
extents_max_y = extents_holder->add<TextBox>("");
|
|
|
|
|
|
|
|
|
|
extents_holder->add<Label>("Z", "sans-bold", 20);
|
|
|
|
|
extents_min_z = extents_holder->add<TextBox>("");
|
|
|
|
|
extents_max_z = extents_holder->add<TextBox>("");
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-07 23:06:12 +03:00
|
|
|
TextBox *txt_heightmap_from_x, *txt_heightmap_from_y;
|
|
|
|
|
TextBox *txt_heightmap_to_x, *txt_heightmap_to_y;
|
|
|
|
|
TextBox *txt_grid_res;
|
2023-05-09 14:30:39 +03:00
|
|
|
TextBox *txt_clearance_height, *txt_start_probing_at, *txt_max_negative_z, *txt_final_z_height;
|
2023-05-07 23:06:12 +03:00
|
|
|
|
|
|
|
|
void add_heightmap_markup() {
|
|
|
|
|
heightmap_layer->add<Label>("Grid definition", "sans-bold", 20);
|
|
|
|
|
auto heightmap_params_holder = heightmap_layer->add<Widget>();
|
|
|
|
|
heightmap_params_holder->set_layout(new BoxLayout(Orientation::Vertical, Alignment::Fill, 4, 4));
|
|
|
|
|
|
|
|
|
|
// grid size
|
|
|
|
|
auto grid_size_holder = heightmap_params_holder->add<Widget>();
|
|
|
|
|
grid_size_holder->set_layout(new GridLayout(Orientation::Horizontal, 3, Alignment::Middle, 4, 4));
|
|
|
|
|
|
|
|
|
|
grid_size_holder->add<Label>("", "sans-bold", 20);
|
|
|
|
|
grid_size_holder->add<Label>("X", "sans-bold", 20);
|
|
|
|
|
grid_size_holder->add<Label>("Y", "sans-bold", 20);
|
|
|
|
|
|
|
|
|
|
grid_size_holder->add<Label>("From", "sans-bold", 20);
|
|
|
|
|
txt_heightmap_from_x = grid_size_holder->add<TextBox>("");
|
|
|
|
|
txt_heightmap_from_x->set_editable(true);
|
|
|
|
|
txt_heightmap_from_x->set_fixed_width(150);
|
|
|
|
|
txt_heightmap_from_y = grid_size_holder->add<TextBox>("");
|
|
|
|
|
txt_heightmap_from_y->set_editable(true);
|
|
|
|
|
txt_heightmap_from_y->set_fixed_width(150);
|
|
|
|
|
|
|
|
|
|
grid_size_holder->add<Label>("To", "sans-bold", 20);
|
|
|
|
|
txt_heightmap_to_x = grid_size_holder->add<TextBox>("");
|
|
|
|
|
txt_heightmap_to_x->set_editable(true);
|
|
|
|
|
txt_heightmap_to_x->set_fixed_width(150);
|
|
|
|
|
txt_heightmap_to_y = grid_size_holder->add<TextBox>("");
|
|
|
|
|
txt_heightmap_to_y->set_editable(true);
|
|
|
|
|
txt_heightmap_to_y->set_fixed_width(150);
|
|
|
|
|
|
|
|
|
|
grid_size_holder->add<Label>("");
|
|
|
|
|
auto btn = grid_size_holder->add<Button>("Fetch from model");
|
|
|
|
|
btn->set_callback([&]() {
|
|
|
|
|
fill_heightmap_from_model();
|
|
|
|
|
});
|
|
|
|
|
grid_size_holder->add<Label>("");
|
|
|
|
|
|
|
|
|
|
txt_heightmap_from_x->set_callback([&](const std::string& new_value) {
|
|
|
|
|
update_grid();
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
txt_heightmap_from_y->set_callback([&](const std::string& new_value) {
|
|
|
|
|
update_grid();
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
txt_heightmap_to_x->set_callback([&](const std::string& new_value) {
|
|
|
|
|
update_grid();
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
txt_heightmap_to_y->set_callback([&](const std::string& new_value) {
|
|
|
|
|
update_grid();
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// grid resolution
|
|
|
|
|
auto grid_res_holder = heightmap_params_holder->add<Widget>();
|
|
|
|
|
grid_res_holder->set_layout(new BoxLayout(nanogui::Orientation::Horizontal, Alignment::Middle, 4, 4));
|
|
|
|
|
grid_res_holder->add<Label>("Grid resolution", "sans-bold", 20);
|
|
|
|
|
txt_grid_res = grid_res_holder->add<TextBox>("5");
|
|
|
|
|
txt_grid_res->set_editable(true);
|
|
|
|
|
txt_grid_res->set_fixed_width(150);
|
|
|
|
|
txt_grid_res->set_callback([&](const std::string& new_value) {
|
|
|
|
|
update_grid();
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
grid_res_holder->add<Label>("mm", "sans-bold", 20);
|
2023-05-09 14:30:39 +03:00
|
|
|
|
|
|
|
|
// business params
|
|
|
|
|
auto heightmap_business_holder = heightmap_params_holder->add<Widget>();
|
|
|
|
|
heightmap_business_holder->set_layout(new GridLayout(nanogui::Orientation::Horizontal, 3, Alignment::Fill, 4, 4));
|
|
|
|
|
|
|
|
|
|
heightmap_business_holder->add<Label>("Clearance height Z", "sans-bold", 20);
|
|
|
|
|
txt_clearance_height = heightmap_business_holder->add<TextBox>("1.5");
|
|
|
|
|
txt_clearance_height->set_editable(true);
|
|
|
|
|
heightmap_business_holder->add<Label>("", "sans-bold");
|
|
|
|
|
|
|
|
|
|
heightmap_business_holder->add<Label>("Start probing at Z", "sans-bold", 20);
|
|
|
|
|
txt_start_probing_at = heightmap_business_holder->add<TextBox>("0.5");
|
|
|
|
|
txt_start_probing_at->set_editable(true);
|
|
|
|
|
heightmap_business_holder->add<Label>("", "sans-bold");
|
|
|
|
|
|
|
|
|
|
heightmap_business_holder->add<Label>("Max negative Z", "sans-bold", 20);
|
|
|
|
|
txt_max_negative_z = heightmap_business_holder->add<TextBox>("-1.0");
|
|
|
|
|
txt_max_negative_z->set_editable(true);
|
|
|
|
|
heightmap_business_holder->add<Label>("(when to fail probing)", "sans-bold");
|
|
|
|
|
|
|
|
|
|
heightmap_business_holder->add<Label>("Final Z safety height", "sans-bold", 20);
|
|
|
|
|
txt_final_z_height = heightmap_business_holder->add<TextBox>("15");
|
|
|
|
|
txt_final_z_height->set_editable(true);
|
|
|
|
|
heightmap_business_holder->add<Label>("", "sans-bold");
|
|
|
|
|
|
|
|
|
|
heightmap_business_holder->add<Label>("", "sans-bold");
|
|
|
|
|
auto btn_start_probing = heightmap_business_holder->add<Button>("Start Probing");
|
|
|
|
|
btn_start_probing->set_callback([&]() {
|
|
|
|
|
cnc.probe_heightmap(heightmap_grid);
|
|
|
|
|
});
|
|
|
|
|
heightmap_business_holder->add<Label>("", "sans-bold");
|
2023-05-07 23:06:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void fill_heightmap_from_model() {
|
|
|
|
|
auto min = renderer.get_extents_min();
|
|
|
|
|
auto max = renderer.get_extents_max();
|
|
|
|
|
txt_heightmap_from_x->set_value(std::to_string(min.x));
|
|
|
|
|
txt_heightmap_from_y->set_value(std::to_string(min.y));
|
|
|
|
|
txt_heightmap_to_x->set_value(std::to_string(max.x));
|
|
|
|
|
txt_heightmap_to_y->set_value(std::to_string(max.y));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void update_grid() {
|
|
|
|
|
auto from_x = std::stof(txt_heightmap_from_x->value());
|
|
|
|
|
auto from_y = std::stof(txt_heightmap_from_y->value());
|
|
|
|
|
auto to_x = std::stof(txt_heightmap_to_x->value());
|
|
|
|
|
auto to_y = std::stof(txt_heightmap_to_y->value());
|
2023-05-09 14:30:39 +03:00
|
|
|
auto res = std::stof(txt_grid_res->value());
|
|
|
|
|
|
|
|
|
|
if (from_x != heightmap_grid.from_x ||
|
|
|
|
|
from_y != heightmap_grid.from_y ||
|
|
|
|
|
to_x != heightmap_grid.to_x ||
|
|
|
|
|
to_y != heightmap_grid.to_y ||
|
|
|
|
|
res != heightmap_grid.resolution) {
|
|
|
|
|
heightmap_grid = std::move(grbl::heightmap::from_params(from_x, from_y, to_x, to_y, res));
|
|
|
|
|
renderer.update_grid(heightmap_grid);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-07 23:06:12 +03:00
|
|
|
}
|
|
|
|
|
|
2023-05-06 00:19:57 +03:00
|
|
|
void add_pins_markup() {
|
|
|
|
|
// Pins
|
|
|
|
|
info_layer->add<Label>("Pins", "sans-bold", 20);
|
|
|
|
|
auto pins_holder = info_layer->add<Widget>();
|
|
|
|
|
pins_holder->set_layout(new BoxLayout(Orientation::Horizontal, Alignment::Fill, 4, 4));
|
|
|
|
|
|
|
|
|
|
btn_pin_door = pins_holder->add<ToolButton>(0, "D");
|
|
|
|
|
// btn_pin_door->set_enabled(false);
|
|
|
|
|
btn_pin_door->set_flags(Button::Flags::NormalButton);
|
|
|
|
|
|
|
|
|
|
btn_pin_hold = pins_holder->add<ToolButton>(0, "H");
|
|
|
|
|
// btn_pin_hold->set_enabled(false);
|
|
|
|
|
btn_pin_hold->set_flags(Button::Flags::NormalButton);
|
|
|
|
|
|
|
|
|
|
btn_pin_reset = pins_holder->add<ToolButton>(0, "R");
|
|
|
|
|
// btn_pin_reset->set_enabled(false);
|
|
|
|
|
btn_pin_reset->set_flags(Button::Flags::NormalButton);
|
|
|
|
|
|
|
|
|
|
btn_pin_cycle_start = pins_holder->add<ToolButton>(0, "S");
|
|
|
|
|
// btn_pin_cycle_start->set_enabled(false);
|
|
|
|
|
btn_pin_cycle_start->set_flags(Button::Flags::NormalButton);
|
|
|
|
|
|
|
|
|
|
auto a = pins_holder->add<Label>("");
|
|
|
|
|
a->set_fixed_width(30);
|
|
|
|
|
|
|
|
|
|
btn_pin_limit_x = pins_holder->add<ToolButton>(0, "X");
|
|
|
|
|
// btn_pin_limit_x->set_enabled(false);
|
|
|
|
|
btn_pin_limit_x->set_flags(Button::Flags::NormalButton);
|
|
|
|
|
|
|
|
|
|
btn_pin_limit_y = pins_holder->add<ToolButton>(0, "Y");
|
|
|
|
|
// btn_pin_limit_y->set_enabled(false);
|
|
|
|
|
btn_pin_limit_y->set_flags(Button::Flags::NormalButton);
|
|
|
|
|
|
|
|
|
|
btn_pin_limit_z = pins_holder->add<ToolButton>(0, "Z");
|
|
|
|
|
// btn_pin_limit_z->set_enabled(false);
|
|
|
|
|
btn_pin_limit_z->set_flags(Button::Flags::NormalButton);
|
|
|
|
|
|
|
|
|
|
btn_pin_probe = pins_holder->add<ToolButton>(0, "P");
|
|
|
|
|
// btn_pin_probe->set_enabled(false);
|
|
|
|
|
btn_pin_probe->set_flags(Button::Flags::NormalButton);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-05 22:25:19 +03:00
|
|
|
void refresh_offset() const {
|
|
|
|
|
auto offset_name = "G" + std::to_string(cbo_work_offset->selected_index() + 54);
|
2023-05-05 15:42:30 +03:00
|
|
|
cnc.set_work_offset(offset_name);
|
2023-05-04 15:57:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void fill_in_parameters() {
|
|
|
|
|
parameters_vscroll = new VScrollPanel(tab_widget);
|
|
|
|
|
tab_widget->append_tab("Parameters", parameters_vscroll);
|
|
|
|
|
|
|
|
|
|
parameters_layer = new Widget(parameters_vscroll);
|
2023-05-06 00:19:57 +03:00
|
|
|
parameters_layer->set_layout(new GridLayout(Orientation::Horizontal, 1, Alignment::Middle));
|
2023-05-04 15:57:34 +03:00
|
|
|
|
|
|
|
|
auto& parameters = cnc.get_parameters();
|
|
|
|
|
|
|
|
|
|
for (auto& entry: parameters) {
|
|
|
|
|
auto w = parameters_layer->add<Widget>();
|
2023-05-06 00:19:57 +03:00
|
|
|
w->set_layout(new BoxLayout(Orientation::Horizontal, Alignment::Middle, 2, 2));
|
2023-05-04 15:57:34 +03:00
|
|
|
auto x = w->add<Label>(entry.first, "sans-bold", 20);
|
|
|
|
|
x->set_fixed_width(50);
|
|
|
|
|
|
|
|
|
|
auto y = w->add<TextBox>(entry.second);
|
|
|
|
|
y->set_editable(true);
|
|
|
|
|
y->set_fixed_width(200);
|
|
|
|
|
|
2023-05-06 00:19:57 +03:00
|
|
|
auto z = w->add<Button>("", FA_SAVE);
|
|
|
|
|
// z->set_flags(Button::Flags::NormalButton); // no toggle, please
|
2023-05-04 15:57:34 +03:00
|
|
|
z->set_tooltip("save");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void fill_in_settings() {
|
|
|
|
|
settings_vscroll = new VScrollPanel(tab_widget);
|
|
|
|
|
tab_widget->append_tab("Settings", settings_vscroll);
|
|
|
|
|
|
|
|
|
|
settings_layer = new Widget(settings_vscroll);
|
|
|
|
|
settings_layer->set_layout(new GridLayout(Orientation::Horizontal, 1, Alignment::Middle));
|
|
|
|
|
|
|
|
|
|
auto& settings = cnc.get_settings();
|
|
|
|
|
|
|
|
|
|
for (auto& s: settings) {
|
|
|
|
|
auto w = settings_layer->add<Widget>();
|
|
|
|
|
w->set_layout(new BoxLayout(Orientation::Horizontal, Alignment::Middle, 2, 2));
|
|
|
|
|
auto x = w->add<Label>(s.first, "sans-bold", 20);
|
|
|
|
|
x->set_fixed_width(40);
|
|
|
|
|
|
|
|
|
|
auto desc = grbl::setting_description(std::stoi(s.first.substr(1)));
|
|
|
|
|
|
|
|
|
|
auto y = w->add<TextBox>(s.second);
|
|
|
|
|
y->set_editable(true);
|
|
|
|
|
y->set_tooltip(desc);
|
2023-05-05 22:25:19 +03:00
|
|
|
y->set_fixed_width(150);
|
2023-05-04 15:57:34 +03:00
|
|
|
|
|
|
|
|
auto z = w->add<ToolButton>(FA_SAVE);
|
|
|
|
|
z->set_flags(Button::Flags::NormalButton); // no toggle, please
|
|
|
|
|
z->set_tooltip("save");
|
|
|
|
|
|
|
|
|
|
auto t = w->add<TextArea>();
|
|
|
|
|
t->set_fixed_width(200);
|
|
|
|
|
t->set_fixed_height(20);
|
|
|
|
|
t->clear();
|
|
|
|
|
t->append(desc.substr(0, desc.find_first_of('\n')));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-04 14:15:33 +03:00
|
|
|
void init_program_geometry() {
|
|
|
|
|
renderer.update(pgm, cnc);
|
|
|
|
|
|
|
|
|
|
auto max_pos = renderer.get_extents_max();
|
|
|
|
|
auto min_pos = renderer.get_extents_min();
|
|
|
|
|
|
|
|
|
|
cam_target = (max_pos - min_pos) / 2.0f;
|
|
|
|
|
cam_zoom = (max_pos.x - min_pos.x);
|
|
|
|
|
cam_src_rotation = glm::quat(1.0, 0.0, 0.0, 0.0);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-07 11:05:01 +03:00
|
|
|
void set_program_extents() {
|
|
|
|
|
auto min = renderer.get_extents_min();
|
|
|
|
|
auto max = renderer.get_extents_max();
|
|
|
|
|
|
|
|
|
|
extents_min_x->set_value(std::to_string(min.x));
|
|
|
|
|
extents_max_x->set_value(std::to_string(max.x));
|
|
|
|
|
|
|
|
|
|
extents_min_y->set_value(std::to_string(min.y));
|
|
|
|
|
extents_max_y->set_value(std::to_string(max.y));
|
|
|
|
|
|
|
|
|
|
extents_min_z->set_value(std::to_string(min.z));
|
|
|
|
|
extents_max_z->set_value(std::to_string(max.z));
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-27 23:15:06 +03:00
|
|
|
bool resize_event(const Vector2i& size) override {
|
2023-05-05 22:53:46 +03:00
|
|
|
// window->set_fixed_height(this->height() / 2);
|
|
|
|
|
tab_widget->set_fixed_height((this->height() - 30));
|
|
|
|
|
perform_layout();
|
|
|
|
|
|
2023-04-28 14:50:58 +03:00
|
|
|
return Screen::resize_event(size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void on_connected() override {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void on_disconnected() override {
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-04 15:57:34 +03:00
|
|
|
void on_settings_reloaded() override {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void on_parameters_reloaded() override {
|
|
|
|
|
refresh_offset();
|
|
|
|
|
update_dro();
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-28 16:08:05 +03:00
|
|
|
|
|
|
|
|
grbl::realtime_status_report last_report;
|
|
|
|
|
|
2023-05-05 16:58:34 +03:00
|
|
|
volatile bool machine_initialized = false;
|
|
|
|
|
|
2023-05-04 14:15:33 +03:00
|
|
|
void on_init_completed() override {
|
2023-05-05 16:58:34 +03:00
|
|
|
machine_initialized = true;
|
|
|
|
|
// need to do UI related things in UI thread
|
2023-05-04 14:15:33 +03:00
|
|
|
}
|
|
|
|
|
|
2023-04-28 14:50:58 +03:00
|
|
|
void on_realtime_status_report(grbl::realtime_status_report report) override {
|
2023-05-04 15:57:34 +03:00
|
|
|
// if (report == last_report) return;
|
2023-04-28 16:08:05 +03:00
|
|
|
|
2023-05-05 22:25:19 +03:00
|
|
|
// if (last_report.status != report.status) {
|
2023-05-05 22:53:46 +03:00
|
|
|
txt_status->set_value(grbl::status_to_string(cnc.get_status().status));
|
2023-04-28 16:08:05 +03:00
|
|
|
|
2023-05-05 22:25:19 +03:00
|
|
|
if (cnc.get_status().status == grbl::machine_status::alarm) {
|
2023-05-05 22:53:46 +03:00
|
|
|
txt_status->set_tooltip(grbl::alarm_to_string(last_alarm));
|
2023-05-05 22:25:19 +03:00
|
|
|
} else {
|
2023-05-05 22:53:46 +03:00
|
|
|
txt_status->set_tooltip(cnc.get_status().sub_status);
|
2023-04-28 14:50:58 +03:00
|
|
|
}
|
2023-05-05 22:25:19 +03:00
|
|
|
// }
|
2023-04-28 14:50:58 +03:00
|
|
|
|
2023-05-06 00:19:57 +03:00
|
|
|
|
|
|
|
|
// FIXME: ugly way of retrieving the bg color
|
|
|
|
|
btn_pin_door->set_background_color(report.signals.bit.door ? color_red : btn_load_program->background_color());
|
|
|
|
|
btn_pin_hold->set_background_color(report.signals.bit.hold ? color_red : btn_load_program->background_color());
|
|
|
|
|
btn_pin_reset->set_background_color(report.signals.bit.soft_reset ? color_red : btn_load_program->background_color());
|
|
|
|
|
btn_pin_cycle_start->set_background_color(report.signals.bit.cycle_start ? color_red : btn_load_program->background_color());
|
|
|
|
|
btn_pin_limit_x->set_background_color(report.signals.bit.x_limit ? color_red : btn_load_program->background_color());
|
|
|
|
|
btn_pin_limit_y->set_background_color(report.signals.bit.y_limit ? color_red : btn_load_program->background_color());
|
|
|
|
|
btn_pin_limit_z->set_background_color(report.signals.bit.z_limit ? color_red : btn_load_program->background_color());
|
|
|
|
|
btn_pin_probe->set_background_color(report.signals.bit.probe ? color_red : btn_load_program->background_color());
|
|
|
|
|
|
2023-05-04 15:57:34 +03:00
|
|
|
update_dro();
|
2023-04-28 16:08:05 +03:00
|
|
|
last_report = report;
|
2023-04-28 14:50:58 +03:00
|
|
|
}
|
|
|
|
|
|
2023-05-04 15:57:34 +03:00
|
|
|
void update_dro() {
|
2023-05-05 15:42:30 +03:00
|
|
|
auto work_pos = cnc.get_work_pos();
|
2023-05-05 22:53:46 +03:00
|
|
|
|
|
|
|
|
dro_ss.str("");
|
|
|
|
|
dro_ss << work_pos[0];
|
|
|
|
|
mpos_x_text->set_value(dro_ss.str());
|
|
|
|
|
|
|
|
|
|
dro_ss.str("");
|
|
|
|
|
dro_ss << work_pos[1];
|
|
|
|
|
mpos_y_text->set_value(dro_ss.str());
|
|
|
|
|
|
|
|
|
|
dro_ss.str("");
|
|
|
|
|
dro_ss << work_pos[2];
|
|
|
|
|
mpos_z_text->set_value(dro_ss.str());
|
2023-05-04 15:57:34 +03:00
|
|
|
}
|
|
|
|
|
|
2023-04-28 14:50:58 +03:00
|
|
|
void on_banner(std::string line) override {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void on_message(std::string message) override {
|
2023-05-05 22:25:19 +03:00
|
|
|
txtMessage->set_value(message);
|
2023-04-28 14:50:58 +03:00
|
|
|
set_caption(message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void on_alarm(int alarm) override {
|
|
|
|
|
last_alarm = alarm;
|
2023-04-27 14:31:06 +03:00
|
|
|
}
|
|
|
|
|
|
2023-05-07 11:05:01 +03:00
|
|
|
void on_probe_result(bool probe_touched, float *probe_coords) override {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "Probing ended. Result: " << std::boolalpha << probe_touched << " at coords: ";
|
|
|
|
|
for (auto i = 0; i < 3; i++) {
|
|
|
|
|
ss << probe_coords[i] << ", ";
|
|
|
|
|
}
|
|
|
|
|
new MessageDialog(this, MessageDialog::Type::Warning, "Probe result", ss.str());
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-09 14:30:39 +03:00
|
|
|
void on_heightmap_probe_acquired(grbl::heightmap *grid) override {
|
|
|
|
|
std::cout << "Updating grid" << std::endl;
|
|
|
|
|
renderer.update_grid(*grid);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-28 18:54:49 +03:00
|
|
|
void on_check_completed(bool success, size_t failed_index, size_t error) override {
|
2023-05-05 22:53:46 +03:00
|
|
|
btn_check_program->set_background_color(success ? colGreen : color_red);
|
2023-04-28 18:42:18 +03:00
|
|
|
if (success) {
|
2023-05-05 22:53:46 +03:00
|
|
|
btn_run_program->set_enabled(true);
|
2023-04-28 18:42:18 +03:00
|
|
|
new MessageDialog(this, MessageDialog::Type::Information, "Check result", "Program checked successfully");
|
|
|
|
|
} else {
|
2023-05-05 22:53:46 +03:00
|
|
|
btn_run_program->set_enabled(false);
|
2023-04-28 18:42:18 +03:00
|
|
|
std::stringstream ss;
|
|
|
|
|
auto i = pgm.instruction_at(failed_index);
|
2023-04-28 18:54:49 +03:00
|
|
|
ss << "Program check failed at line " << i.line << " (" << i.command << "). Error: " << error << ", "
|
|
|
|
|
<< grbl::error_to_string(error);
|
2023-04-28 18:42:18 +03:00
|
|
|
new MessageDialog(this, MessageDialog::Type::Warning, "Check result", ss.str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-28 18:54:49 +03:00
|
|
|
void on_run_completed(bool success, size_t failed_index, size_t error) override {
|
2023-05-05 22:53:46 +03:00
|
|
|
btn_run_program->set_background_color(success ? colGreen : color_red);
|
2023-04-28 18:42:18 +03:00
|
|
|
if (success) {
|
|
|
|
|
new MessageDialog(this, MessageDialog::Type::Information, "Run result", "Program executed successfully");
|
|
|
|
|
} else {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
auto i = pgm.instruction_at(failed_index);
|
2023-04-28 18:54:49 +03:00
|
|
|
ss << "Program execution failed at line " << i.line << " (" << i.command << "). Error: " << error << ", "
|
|
|
|
|
<< grbl::error_to_string(error);
|
2023-04-28 18:42:18 +03:00
|
|
|
new MessageDialog(this, MessageDialog::Type::Warning, "Run result", ss.str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-27 23:15:06 +03:00
|
|
|
|
2023-04-28 14:50:58 +03:00
|
|
|
bool keyboard_event(int key, int scancode, int action, int modifiers) override {
|
2023-04-27 14:31:06 +03:00
|
|
|
if (Screen::keyboard_event(key, scancode, action, modifiers))
|
|
|
|
|
return true;
|
2023-05-03 12:41:54 +03:00
|
|
|
|
|
|
|
|
// if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
|
|
|
|
|
// set_visible(false);
|
|
|
|
|
// return true;
|
|
|
|
|
// }
|
2023-04-27 14:31:06 +03:00
|
|
|
|
2023-05-04 14:15:33 +03:00
|
|
|
if (key == GLFW_KEY_SPACE && action == GLFW_PRESS && !window->mouse_focused()) {
|
|
|
|
|
// reset trackball rotation
|
|
|
|
|
cam_src_rotation = glm::quat(1, 0, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-27 23:15:06 +03:00
|
|
|
auto new_jog = jog;
|
|
|
|
|
|
|
|
|
|
if (key == GLFW_KEY_LEFT_SHIFT) {
|
|
|
|
|
if (action == GLFW_PRESS) {
|
|
|
|
|
new_jog.speed_fast_pressed = true;
|
|
|
|
|
} else if (action == GLFW_RELEASE) {
|
|
|
|
|
new_jog.speed_fast_pressed = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key == GLFW_KEY_LEFT_CONTROL) {
|
|
|
|
|
if (action == GLFW_PRESS) {
|
|
|
|
|
new_jog.speed_slow_pressed = true;
|
|
|
|
|
} else if (action == GLFW_RELEASE) {
|
|
|
|
|
new_jog.speed_slow_pressed = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-27 14:31:06 +03:00
|
|
|
if (key == GLFW_KEY_UP) {
|
|
|
|
|
if (action == GLFW_PRESS) {
|
2023-04-27 23:15:06 +03:00
|
|
|
new_jog.up_pressed = true;
|
|
|
|
|
} else if (action == GLFW_RELEASE) {
|
|
|
|
|
new_jog.up_pressed = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key == GLFW_KEY_DOWN) {
|
|
|
|
|
if (action == GLFW_PRESS) {
|
|
|
|
|
new_jog.down_pressed = true;
|
2023-04-27 14:31:06 +03:00
|
|
|
} else if (action == GLFW_RELEASE) {
|
2023-04-27 23:15:06 +03:00
|
|
|
new_jog.down_pressed = false;
|
2023-04-27 14:31:06 +03:00
|
|
|
}
|
|
|
|
|
}
|
2023-04-27 23:15:06 +03:00
|
|
|
|
|
|
|
|
if (key == GLFW_KEY_LEFT) {
|
|
|
|
|
if (action == GLFW_PRESS) {
|
|
|
|
|
new_jog.left_pressed = true;
|
|
|
|
|
} else if (action == GLFW_RELEASE) {
|
|
|
|
|
new_jog.left_pressed = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key == GLFW_KEY_RIGHT) {
|
|
|
|
|
if (action == GLFW_PRESS) {
|
|
|
|
|
new_jog.right_pressed = true;
|
|
|
|
|
} else if (action == GLFW_RELEASE) {
|
|
|
|
|
new_jog.right_pressed = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key == GLFW_KEY_PAGE_UP) {
|
|
|
|
|
if (action == GLFW_PRESS) {
|
|
|
|
|
new_jog.z_up_pressed = true;
|
|
|
|
|
} else if (action == GLFW_RELEASE) {
|
|
|
|
|
new_jog.z_up_pressed = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key == GLFW_KEY_PAGE_DOWN) {
|
|
|
|
|
if (action == GLFW_PRESS) {
|
|
|
|
|
new_jog.z_down_pressed = true;
|
|
|
|
|
} else if (action == GLFW_RELEASE) {
|
|
|
|
|
new_jog.z_down_pressed = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-05 22:25:19 +03:00
|
|
|
if (btn_keyboard_jog->pushed() && jog != new_jog) {
|
2023-04-27 23:15:06 +03:00
|
|
|
cnc.request_jog(new_jog);
|
|
|
|
|
jog = new_jog;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-27 14:31:06 +03:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-03 12:41:54 +03:00
|
|
|
bool mouse_motion_event(const Vector2i& p, const Vector2i& rel, int button, int modifiers) override {
|
|
|
|
|
auto result = Widget::mouse_motion_event(p, rel, button, modifiers);
|
|
|
|
|
if (window->mouse_focused()) {
|
|
|
|
|
return result;
|
|
|
|
|
} else {
|
2023-05-03 13:39:47 +03:00
|
|
|
// std::cout << "Mouse motion p:" << p << ", rel:" << rel << ", button:" << button << ", .modifiers:" << modifiers << std::endl;
|
|
|
|
|
bool is_rotating = button == 1;
|
|
|
|
|
bool is_panning = button == 2;
|
|
|
|
|
if (is_rotating) {
|
|
|
|
|
if (abs(rel.x()) > abs(rel.y())) {
|
|
|
|
|
cam_src_rotation *= glm::angleAxis(rel.x() / 100.0f, glm::vec3(0, 1, 0));
|
|
|
|
|
} else {
|
|
|
|
|
cam_src_rotation *= glm::angleAxis(rel.y() / 100.0f, glm::vec3(1, 0, 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (is_panning) {
|
|
|
|
|
// cam_src.x += (float) rel.x() / 10.0f;
|
|
|
|
|
// cam_src.y += (float) rel.y() / 10.0f;
|
2023-05-03 12:41:54 +03:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool scroll_event(const Vector2i& p, const Vector2f& rel) override {
|
|
|
|
|
if (window->mouse_focused()) {
|
|
|
|
|
return Widget::scroll_event(p, rel);
|
|
|
|
|
} else {
|
2023-05-03 13:39:47 +03:00
|
|
|
// std::cout << "Scroll event: p:" << p << ", rel:" << rel << std::endl;
|
2023-05-04 14:15:33 +03:00
|
|
|
cam_zoom -= rel.y() * cam_zoom / 10.0f;
|
2023-05-03 13:39:47 +03:00
|
|
|
cam_zoom = std::max(cam_zoom, 0.1f);
|
2023-05-03 12:41:54 +03:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-05 16:58:34 +03:00
|
|
|
void draw(NVGcontext *ctx) override {
|
|
|
|
|
// not pretty but need to do this in UI thread
|
|
|
|
|
if (machine_initialized) {
|
|
|
|
|
fill_in_settings();
|
|
|
|
|
fill_in_parameters();
|
|
|
|
|
|
2023-05-05 22:25:19 +03:00
|
|
|
perform_layout();
|
|
|
|
|
|
2023-05-05 16:58:34 +03:00
|
|
|
refresh_offset();
|
|
|
|
|
update_dro();
|
|
|
|
|
machine_initialized = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget::draw(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-05 22:53:46 +03:00
|
|
|
void draw_contents() override {
|
2023-05-03 12:41:54 +03:00
|
|
|
auto fb_size = framebuffer_size();
|
|
|
|
|
|
|
|
|
|
// start rendering
|
2023-04-27 14:31:06 +03:00
|
|
|
m_render_pass->resize(framebuffer_size());
|
|
|
|
|
m_render_pass->begin();
|
|
|
|
|
|
2023-05-04 14:15:33 +03:00
|
|
|
if (pgm.is_loaded) {
|
|
|
|
|
renderer.update(pgm, cnc);
|
|
|
|
|
// compute mvp
|
|
|
|
|
glm::mat4 projection = glm::perspective(45.0f * glm::pi<float>() / 180.0f,
|
|
|
|
|
(float) fb_size.x() / (float) fb_size.y(),
|
|
|
|
|
0.1f,
|
|
|
|
|
10000.f);
|
|
|
|
|
|
|
|
|
|
glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(0, 0, -cam_zoom)) *
|
|
|
|
|
glm::toMat4(cam_src_rotation) *
|
|
|
|
|
glm::translate(glm::mat4(1.0f), -cam_target);
|
|
|
|
|
|
|
|
|
|
glm::mat4 model = glm::mat4(1.0f);
|
2023-05-07 23:06:12 +03:00
|
|
|
glm::mat3 normal_mat = glm::inverseTranspose(glm::mat3(view * model));
|
|
|
|
|
renderer.render(model, view, projection, normal_mat, glm::vec2(fb_size.x(), fb_size.y()));
|
2023-05-04 14:15:33 +03:00
|
|
|
}
|
2023-04-27 14:31:06 +03:00
|
|
|
|
|
|
|
|
m_render_pass->end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
ProgressBar *m_progress;
|
|
|
|
|
ref<RenderPass> m_render_pass;
|
|
|
|
|
|
|
|
|
|
using ImageHolder = std::unique_ptr<uint8_t[], void (*)(void *)>;
|
|
|
|
|
std::vector<std::pair<ref<Texture>, ImageHolder>> m_images;
|
|
|
|
|
int m_current_image;
|
|
|
|
|
};
|
|
|
|
|
|
2023-04-27 15:53:44 +03:00
|
|
|
|
2023-04-27 14:31:06 +03:00
|
|
|
int main(int argc, char **argv) {
|
|
|
|
|
|
|
|
|
|
testing::InitGoogleTest(&argc, argv);
|
|
|
|
|
auto result = RUN_ALL_TESTS();
|
|
|
|
|
if (result) {
|
|
|
|
|
exit(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
nanogui::init();
|
|
|
|
|
|
|
|
|
|
// scoped variables
|
|
|
|
|
{
|
|
|
|
|
ref<SenderApp> app = new SenderApp();
|
|
|
|
|
app->dec_ref();
|
|
|
|
|
app->draw_all();
|
|
|
|
|
app->set_visible(true);
|
2023-04-28 14:50:58 +03:00
|
|
|
|
|
|
|
|
cnc.set_listener(app);
|
|
|
|
|
cnc.connect();
|
|
|
|
|
|
2023-04-27 14:31:06 +03:00
|
|
|
nanogui::mainloop(1 / 60.f * 1000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nanogui::shutdown();
|
2023-04-27 23:15:06 +03:00
|
|
|
} catch (const std::exception& e) {
|
2023-04-27 14:31:06 +03:00
|
|
|
std::string error_msg = std::string("Caught a fatal error: ") + std::string(e.what());
|
|
|
|
|
std::cerr << error_msg << std::endl;
|
|
|
|
|
return -1;
|
|
|
|
|
} catch (...) {
|
|
|
|
|
std::cerr << "Caught an unknown error!" << std::endl;
|
|
|
|
|
return -2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|