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