1 #include "place_model_box.hpp"
2 #include "../imp_package.hpp"
3 #include "3d_view.hpp"
4 #include "canvas3d/canvas3d.hpp"
5 #include "util/util.hpp"
6 #include "util/gtk_util.hpp"
7 
8 namespace horizon {
9 
10 
11 class PlaceAtPadDialog : public Gtk::Dialog {
12 public:
13     PlaceAtPadDialog(const Package &pkg);
14     UUID selected_pad;
15 
16 private:
17     const Package &pkg;
18 };
19 
20 class MyLabel : public Gtk::Label {
21 public:
MyLabel(const std::string & txt,const UUID & uu)22     MyLabel(const std::string &txt, const UUID &uu) : Gtk::Label(txt), uuid(uu)
23     {
24         set_xalign(0);
25         property_margin() = 5;
26     }
27 
28     const UUID uuid;
29 };
30 
PlaceAtPadDialog(const Package & p)31 PlaceAtPadDialog::PlaceAtPadDialog(const Package &p)
32     : Gtk::Dialog("Place at pad", Gtk::DIALOG_MODAL | Gtk::DIALOG_USE_HEADER_BAR), pkg(p)
33 {
34     set_default_size(200, 400);
35     auto sc = Gtk::manage(new Gtk::ScrolledWindow);
36     sc->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
37 
38     auto lb = Gtk::manage(new Gtk::ListBox);
39     lb->set_selection_mode(Gtk::SELECTION_NONE);
40     lb->set_activate_on_single_click(true);
41     lb->set_header_func(sigc::ptr_fun(header_func_separator));
42     sc->add(*lb);
43 
44     std::vector<const Pad *> pads;
45     for (const auto &it : pkg.pads) {
46         pads.push_back(&it.second);
47     }
48     std::sort(pads.begin(), pads.end(), [](auto a, auto b) { return strcmp_natural(a->name, b->name) < 0; });
49 
50     for (const auto it : pads) {
51         auto la = Gtk::manage(new MyLabel(it->name, it->uuid));
52         lb->append(*la);
53     }
54     lb->signal_row_activated().connect([this](Gtk::ListBoxRow *row) {
55         auto la = dynamic_cast<MyLabel *>(row->get_child());
56         selected_pad = la->uuid;
57         response(Gtk::RESPONSE_OK);
58     });
59 
60     sc->show_all();
61     get_content_area()->set_border_width(0);
62     get_content_area()->pack_start(*sc, true, true, 0);
63 }
64 
PlaceModelBox(ImpPackage & aimp)65 PlaceModelBox::PlaceModelBox(ImpPackage &aimp) : Gtk::Box(Gtk::ORIENTATION_VERTICAL), imp(aimp)
66 {
67     {
68         auto box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 10));
69         box->property_margin() = 10;
70 
71         auto back_button = Gtk::manage(new Gtk::Button);
72         back_button->set_image_from_icon_name("go-previous-symbolic", Gtk::ICON_SIZE_BUTTON);
73         back_button->signal_clicked().connect([this] {
74             start_pick(PickState::IDLE);
75             imp.view_3d_stack->set_visible_child("models");
76         });
77         box->pack_start(*back_button, false, false, 0);
78 
79         auto la = Gtk::manage(new Gtk::Label);
80         la->set_markup("<b>Align model</b>");
81         la->get_style_context()->add_class("dim-label");
82         la->set_xalign(0);
83         box->pack_start(*la, false, false, 0);
84 
85         reset_button = Gtk::manage(new Gtk::Button("Reset"));
86         reset_button->signal_clicked().connect(sigc::mem_fun(*this, &PlaceModelBox::reset));
87         box->pack_end(*reset_button, false, false, 0);
88         box->show_all();
89         pack_start(*box, false, false, 0);
90     }
91     {
92         auto sep = Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL));
93         pack_start(*sep, false, false, 0);
94         sep->show();
95     }
96     auto sg_from_to = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
97     {
98         auto box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 10));
99         box->property_margin() = 10;
100 
101         {
102             auto la = Gtk::manage(new Gtk::Label);
103             la->set_label("From");
104             sg_from_to->add_widget(*la);
105             la->get_style_context()->add_class("dim-label");
106             la->set_xalign(0);
107             box->pack_start(*la, false, false, 0);
108         }
109 
110         {
111             auto bbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
112             bbox->get_style_context()->add_class("linked");
113             pick1_button = Gtk::manage(new Gtk::Button("Pick"));
114             pick1_button->signal_clicked().connect([this] { start_pick(PickState::PICK_1); });
115             bbox->pack_start(*pick1_button, false, false, 0);
116             pick2_button = Gtk::manage(new Gtk::Button("Pick two (center)"));
117             pick2_button->signal_clicked().connect([this] { start_pick(PickState::PICK_2_1); });
118             bbox->pack_start(*pick2_button, false, false, 0);
119             pick_cancel_button = Gtk::manage(new Gtk::Button("Cancel"));
120             pick_cancel_button->signal_clicked().connect([this] { start_pick(PickState::IDLE); });
121             bbox->pack_start(*pick_cancel_button, false, false, 0);
122             box->pack_start(*bbox, false, false, 0);
123         }
124         pick_state_label = Gtk::manage(new Gtk::Label);
125         box->pack_start(*pick_state_label, false, false, 0);
126 
127         box->show_all();
128         pack_start(*box, false, false, 0);
129     }
130     static const std::array<std::string, 3> s_xyz = {"X", "Y", "Z"};
131     auto sg = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
132     {
133         auto placement_grid = Gtk::manage(new Gtk::Grid);
134         placement_grid->set_hexpand_set(true);
135         placement_grid->set_row_spacing(5);
136         placement_grid->set_column_spacing(5);
137         placement_grid->set_margin_start(20);
138 
139         for (unsigned int ax = 0; ax < 3; ax++) {
140             auto sp = Gtk::manage(new SpinButtonDim);
141             sp->set_range(-1000_mm, 1000_mm);
142             auto la = Gtk::manage(new Gtk::Label(s_xyz.at(ax)));
143             la->set_xalign(1);
144             la->set_margin_end(4);
145             sg->add_widget(*la);
146             placement_grid->attach(*la, 0, ax, 1, 1);
147             placement_grid->attach(*sp, 1, ax, 1, 1);
148             sp_from.set(ax, sp);
149         }
150         placement_grid->show_all();
151         pack_start(*placement_grid, false, false, 0);
152     }
153 
154     {
155         auto box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 10));
156         box->property_margin() = 10;
157 
158         {
159             auto la = Gtk::manage(new Gtk::Label);
160             la->set_label("To");
161             la->get_style_context()->add_class("dim-label");
162             la->set_xalign(0);
163             sg_from_to->add_widget(*la);
164             box->pack_start(*la, false, false, 0);
165         }
166 
167         {
168             auto bu = Gtk::manage(new Gtk::Button("Pad…"));
169             box->pack_start(*bu, false, false, 0);
170             bu->signal_clicked().connect([this] {
171                 PlaceAtPadDialog dia(imp.package);
172                 dia.set_transient_for(*imp.view_3d_window);
173                 if (dia.run() == Gtk::RESPONSE_OK) {
174                     auto &pad = imp.package.pads.at(dia.selected_pad);
175                     sp_to.x->set_value(pad.placement.shift.x);
176                     sp_to.y->set_value(pad.placement.shift.y);
177                     cb_to.x->set_active(true);
178                     cb_to.y->set_active(true);
179                     cb_to.z->set_active(false);
180                 }
181             });
182         }
183         {
184             auto bu = Gtk::manage(new Gtk::Button("Origin"));
185             box->pack_start(*bu, false, false, 0);
186             bu->signal_clicked().connect([this] {
187                 for (unsigned int ax = 0; ax < 3; ax++) {
188                     sp_to.get(ax)->set_value(0);
189                 }
190             });
191         }
192 
193         box->show_all();
194         pack_start(*box, false, false, 0);
195     }
196     {
197         auto placement_grid = Gtk::manage(new Gtk::Grid);
198         placement_grid->set_hexpand_set(true);
199         placement_grid->set_row_spacing(5);
200         placement_grid->set_column_spacing(5);
201         placement_grid->set_margin_start(20);
202         for (unsigned int ax = 0; ax < 3; ax++) {
203             auto sp = Gtk::manage(new SpinButtonDim);
204             sp->set_range(-1000_mm, 1000_mm);
205             auto cb = Gtk::manage(new Gtk::CheckButton(s_xyz.at(ax)));
206             sg->add_widget(*cb);
207             placement_grid->attach(*cb, 0, ax, 1, 1);
208             placement_grid->attach(*sp, 1, ax, 1, 1);
209             sp_to.set(ax, sp);
210             cb_to.set(ax, cb);
211         }
212         placement_grid->show_all();
213         pack_start(*placement_grid, false, false, 0);
214     }
215 
216     {
217         move_button = Gtk::manage(new Gtk::Button("Move along selected axes"));
218         move_button->get_style_context()->add_class("suggested-action");
219         move_button->signal_clicked().connect(sigc::mem_fun(*this, &PlaceModelBox::do_move));
220         move_button->show();
221         move_button->property_margin() = 20;
222         pack_start(*move_button, false, false, 0);
223     }
224 
225     update_pick_state();
226 }
227 
update_pick_state()228 void PlaceModelBox::update_pick_state()
229 {
230     const char *s = nullptr;
231     switch (pick_state) {
232     case PickState::IDLE:
233         s = "";
234         break;
235 
236     case PickState::PICK_1:
237         s = "Pick point";
238         break;
239 
240     case PickState::PICK_2_1:
241         s = "Pick first point";
242         break;
243 
244     case PickState::PICK_2_2:
245         s = "Pick second point";
246         break;
247     }
248     pick_state_label->set_text(s);
249     const bool is_idle = pick_state == PickState::IDLE;
250     pick_cancel_button->set_visible(!is_idle);
251     pick1_button->set_visible(is_idle);
252     pick2_button->set_visible(is_idle);
253     reset_button->set_sensitive(is_idle);
254     move_button->set_sensitive(is_idle);
255     for (unsigned int ax = 0; ax < 3; ax++) {
256         sp_from.get(ax)->set_sensitive(is_idle);
257     }
258 }
259 
start_pick(PickState which)260 void PlaceModelBox::start_pick(PickState which)
261 {
262     switch (which) {
263     case PickState::IDLE:
264         imp.view_3d_window->get_canvas().set_show_points(false);
265         break;
266 
267     case PickState::PICK_1:
268     case PickState::PICK_2_1:
269         imp.update_points();
270         imp.view_3d_window->get_canvas().set_show_points(true);
271         break;
272 
273     default:;
274     }
275     pick_state = which;
276     update_pick_state();
277 }
278 
set_sp(SpinButtonDim * sp,double v,bool avg)279 static void set_sp(SpinButtonDim *sp, double v, bool avg)
280 {
281     if (avg)
282         sp->set_value((sp->get_value() + v * 1e6) / 2);
283     else
284         sp->set_value(v * 1e6);
285 }
286 
handle_pick(const glm::dvec3 & p)287 void PlaceModelBox::handle_pick(const glm::dvec3 &p)
288 {
289     const bool is_2 = pick_state == PickState::PICK_2_2;
290     for (unsigned int ax = 0; ax < 3; ax++) {
291         set_sp(sp_from.get(ax), p[ax], is_2);
292     }
293     switch (pick_state) {
294     case PickState::PICK_1:
295     case PickState::PICK_2_2:
296         start_pick(PickState::IDLE);
297         break;
298 
299     case PickState::PICK_2_1:
300         pick_state = PickState::PICK_2_2;
301         break;
302 
303     default:;
304     }
305     update_pick_state();
306 }
307 
do_move()308 void PlaceModelBox::do_move()
309 {
310     auto &model = imp.core_package.models.at(imp.current_model);
311     for (unsigned int ax = 0; ax < 3; ax++) {
312         if (cb_to.get(ax)->get_active()) {
313             const auto v = model.get_shift(ax);
314             auto delta = sp_to.get(ax)->get_value_as_int() - sp_from.get(ax)->get_value_as_int();
315             model.set_shift(ax, v + delta);
316             sp_from.get(ax)->set_value(sp_from.get(ax)->get_value_as_int() + delta);
317         }
318     }
319     imp.reload_model_editor();
320     imp.update_fake_board();
321     imp.view_3d_window->get_canvas().update_packages();
322     imp.core_package.set_needs_save();
323 }
324 
init(const Package::Model & model)325 void PlaceModelBox::init(const Package::Model &model)
326 {
327     for (unsigned int ax = 0; ax < 3; ax++) {
328         shift_init.at(ax) = model.get_shift(ax);
329         sp_from.get(ax)->set_value(0);
330         sp_to.get(ax)->set_value(0);
331         cb_to.get(ax)->set_active(false);
332     }
333 }
334 
reset()335 void PlaceModelBox::reset()
336 {
337     auto &model = imp.core_package.models.at(imp.current_model);
338     for (unsigned int ax = 0; ax < 3; ax++) {
339         model.set_shift(ax, shift_init.at(ax));
340     }
341     imp.reload_model_editor();
342     imp.update_fake_board();
343     imp.view_3d_window->get_canvas().update_packages();
344 }
345 } // namespace horizon
346