1 #include "footprint_generator_dual.hpp"
2 #include "util/geom_util.hpp"
3 #include "widgets/pool_browser_button.hpp"
4 #include "document/idocument_package.hpp"
5 #include "pool/ipool.hpp"
6 #include "pool/package.hpp"
7
8 namespace horizon {
FootprintGeneratorDual(IDocumentPackage & c)9 FootprintGeneratorDual::FootprintGeneratorDual(IDocumentPackage &c)
10 : Glib::ObjectBase(typeid(FootprintGeneratorDual)),
11 FootprintGeneratorBase("/org/horizon-eda/horizon/imp/footprint_generator/dual.svg", c)
12 {
13
14 {
15 auto tbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4));
16 auto la = Gtk::manage(new Gtk::Label("Count:"));
17 tbox->pack_start(*la, false, false, 0);
18
19 sp_count = Gtk::manage(new Gtk::SpinButton());
20 sp_count->set_range(2, 512);
21 sp_count->set_increments(2, 2);
22 sp_count->signal_input().connect([this](double *v) {
23 auto txt = sp_count->get_text();
24 int64_t va = 0;
25 try {
26 va = MAX(round_multiple(std::stoi(txt), 2), 1);
27 *v = va;
28 }
29 catch (const std::exception &e) {
30 return false;
31 }
32 return true;
33 });
34 tbox->pack_start(*sp_count, false, false, 0);
35
36 box_top->pack_start(*tbox, false, false, 0);
37 }
38
39 update_preview();
40 sp_spacing = Gtk::manage(new SpinButtonDim());
41 sp_spacing->set_range(0, 100_mm);
42 sp_spacing->set_valign(Gtk::ALIGN_CENTER);
43 sp_spacing->set_halign(Gtk::ALIGN_START);
44 sp_spacing->set_value(3_mm);
45 sp_spacing_connections.push_back(
46 sp_spacing->signal_value_changed().connect([this] { update_spacing(Mode::SPACING); }));
47 overlay->add_at_sub(*sp_spacing, "#spacing");
48 sp_spacing->show();
49
50 sp_spacing_inner = Gtk::manage(new SpinButtonDim());
51 sp_spacing_inner->set_range(0, 100_mm);
52 sp_spacing_inner->set_valign(Gtk::ALIGN_CENTER);
53 sp_spacing_inner->set_halign(Gtk::ALIGN_START);
54 sp_spacing_connections.push_back(
55 sp_spacing_inner->signal_value_changed().connect([this] { update_spacing(Mode::SPACING_INNER); }));
56 overlay->add_at_sub(*sp_spacing_inner, "#spacing_inner");
57 sp_spacing_inner->show();
58
59 sp_spacing_outer = Gtk::manage(new SpinButtonDim());
60 sp_spacing_outer->set_range(0, 100_mm);
61 sp_spacing_outer->set_valign(Gtk::ALIGN_CENTER);
62 sp_spacing_outer->set_halign(Gtk::ALIGN_START);
63 sp_spacing_connections.push_back(
64 sp_spacing_outer->signal_value_changed().connect([this] { update_spacing(Mode::SPACING_OUTER); }));
65 overlay->add_at_sub(*sp_spacing_outer, "#spacing_outer");
66 sp_spacing_outer->show();
67
68 sp_pitch = Gtk::manage(new SpinButtonDim());
69 sp_pitch->set_range(0, 50_mm);
70 sp_pitch->set_valign(Gtk::ALIGN_CENTER);
71 sp_pitch->set_halign(Gtk::ALIGN_START);
72 sp_pitch->set_value(1_mm);
73 overlay->add_at_sub(*sp_pitch, "#pitch");
74 sp_pitch->show();
75
76 sp_pad_height = Gtk::manage(new SpinButtonDim());
77 sp_pad_height->set_range(0, 50_mm);
78 sp_pad_height->set_valign(Gtk::ALIGN_CENTER);
79 sp_pad_height->set_halign(Gtk::ALIGN_START);
80 sp_pad_height->set_value(2_mm);
81 sp_spacing_connections.push_back(
82 sp_pad_height->signal_value_changed().connect([this] { update_spacing(Mode::PAD_HEIGHT); }));
83 overlay->add_at_sub(*sp_pad_height, "#pad_height");
84 sp_pad_height->show();
85
86 sp_pad_width = Gtk::manage(new SpinButtonDim());
87 sp_pad_width->set_range(0, 50_mm);
88 sp_pad_width->set_valign(Gtk::ALIGN_CENTER);
89 sp_pad_width->set_halign(Gtk::ALIGN_START);
90 sp_pad_width->set_value(.5_mm);
91 overlay->add_at_sub(*sp_pad_width, "#pad_width");
92 sp_pad_width->show();
93
94 update_spacing(Mode::PAD_HEIGHT);
95
96 sp_count->signal_value_changed().connect([this] {
97 pad_count = sp_count->get_value_as_int();
98 update_preview();
99 });
100
101 {
102 auto tbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4));
103 auto la = Gtk::manage(new Gtk::Label("Mode:"));
104 tbox->pack_start(*la, false, false, 0);
105
106 auto rbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
107 rbox->get_style_context()->add_class("linked");
108 auto rb1 = Gtk::manage(new Gtk::RadioButton("Circular"));
109 rb1->set_mode(false);
110 auto rb2 = Gtk::manage(new Gtk::RadioButton("Zigzag"));
111 rb2->signal_toggled().connect([this, rb2] {
112 zigzag = rb2->get_active();
113 update_preview();
114 });
115 // sp_count->signal_changed().connect([this]{set_pad_count(sp_count->get_value_as_int());});
116 rb2->set_mode(false);
117 rb2->join_group(*rb1);
118 rbox->pack_start(*rb1, false, false, 0);
119 rbox->pack_start(*rb2, false, false, 0);
120 rb1->set_active(true);
121
122 tbox->pack_start(*rbox, false, false, 0);
123
124 box_top->pack_start(*tbox, false, false, 0);
125 }
126 sp_count->set_value(4);
127 }
128
generate()129 bool FootprintGeneratorDual::generate()
130 {
131 if (!property_can_generate())
132 return false;
133 int64_t spacing = sp_spacing->get_value_as_int();
134 int64_t pitch = sp_pitch->get_value_as_int();
135 int64_t y0 = (pad_count / 2 - 1) * (pitch / 2);
136 int64_t pad_width = sp_pad_width->get_value_as_int();
137 int64_t pad_height = sp_pad_height->get_value_as_int();
138 auto padstack = core.get_pool().get_padstack(browser_button->property_selected_uuid());
139 for (auto it : {-1, 1}) {
140 for (unsigned int i = 0; i < pad_count / 2; i++) {
141 auto uu = UUID::random();
142
143 auto &pad = package.pads.emplace(uu, Pad(uu, padstack)).first->second;
144 pad.placement.shift = {it * spacing, y0 - pitch * i};
145 update_pad_parameters(*padstack, pad, pad_width, pad_height);
146 if (it < 0)
147 pad.placement.set_angle_deg(270);
148 else
149 pad.placement.set_angle_deg(90);
150 if (!zigzag) {
151 if (it < 0) {
152 pad.name = std::to_string(i + 1);
153 }
154 else {
155 pad.name = std::to_string(pad_count - i);
156 }
157 }
158 else {
159 if (it < 0) {
160 pad.name = std::to_string(i * 2 + 1);
161 }
162 else {
163 pad.name = std::to_string(i * 2 + 2);
164 }
165 }
166 }
167 }
168 return true;
169 }
170
update_preview()171 void FootprintGeneratorDual::update_preview()
172 {
173 auto n = pad_count;
174 n &= ~1;
175 if (n < 2)
176 return;
177 if (!zigzag) {
178 if (n >= 8) {
179 overlay->sub_texts["#pad1"] = "1";
180 overlay->sub_texts["#pad2"] = "2";
181 overlay->sub_texts["#pad3"] = std::to_string(n / 2 - 1);
182 overlay->sub_texts["#pad4"] = std::to_string(n / 2);
183 overlay->sub_texts["#pad5"] = std::to_string(n / 2 + 1);
184 overlay->sub_texts["#pad6"] = std::to_string(n / 2 + 2);
185 overlay->sub_texts["#pad7"] = std::to_string(n - 1);
186 overlay->sub_texts["#pad8"] = std::to_string(n);
187 }
188 else if (n == 6) {
189 overlay->sub_texts["#pad1"] = "1";
190 overlay->sub_texts["#pad2"] = "2";
191 overlay->sub_texts["#pad3"] = "3";
192 overlay->sub_texts["#pad4"] = "X";
193 overlay->sub_texts["#pad5"] = "X";
194 overlay->sub_texts["#pad6"] = "4";
195 overlay->sub_texts["#pad7"] = "5";
196 overlay->sub_texts["#pad8"] = "6";
197 }
198 else if (n == 4) {
199 overlay->sub_texts["#pad1"] = "1";
200 overlay->sub_texts["#pad2"] = "2";
201 overlay->sub_texts["#pad3"] = "X";
202 overlay->sub_texts["#pad4"] = "X";
203 overlay->sub_texts["#pad5"] = "X";
204 overlay->sub_texts["#pad6"] = "X";
205 overlay->sub_texts["#pad7"] = "3";
206 overlay->sub_texts["#pad8"] = "4";
207 }
208 else if (n == 2) {
209 overlay->sub_texts["#pad1"] = "1";
210 overlay->sub_texts["#pad2"] = "X";
211 overlay->sub_texts["#pad3"] = "X";
212 overlay->sub_texts["#pad4"] = "X";
213 overlay->sub_texts["#pad5"] = "X";
214 overlay->sub_texts["#pad6"] = "X";
215 overlay->sub_texts["#pad7"] = "X";
216 overlay->sub_texts["#pad8"] = "2";
217 }
218 }
219 else {
220 if (n >= 8) {
221 overlay->sub_texts["#pad1"] = "1";
222 overlay->sub_texts["#pad8"] = "2";
223 overlay->sub_texts["#pad2"] = "3";
224 overlay->sub_texts["#pad7"] = "4";
225 overlay->sub_texts["#pad5"] = std::to_string(n);
226 overlay->sub_texts["#pad4"] = std::to_string(n - 1);
227 overlay->sub_texts["#pad6"] = std::to_string(n - 2);
228 overlay->sub_texts["#pad3"] = std::to_string(n - 3);
229 }
230 else if (n == 6) {
231 overlay->sub_texts["#pad1"] = "1";
232 overlay->sub_texts["#pad8"] = "2";
233 overlay->sub_texts["#pad2"] = "3";
234 overlay->sub_texts["#pad7"] = "4";
235 overlay->sub_texts["#pad5"] = "X";
236 overlay->sub_texts["#pad4"] = "X";
237 overlay->sub_texts["#pad6"] = "6";
238 overlay->sub_texts["#pad3"] = "5";
239 }
240 else if (n == 4) {
241 overlay->sub_texts["#pad1"] = "1";
242 overlay->sub_texts["#pad8"] = "2";
243 overlay->sub_texts["#pad2"] = "3";
244 overlay->sub_texts["#pad7"] = "4";
245 overlay->sub_texts["#pad5"] = "X";
246 overlay->sub_texts["#pad4"] = "X";
247 overlay->sub_texts["#pad6"] = "X";
248 overlay->sub_texts["#pad3"] = "X";
249 }
250 else if (n == 2) {
251 overlay->sub_texts["#pad1"] = "1";
252 overlay->sub_texts["#pad8"] = "2";
253 overlay->sub_texts["#pad2"] = "X";
254 overlay->sub_texts["#pad7"] = "X";
255 overlay->sub_texts["#pad5"] = "X";
256 overlay->sub_texts["#pad4"] = "X";
257 overlay->sub_texts["#pad6"] = "X";
258 overlay->sub_texts["#pad3"] = "X";
259 }
260 }
261 overlay->queue_draw();
262 }
263
update_spacing(Mode mode)264 void FootprintGeneratorDual::update_spacing(Mode mode)
265 {
266 for (auto &it : sp_spacing_connections) {
267 it.block();
268 }
269 int64_t spacing = sp_spacing->get_value_as_int();
270 int64_t spacing_inner = sp_spacing_inner->get_value_as_int();
271 int64_t spacing_outer = sp_spacing_outer->get_value_as_int();
272 int64_t pad_height = sp_pad_height->get_value_as_int();
273
274 switch (mode) {
275 case Mode::SPACING:
276 case Mode::PAD_HEIGHT:
277 sp_spacing_inner->set_value(spacing * 2 - pad_height / 2);
278 sp_spacing_outer->set_value(spacing * 2 + pad_height / 2);
279 break;
280
281 case Mode::SPACING_INNER:
282 case Mode::SPACING_OUTER:
283 sp_pad_height->set_value((spacing_outer - spacing_inner) / 2);
284 sp_spacing->set_value((spacing_outer + spacing_inner) / 4);
285 break;
286 }
287
288 for (auto &it : sp_spacing_connections) {
289 it.unblock();
290 }
291 }
292
293 } // namespace horizon
294