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