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