1 #include "canvas.hpp"
2 #include "board/board.hpp"
3 #include "common/layer_provider.hpp"
4 #include "schematic/sheet.hpp"
5 #include "frame/frame.hpp"
6 #include "pool/decal.hpp"
7 #include "util/util.hpp"
8 #include <algorithm>
9 #include <cmath> // for std::isnan()
10 #include "util/bbox_accumulator.hpp"
11 
12 namespace horizon {
13 
Canvas()14 Canvas::Canvas() : selectables(*this)
15 {
16     layer_display[10000] = LayerDisplay(true, LayerDisplay::Mode::FILL);
17     layer_display[0] = LayerDisplay(true, LayerDisplay::Mode::FILL);
18 }
19 
set_layer_display(int index,const LayerDisplay & ld)20 void Canvas::set_layer_display(int index, const LayerDisplay &ld)
21 {
22     layer_display[index] = ld;
23     request_push();
24 }
25 
26 static const LayerDisplay ld_default;
27 
get_layer_display(int index) const28 const LayerDisplay &Canvas::get_layer_display(int index) const
29 {
30     if (layer_display.count(index))
31         return layer_display.at(index);
32     else
33         return ld_default;
34 }
35 
layer_is_visible(int layer) const36 bool Canvas::layer_is_visible(int layer) const
37 {
38     return layer == work_layer || get_layer_display(layer).visible;
39 }
40 
layer_is_visible(LayerRange layer) const41 bool Canvas::layer_is_visible(LayerRange layer) const
42 {
43     if (!layer.is_multilayer())
44         return layer_is_visible(layer.start());
45     if (layer.overlaps(work_layer))
46         return true;
47     for (auto la : {layer.start(), layer.end()}) {
48         if (la == work_layer || get_layer_display(la).visible)
49             return true;
50     }
51     for (const auto &[la, ld] : layer_display) {
52         if (ld.visible && layer.overlaps(la))
53             return true;
54     }
55     return false;
56 }
57 
clear()58 void Canvas::clear()
59 {
60     selectables.clear();
61     for (auto &it : triangles) {
62         if (it.first < 20000 || it.first >= 30000)
63             it.second.clear();
64     }
65     targets.clear();
66     sheet_current_uuid = UUID();
67     for (auto &it : object_refs) {
68         it.second.clear();
69     }
70     object_refs_current.clear();
71     object_ref_idx.clear();
72     pictures.clear();
73 }
74 
remove_obj(const ObjectRef & r)75 void Canvas::remove_obj(const ObjectRef &r)
76 {
77     if (!object_refs.count(r))
78         return;
79     std::set<int> layers;
80     for (const auto &it : object_refs.at(r)) {
81         auto layer = it.first;
82         layers.insert(layer);
83         auto first = it.second.first;
84         auto last = it.second.second + 1;
85         assert(it.second.first < triangles[layer].size());
86         assert(it.second.second < triangles[layer].size());
87 
88         triangles.at(layer).erase(first, last);
89     }
90 
91     // fix refs that changed due to triangles being deleted
92     for (auto &it : object_refs) {
93         if (it.first != r) {
94             for (auto layer : layers) {
95                 if (it.second.count(layer)) {
96                     auto &idx = it.second.at(layer);
97                     auto idx_removed = object_refs.at(r).at(layer);
98                     auto n_removed = (idx_removed.second - idx_removed.first) + 1;
99                     if (idx.first > idx_removed.first) {
100                         idx.first -= n_removed;
101                         idx.second -= n_removed;
102                     }
103                 }
104             }
105         }
106     }
107 
108     object_refs.erase(r);
109 
110     request_push();
111 }
112 
set_flags(const ObjectRef & r,uint8_t mask_set,uint8_t mask_clear)113 void Canvas::set_flags(const ObjectRef &r, uint8_t mask_set, uint8_t mask_clear)
114 {
115     if (!object_refs.count(r))
116         return;
117     for (const auto &it : object_refs.at(r)) {
118         auto layer = it.first;
119         for (auto i = it.second.first; i <= it.second.second; i++) {
120             triangles.at(layer).atm(i).second.flags |= mask_set;
121             triangles.at(layer).atm(i).second.flags &= ~mask_clear;
122         }
123     }
124     request_push();
125 }
126 
set_flags_all(uint8_t mask_set,uint8_t mask_clear)127 void Canvas::set_flags_all(uint8_t mask_set, uint8_t mask_clear)
128 {
129     for (auto &it : triangles) {
130         for (auto it2 : it.second) {
131             it2.second.flags |= mask_set;
132             it2.second.flags &= ~mask_clear;
133         }
134     }
135     request_push();
136 }
137 
hide_obj(const ObjectRef & r)138 void Canvas::hide_obj(const ObjectRef &r)
139 {
140     set_flags(r, TriangleInfo::FLAG_HIDDEN, 0);
141 }
142 
show_obj(const ObjectRef & r)143 void Canvas::show_obj(const ObjectRef &r)
144 {
145     set_flags(r, 0, TriangleInfo::FLAG_HIDDEN);
146 }
147 
show_all_obj()148 void Canvas::show_all_obj()
149 {
150     set_flags_all(0, TriangleInfo::FLAG_HIDDEN);
151 }
152 
reset_color2()153 void Canvas::reset_color2()
154 {
155     for (auto &[layer, tris] : triangles) {
156         for (auto [tri, tri_info] : tris) {
157             tri.color2 = 0;
158         }
159     }
160     request_push();
161 }
162 
set_color2(const ObjectRef & r,uint8_t color)163 void Canvas::set_color2(const ObjectRef &r, uint8_t color)
164 {
165     if (!object_refs.count(r))
166         return;
167     for (const auto &[layer, range] : object_refs.at(r)) {
168         const auto [first, last] = range;
169         for (auto i = first; i <= last; i++) {
170             triangles.at(layer).atm(i).first.color2 = color;
171         }
172     }
173 }
174 
add_triangle(int layer,const Coordf & p0,const Coordf & p1,const Coordf & p2,ColorP color,uint8_t flags,uint8_t color2)175 void Canvas::add_triangle(int layer, const Coordf &p0, const Coordf &p1, const Coordf &p2, ColorP color, uint8_t flags,
176                           uint8_t color2)
177 {
178     if (group_tris) {
179         assert(group_layer == layer);
180         group_tris->emplace_back(std::forward_as_tuple(p0, p1, p2, color, lod_current, color2),
181                                  std::forward_as_tuple(triangle_type_current, flags));
182         return;
183     }
184     triangles[layer].emplace_back(std::forward_as_tuple(p0, p1, p2, color, lod_current, color2),
185                                   std::forward_as_tuple(triangle_type_current, flags));
186     auto idx = triangles[layer].size() - 1;
187     for (auto it : object_ref_idx) {
188         auto &layers = *it;
189         if (layers.count(layer)) {
190             auto &idxs = layers[layer];
191             assert(idxs.second == idx - 1);
192             idxs.second = idx;
193         }
194         else {
195             auto &idxs = layers[layer];
196             idxs.first = idx;
197             idxs.second = idx;
198         }
199     }
200 }
201 
update(const Symbol & sym,const Placement & tr,bool edit)202 void Canvas::update(const Symbol &sym, const Placement &tr, bool edit)
203 {
204     clear();
205     layer_provider = &sym;
206     transform = tr;
207     render(sym, !edit);
208     request_push();
209 }
210 
update(const Sheet & sheet)211 void Canvas::update(const Sheet &sheet)
212 {
213     clear();
214     layer_provider = &sheet;
215     sheet_current_uuid = sheet.uuid;
216     update_markers();
217     render(sheet);
218     request_push();
219 }
220 
update(const Padstack & padstack,bool edit)221 void Canvas::update(const Padstack &padstack, bool edit)
222 {
223     clear();
224     layer_provider = &padstack;
225     render(padstack, edit);
226     request_push();
227 }
228 
update(const Package & pkg,bool edit)229 void Canvas::update(const Package &pkg, bool edit)
230 {
231     clear();
232     layer_provider = &pkg;
233     render(pkg, edit);
234     request_push();
235 }
236 
update(const Board & brd,PanelMode mode)237 void Canvas::update(const Board &brd, PanelMode mode)
238 {
239     clear();
240     layer_provider = &brd;
241     render(brd, true, mode);
242     request_push();
243 }
update(const class Frame & fr,bool edit)244 void Canvas::update(const class Frame &fr, bool edit)
245 {
246     clear();
247     layer_provider = &fr;
248     render(fr, !edit);
249     request_push();
250 }
251 
update(const class Decal & dec,bool edit)252 void Canvas::update(const class Decal &dec, bool edit)
253 {
254     clear();
255     layer_provider = &dec;
256     render(dec, edit);
257     request_push();
258 }
259 
transform_save()260 void Canvas::transform_save()
261 {
262     transforms.push_back(transform);
263 }
264 
transform_restore()265 void Canvas::transform_restore()
266 {
267     if (transforms.size()) {
268         transform = transforms.back();
269         transforms.pop_back();
270     }
271 }
272 
get_overlay_layer(const LayerRange & layer,bool ignore_flip)273 int Canvas::get_overlay_layer(const LayerRange &layer, bool ignore_flip)
274 {
275     if (overlay_layers.count({layer, ignore_flip}) == 0) {
276         auto ol = overlay_layer_current++;
277         overlay_layers[{layer, ignore_flip}] = ol;
278         layer_display[ol].visible = true;
279         layer_display[ol].mode = LayerDisplay::Mode::OUTLINE;
280     }
281 
282     return overlay_layers.at({layer, ignore_flip});
283 }
284 
is_overlay_layer(int overlay_layer,int layer) const285 bool Canvas::is_overlay_layer(int overlay_layer, int layer) const
286 {
287     for (const auto &[k, v] : overlay_layers) {
288         if (k.first.overlaps(layer))
289             if (v == overlay_layer)
290                 return true;
291     }
292     return false;
293 }
294 
295 
get_layer_color(int layer) const296 Color Canvas::get_layer_color(int layer) const
297 {
298     if (layer_colors.count(layer))
299         return layer_colors.at(layer);
300     else
301         return {1, 1, 0};
302 }
303 
set_layer_color(int layer,const Color & color)304 void Canvas::set_layer_color(int layer, const Color &color)
305 {
306     layer_colors[layer] = color;
307 }
308 
object_ref_pop()309 void Canvas::object_ref_pop()
310 {
311     object_refs_current.pop_back();
312     object_ref_idx.pop_back();
313     assert(group_tris == nullptr);
314 }
315 
object_ref_push(const ObjectRef & ref)316 void Canvas::object_ref_push(const ObjectRef &ref)
317 {
318     object_refs_current.push_back(ref);
319     object_ref_idx.push_back(&object_refs[ref]);
320     assert(group_tris == nullptr);
321 }
322 
begin_group(int layer)323 void Canvas::begin_group(int layer)
324 {
325     group_layer = layer;
326     assert(group_tris == nullptr);
327     group_tris = &triangles[layer];
328     group_size = group_tris->size();
329 }
330 
end_group()331 void Canvas::end_group()
332 {
333     auto new_size = group_tris->size();
334     if (new_size != group_size) { // something was drawn
335         for (auto it : object_ref_idx) {
336             auto &layers = *it;
337             if (layers.count(group_layer)) {
338                 auto &idxs = layers[group_layer];
339                 idxs.second = new_size - 1;
340             }
341             else {
342                 auto &idxs = layers[group_layer];
343                 idxs.first = group_size;
344                 idxs.second = new_size - 1;
345             }
346         }
347     }
348     group_tris = nullptr;
349 }
350 
get_bbox(bool visible_only) const351 std::pair<Coordf, Coordf> Canvas::get_bbox(bool visible_only) const
352 {
353     BBoxAccumulator<Coordf::type> acc;
354     for (const auto &it : triangles) {
355         if (visible_only == false || get_layer_display(it.first).visible) {
356             for (const auto &[it2, it_info] : it.second) {
357                 if (it_info.flags & TriangleInfo::FLAG_GLYPH)
358                     continue;
359                 const std::vector<Coordf> points = {Coordf(it2.x0, it2.y0), Coordf(it2.x1, it2.y1),
360                                                     Coordf(it2.x2, it2.y2)};
361                 if (it_info.flags & TriangleInfo::FLAG_ARC) {
362                     const float r = it2.x2 + it2.y2 / 2;
363                     const auto rv = Coordf(r, r);
364                     acc.accumulate({points.at(0) - rv, points.at(0) + rv});
365                 }
366                 else if (std::isnan(it2.y1) && std::isnan(it2.x2) && std::isnan(it2.y2)) { // circle
367                     auto r = Coordf(it2.x1, it2.x1);
368                     acc.accumulate({points.at(0) - r, points.at(0) + r});
369                 }
370                 else if (std::isnan(it2.y2) && !(it_info.flags & TriangleInfo::FLAG_BUTT)) { // line
371                     float width = it2.x2;
372                     Coordf offset(width / 2, width / 2);
373                     acc.accumulate(points.at(0) - offset);
374                     acc.accumulate(points.at(1) - offset);
375                     acc.accumulate(points.at(0) + offset);
376                     acc.accumulate(points.at(1) + offset);
377                 }
378                 else if (it_info.flags & TriangleInfo::FLAG_BUTT) { // line
379                     const Coordf v = points.at(1) - points.at(0);
380                     const Coordf vn = Coordf(-v.y, v.x).normalize();
381                     const Coordf w = vn * (it2.x2 / 2);
382                     acc.accumulate(points.at(0) - w);
383                     acc.accumulate(points.at(1) - w);
384                     acc.accumulate(points.at(0) + w);
385                     acc.accumulate(points.at(1) + w);
386                 }
387                 else { // triangle
388                     for (const auto &p : points) {
389                         acc.accumulate(p);
390                     }
391                 }
392             }
393         }
394     }
395     const auto [a, b] = acc.get_or_0();
396     if ((b - a).mag() < .1_mm)
397         return std::make_pair(Coordf(-5_mm, -5_mm), Coordf(5_mm, 5_mm));
398     else
399         return std::make_pair(a, b);
400 }
401 } // namespace horizon
402