1 #include "renumber_pads_window.hpp"
2 #include "pool/package.hpp"
3 #include "util/gtk_util.hpp"
4 #include "util/util.hpp"
5 #include <math.h>
6 
7 namespace horizon {
RenumberPadsWindow(Gtk::Window * parent,ImpInterface * intf,Package & a_pkg,const std::set<UUID> & a_pads)8 RenumberPadsWindow::RenumberPadsWindow(Gtk::Window *parent, ImpInterface *intf, Package &a_pkg,
9                                        const std::set<UUID> &a_pads)
10     : ToolWindow(parent, intf), pkg(a_pkg)
11 {
12     set_title("Renumber pads");
13     for (const auto &it : a_pads) {
14         pads.insert(&pkg.pads.at(it));
15     }
16 
17 
18     auto grid = Gtk::manage(new Gtk::Grid());
19     grid->set_column_spacing(10);
20     grid->set_row_spacing(10);
21     grid->set_margin_bottom(20);
22     grid->set_margin_top(20);
23     grid->set_margin_end(20);
24     grid->set_margin_start(20);
25 
26     int top = 0;
27 
28     auto fn_update = [this](auto v) { this->renumber(); };
29 
30     const int width_chars = 9;
31     {
32         auto b = make_boolean_ganged_switch(circular, "Axis", "Circular", fn_update);
33         grid_attach_label_and_widget(grid, "Mode", b, top)->set_width_chars(width_chars);
34     }
35     {
36         auto b = make_boolean_ganged_switch(x_first, "X, then Y", "Y, then X", fn_update);
37         auto l = grid_attach_label_and_widget(grid, "Axis", b, top);
38         l->set_width_chars(width_chars);
39         widgets_axis.insert(b);
40         widgets_axis.insert(l);
41     }
42     {
43         auto b = make_boolean_ganged_switch(down, "Up", "Down", fn_update);
44         auto l = grid_attach_label_and_widget(grid, "Y direction", b, top);
45         l->set_width_chars(width_chars);
46         widgets_axis.insert(b);
47         widgets_axis.insert(l);
48     }
49     {
50         auto b = make_boolean_ganged_switch(right, "Left", "Right", fn_update);
51         auto l = grid_attach_label_and_widget(grid, "X direction", b, top);
52         l->set_width_chars(width_chars);
53         widgets_axis.insert(b);
54         widgets_axis.insert(l);
55     }
56     {
57         auto b = make_boolean_ganged_switch(clockwise, "CCW", "CW", fn_update);
58         auto l = grid_attach_label_and_widget(grid, "Direction", b, top);
59         l->set_width_chars(width_chars);
60         widgets_circular.insert(b);
61         widgets_circular.insert(l);
62     }
63     {
64         auto b = Gtk::manage(new Gtk::ComboBoxText);
65         const std::map<Origin, std::string> m = {
66                 {Origin::TOP_LEFT, "Top left"},
67                 {Origin::BOTTOM_LEFT, "Bottom left"},
68                 {Origin::BOTTOM_RIGHT, "Bottom right"},
69                 {Origin::TOP_RIGHT, "Top right"},
70         };
71         bind_widget<Origin>(b, m, circular_origin, [this](auto v) { this->renumber(); });
72         auto l = grid_attach_label_and_widget(grid, "Origin", b, top);
73         l->set_width_chars(width_chars);
74         widgets_circular.insert(b);
75         widgets_circular.insert(l);
76     }
77     {
78         entry_prefix = Gtk::manage(new Gtk::Entry);
79         entry_prefix->signal_changed().connect(sigc::mem_fun(*this, &RenumberPadsWindow::renumber));
80         entry_prefix->set_hexpand(true);
81         grid_attach_label_and_widget(grid, "Prefix", entry_prefix, top)->set_width_chars(width_chars);
82     }
83     {
84         sp_start = Gtk::manage(new Gtk::SpinButton);
85         sp_start->set_range(1, 1000);
86         sp_start->set_increments(1, 1);
87         sp_start->set_value(1);
88         sp_start->signal_value_changed().connect(sigc::mem_fun(*this, &RenumberPadsWindow::renumber));
89         grid_attach_label_and_widget(grid, "Start", sp_start, top)->set_width_chars(width_chars);
90     }
91     {
92         sp_step = Gtk::manage(new Gtk::SpinButton);
93         sp_step->set_range(1, 10);
94         sp_step->set_increments(1, 1);
95         sp_step->set_value(1);
96         sp_step->signal_value_changed().connect(sigc::mem_fun(*this, &RenumberPadsWindow::renumber));
97         grid_attach_label_and_widget(grid, "Step", sp_step, top)->set_width_chars(width_chars);
98     }
99     grid->show_all();
100     add(*grid);
101 
102     renumber();
103 }
104 
sort_pads(std::vector<Pad * > & pads,bool x,bool asc)105 static void sort_pads(std::vector<Pad *> &pads, bool x, bool asc)
106 {
107     std::stable_sort(pads.begin(), pads.end(), [x, asc](const Pad *a, const Pad *b) {
108         int64_t va, vb;
109         if (x) {
110             va = a->placement.shift.x;
111             vb = b->placement.shift.x;
112         }
113         else {
114             va = a->placement.shift.y;
115             vb = b->placement.shift.y;
116         }
117 
118         if (asc)
119             return va < vb;
120         else
121             return vb < va;
122     });
123 }
124 
get_angle(const Coordi & c)125 static double get_angle(const Coordi &c)
126 {
127     auto a = atan2(c.y, c.x);
128     if (a < 0)
129         return a + 2 * M_PI;
130     else
131         return a;
132 }
133 
add_angle(double a,double b)134 static double add_angle(double a, double b)
135 {
136     auto r = a + b;
137     while (r >= 2 * M_PI)
138         r -= 2 * M_PI;
139     while (r < 0)
140         r += 2 * M_PI;
141     return r;
142 }
143 
renumber()144 void RenumberPadsWindow::renumber()
145 {
146     for (auto w : widgets_circular) {
147         w->set_visible(circular);
148     }
149 
150     for (auto w : widgets_axis) {
151         w->set_visible(!circular);
152     }
153 
154     pads_sorted.clear();
155     std::copy(pads.begin(), pads.end(), std::back_inserter(pads_sorted));
156     if (!circular) {
157         if (x_first) {
158             sort_pads(pads_sorted, true, right);
159             sort_pads(pads_sorted, false, !down);
160         }
161         else {
162             sort_pads(pads_sorted, false, !down);
163             sort_pads(pads_sorted, true, right);
164         }
165     }
166     else {
167         Coordi a = pads_sorted.front()->placement.shift;
168         Coordi b = a;
169         for (const auto &it : pads_sorted) {
170             a = Coordi::min(a, it->placement.shift);
171             b = Coordi::max(b, it->placement.shift);
172         }
173         const Coordi center = (a + b) / 2;
174         const Coordi ar = a - center;
175         const Coordi br = b - center;
176 
177         double angle_offset = 0;
178         switch (circular_origin) {
179         case Origin::TOP_LEFT:
180             angle_offset = get_angle({ar.x, br.y});
181             break;
182 
183         case Origin::TOP_RIGHT:
184             angle_offset = get_angle({br.x, br.y});
185             break;
186 
187         case Origin::BOTTOM_LEFT:
188             angle_offset = get_angle({ar.x, ar.y});
189             break;
190 
191         case Origin::BOTTOM_RIGHT:
192             angle_offset = get_angle({br.x, ar.y});
193             break;
194         }
195 
196         // compensate for rounding errors
197         if (clockwise)
198             angle_offset += 1e-3;
199         else
200             angle_offset -= 1e-3;
201 
202         std::sort(pads_sorted.begin(), pads_sorted.end(), [this, center, angle_offset](const Pad *x, const Pad *y) {
203             auto alpha_a = add_angle(get_angle(x->placement.shift - center), -angle_offset);
204             auto alpha_b = add_angle(get_angle(y->placement.shift - center), -angle_offset);
205 
206             if (clockwise)
207                 return alpha_b < alpha_a;
208             else
209                 return alpha_a < alpha_b;
210         });
211     }
212     int n = sp_start->get_value_as_int();
213     for (auto &it : pads_sorted) {
214         it->name = entry_prefix->get_text() + std::to_string(n);
215         n += sp_step->get_value_as_int();
216     }
217     emit_event(ToolDataWindow::Event::UPDATE);
218 }
219 
get_pads_sorted()220 const std::vector<Pad *> &RenumberPadsWindow::get_pads_sorted()
221 {
222     return pads_sorted;
223 }
224 
225 
226 } // namespace horizon
227