Root/
Source at commit e047cc074dbb55a57c011636447ef75deaca22b7 created 13 years 11 months ago. By werner, Holes can now also be output as KiCad modules. | |
---|---|
1 | /* |
2 | * inst.c - Instance structures |
3 | * |
4 | * Written 2009, 2010 by Werner Almesberger |
5 | * Copyright 2009, 2010 by Werner Almesberger |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | */ |
12 | |
13 | |
14 | #include <stdlib.h> |
15 | #include <stdio.h> |
16 | #include <math.h> |
17 | |
18 | #include "util.h" |
19 | #include "coord.h" |
20 | #include "expr.h" |
21 | #include "layer.h" |
22 | #include "obj.h" |
23 | #include "delete.h" |
24 | #include "gui_util.h" |
25 | #include "gui_status.h" |
26 | #include "gui_canvas.h" |
27 | #include "gui_tool.h" |
28 | #include "gui_meas.h" |
29 | #include "gui_inst.h" |
30 | #include "gui_frame.h" |
31 | #include "gui.h" |
32 | #include "inst.h" |
33 | |
34 | |
35 | struct inst *selected_inst = NULL; |
36 | struct bbox active_frame_bbox; |
37 | struct pkg *pkgs, *active_pkg, *curr_pkg; |
38 | struct inst *curr_frame = NULL; |
39 | |
40 | static struct pkg *prev_pkgs; |
41 | |
42 | static unsigned long active_set = 0; |
43 | |
44 | static struct inst_ops vec_ops; |
45 | static struct inst_ops frame_ops; |
46 | static struct inst_ops meas_ops; |
47 | |
48 | |
49 | #define IS_ACTIVE ((active_set & 1)) |
50 | |
51 | |
52 | /* ----- selective visibility ---------------------------------------------- */ |
53 | |
54 | |
55 | static int show(enum inst_prio prio) |
56 | { |
57 | switch (prio) { |
58 | case ip_vec: |
59 | case ip_frame: |
60 | return show_stuff; |
61 | case ip_meas: |
62 | return show_meas; |
63 | default: |
64 | return 1; |
65 | } |
66 | } |
67 | |
68 | |
69 | int bright(const struct inst *inst) |
70 | { |
71 | if (!show_bright) |
72 | return 0; |
73 | return inst->ops != &vec_ops && inst->ops != &frame_ops && |
74 | inst->ops != &meas_ops; |
75 | } |
76 | |
77 | |
78 | static int show_this(const struct inst *inst) |
79 | { |
80 | if (show_all) |
81 | return 1; |
82 | if (inst->ops == &frame_ops && inst->u.frame.ref == active_frame) |
83 | return 1; |
84 | if (!inst->outer) |
85 | return active_frame == root_frame; |
86 | return inst->outer->u.frame.ref == active_frame; |
87 | } |
88 | |
89 | |
90 | /* ----- selection of items not on the canvas ------------------------------ */ |
91 | |
92 | |
93 | static void *selected_outside = NULL; |
94 | static void (*outside_deselect)(void *item); |
95 | |
96 | |
97 | static void deselect_outside(void) |
98 | { |
99 | if (selected_outside && outside_deselect) |
100 | outside_deselect(selected_outside); |
101 | selected_outside = NULL; |
102 | } |
103 | |
104 | |
105 | void inst_select_outside(void *item, void (*deselect)(void *item)) |
106 | { |
107 | if (item == selected_outside) |
108 | return; |
109 | deselect_outside(); |
110 | inst_deselect(); |
111 | selected_outside = item; |
112 | outside_deselect = deselect; |
113 | } |
114 | |
115 | |
116 | /* ----- check connectedness ----------------------------------------------- */ |
117 | |
118 | |
119 | /* |
120 | * After an instantiation failure, the instances can get out of sync with the |
121 | * object tree, and attempts to select an item on the canvas can cause accesses |
122 | * to objects that aren't there anymore. So we need to check if we can still |
123 | * reach the corresponding object. |
124 | * |
125 | * Note: even this isn't bullet-proof. Theoretically, we may get a new object |
126 | * in the old place. However, this probably doesn't do any serious damage. |
127 | */ |
128 | |
129 | |
130 | static int inst_connected(const struct inst *inst) |
131 | { |
132 | const struct frame *frame; |
133 | const struct vec *vec; |
134 | const struct obj *obj; |
135 | |
136 | for (frame = frames; frame; frame = frame->next) { |
137 | if (inst->ops == &vec_ops) { |
138 | for (vec = frame->vecs; vec; vec = vec->next) |
139 | if (vec == inst->vec) |
140 | return 1; |
141 | } else { |
142 | for (obj = frame->objs; obj; obj = obj->next) |
143 | if (obj == inst->obj) |
144 | return 1; |
145 | } |
146 | } |
147 | return 0; |
148 | } |
149 | |
150 | |
151 | /* ----- selection --------------------------------------------------------- */ |
152 | |
153 | |
154 | static void inst_select_inst(struct inst *inst) |
155 | { |
156 | selected_inst = inst; |
157 | tool_selected_inst(inst); |
158 | gui_frame_select_inst(inst); |
159 | if (inst->ops->select) |
160 | selected_inst->ops->select(inst); |
161 | status_set_icon(get_icon_by_inst(inst)); |
162 | } |
163 | |
164 | |
165 | /* |
166 | * @@@ This logic is overly complicated and should be simplified. The general |
167 | * idea was to avoid making unnecessary changes to the user's selections, but |
168 | * that risk doesn't exist. Furthermore, the way activate_item is used, its |
169 | * preconditions aren't met. It works anyway but it could be simpler as a |
170 | * consequence. |
171 | * |
172 | * activate_item tries to activate the path through the frame references, |
173 | * leading to a specific instance. It returns whether this is failed or whether |
174 | * it may have been successful. |
175 | * |
176 | * The initial condition is that we want to activate an item on a frame |
177 | * instance that's not active. Since the frame has been instantiated, there |
178 | * must be a way to activate it. We just have to find out how. |
179 | * |
180 | * The first test eliminates the root frame. If we're at the root frame and |
181 | * still haven't figured out what to do, something is wrong and we give up. |
182 | * |
183 | * The next test skips references that are already right. Since we know that |
184 | * there must be at least one reference that leads elsewhere, and we haven't |
185 | * found it yet, the recursion will tell us whether it can find it at all. |
186 | * |
187 | * Finally, if we've found a mismatch, we correct it. We then try to fix any |
188 | * further mismatches. Since we've made progress, we return 1, even if the |
189 | * other fixes should fail (or reach the root frame). |
190 | * |
191 | */ |
192 | |
193 | static int activate_item(struct inst *inst) |
194 | { |
195 | if (!inst->outer) |
196 | return 0; |
197 | if (inst->outer->u.frame.ref->active_ref == inst->outer->obj) |
198 | return activate_item(inst->outer); |
199 | inst->outer->u.frame.ref->active_ref = inst->outer->obj; |
200 | activate_item(inst->outer); |
201 | return 1; |
202 | } |
203 | |
204 | |
205 | static int __inst_select(struct coord pos, int tries) |
206 | { |
207 | enum inst_prio prio; |
208 | const struct inst *prev; |
209 | struct inst *inst; |
210 | struct inst *first = NULL; /* first active item */ |
211 | struct inst *next = NULL; /* active item after currently sel. */ |
212 | struct inst *any_first = NULL; /* first item, active or inactive */ |
213 | struct inst *any_same_frame = NULL; /* first item on active frame */ |
214 | struct frame *frame; |
215 | int best_dist = 0; /* keep gcc happy */ |
216 | int select_next; |
217 | int dist, i; |
218 | |
219 | if (!tries) { |
220 | fprintf(stderr, "__inst_select: tries exhausted\n"); |
221 | return 0; |
222 | } |
223 | prev = selected_inst; |
224 | deselect_outside(); |
225 | edit_nothing(); |
226 | if (selected_inst) { |
227 | gui_frame_deselect_inst(selected_inst); |
228 | tool_selected_inst(NULL); |
229 | } |
230 | inst_deselect(); |
231 | select_next = 0; |
232 | FOR_INST_PRIOS_DOWN(prio) { |
233 | if (!show(prio)) |
234 | continue; |
235 | FOR_ALL_INSTS(i, prio, inst) { |
236 | if (!show_this(inst)) |
237 | continue; |
238 | if (!inst->ops->distance) |
239 | continue; |
240 | if (!inst_connected(inst)) |
241 | continue; |
242 | dist = inst->ops->distance(inst, pos, draw_ctx.scale); |
243 | if (dist >= 0) { |
244 | if (!any_first) |
245 | any_first = inst; |
246 | if (!any_same_frame && inst->outer && |
247 | inst->outer->u.frame.ref == active_frame) |
248 | any_same_frame = inst; |
249 | if (!inst->active) |
250 | continue; |
251 | if (!first) |
252 | first = inst; |
253 | if (!next && select_next) |
254 | next = inst; |
255 | if (inst == prev) |
256 | select_next = 1; |
257 | if (!selected_inst || best_dist > dist) { |
258 | selected_inst = inst; |
259 | best_dist = dist; |
260 | } |
261 | } |
262 | } |
263 | } |
264 | if (select_next) { |
265 | selected_inst = next ? next : first; |
266 | goto selected; |
267 | } |
268 | if (selected_inst) |
269 | goto selected; |
270 | |
271 | /* give vectors a second chance */ |
272 | |
273 | if (show_stuff) { |
274 | FOR_ALL_INSTS(i, ip_vec, inst) { |
275 | if (!inst->active) |
276 | continue; |
277 | if (!inst_connected(inst)) |
278 | continue; |
279 | dist = gui_dist_vec_fallback(inst, pos, draw_ctx.scale); |
280 | if (dist >= 0 && (!selected_inst || best_dist > dist)) { |
281 | selected_inst = inst; |
282 | best_dist = dist; |
283 | } |
284 | } |
285 | |
286 | if (selected_inst) |
287 | goto selected; |
288 | } |
289 | |
290 | if (!show_all) |
291 | return 0; |
292 | |
293 | if (any_same_frame) { |
294 | activate_item(any_same_frame); |
295 | search_inst(any_same_frame); |
296 | instantiate(); |
297 | change_world(); |
298 | return __inst_select(pos, tries-1); |
299 | } |
300 | if (any_first) { |
301 | frame = any_first->outer ? any_first->outer->u.frame.ref : NULL; |
302 | if (frame != active_frame) { |
303 | select_frame(frame); |
304 | return __inst_select(pos, tries-1); |
305 | } |
306 | } |
307 | |
308 | return 0; |
309 | |
310 | selected: |
311 | inst_select_inst(selected_inst); |
312 | return 1; |
313 | } |
314 | |
315 | |
316 | int inst_select(struct coord pos) |
317 | { |
318 | /* |
319 | * We shouldn't need more than 2 tries to select any item, so 5 is more |
320 | * than enough. This can still fail, but then it would for any number |
321 | * of tries. |
322 | */ |
323 | return __inst_select(pos, 5); |
324 | } |
325 | |
326 | |
327 | struct inst *inst_find_point(struct coord pos) |
328 | { |
329 | struct inst *inst, *found; |
330 | int best_dist = 0; /* keep gcc happy */ |
331 | int dist, i; |
332 | |
333 | found = NULL; |
334 | FOR_ALL_INSTS(i, ip_frame, inst) { |
335 | if (!inst->u.frame.active) |
336 | continue; |
337 | dist = gui_dist_frame_eye(inst, pos, draw_ctx.scale); |
338 | if (dist >= 0 && (!found || best_dist > dist)) { |
339 | found = inst; |
340 | best_dist = dist; |
341 | } |
342 | } |
343 | if (found) |
344 | return found; |
345 | |
346 | FOR_ALL_INSTS(i, ip_vec, inst) { |
347 | if (!inst->active || !inst->ops->distance) |
348 | continue; |
349 | dist = inst->ops->distance(inst, pos, draw_ctx.scale); |
350 | if (dist >= 0 && (!found || best_dist > dist)) { |
351 | found = inst; |
352 | best_dist = dist; |
353 | } |
354 | } |
355 | return found; |
356 | } |
357 | |
358 | |
359 | int inst_find_point_selected(struct coord pos, struct inst **res) |
360 | { |
361 | struct vec **anchors[3]; |
362 | int n, best_i, i; |
363 | struct inst *best = NULL; |
364 | struct inst *inst; |
365 | int d_min, d, j; |
366 | |
367 | assert(selected_inst); |
368 | n = inst_anchors(selected_inst, anchors); |
369 | for (i = 0; i != n; i++) { |
370 | if (*anchors[i]) { |
371 | FOR_ALL_INSTS(j, ip_vec, inst) { |
372 | if (inst->vec != *anchors[i]) |
373 | continue; |
374 | d = gui_dist_vec(inst, pos, draw_ctx.scale); |
375 | if (d != -1 && (!best || d < d_min)) { |
376 | best = inst; |
377 | best_i = i; |
378 | d_min = d; |
379 | } |
380 | } |
381 | } else { |
382 | FOR_ALL_INSTS(j, ip_frame, inst) { |
383 | if (inst != selected_inst->outer) |
384 | continue; |
385 | d = gui_dist_frame(inst, pos, draw_ctx.scale); |
386 | if (d != -1 && (!best || d < d_min)) { |
387 | best = inst; |
388 | best_i = i; |
389 | d_min = d; |
390 | } |
391 | } |
392 | } |
393 | } |
394 | if (!best) |
395 | return -1; |
396 | if (res) |
397 | *res = best; |
398 | return best_i; |
399 | } |
400 | |
401 | |
402 | struct coord inst_get_point(const struct inst *inst) |
403 | { |
404 | if (inst->ops == &vec_ops) |
405 | return inst->u.vec.end; |
406 | if (inst->ops == &frame_ops) |
407 | return inst->base; |
408 | abort(); |
409 | } |
410 | |
411 | |
412 | struct vec *inst_get_vec(const struct inst *inst) |
413 | { |
414 | if (inst->ops == &vec_ops) |
415 | return inst->vec; |
416 | if (inst->ops == &frame_ops) |
417 | return NULL; |
418 | abort(); |
419 | } |
420 | |
421 | |
422 | int inst_anchors(struct inst *inst, struct vec ***anchors) |
423 | { |
424 | if (inst->vec) { |
425 | anchors[0] = &inst->vec->base; |
426 | return 1; |
427 | } |
428 | return obj_anchors(inst->obj, anchors); |
429 | } |
430 | |
431 | |
432 | void inst_deselect(void) |
433 | { |
434 | if (selected_inst) { |
435 | tool_selected_inst(NULL); |
436 | gui_frame_deselect_inst(selected_inst); |
437 | } |
438 | deselect_outside(); |
439 | status_set_type_x(NULL, ""); |
440 | status_set_type_y(NULL, ""); |
441 | status_set_type_entry(NULL, ""); |
442 | status_set_name(NULL, ""); |
443 | status_set_x(NULL, ""); |
444 | status_set_y(NULL, ""); |
445 | status_set_r(NULL, ""); |
446 | status_set_angle(NULL, ""); |
447 | selected_inst = NULL; |
448 | edit_nothing(); |
449 | refresh_pos(); |
450 | status_set_icon(NULL); |
451 | } |
452 | |
453 | |
454 | /* ----- select instance by vector/object ---------------------------------- */ |
455 | |
456 | |
457 | static void vec_edit(struct vec *vec); |
458 | static void obj_edit(struct obj *obj); |
459 | |
460 | |
461 | void inst_select_vec(struct vec *vec) |
462 | { |
463 | struct inst *inst; |
464 | int i; |
465 | |
466 | if (vec->frame != active_frame) |
467 | select_frame(vec->frame); |
468 | FOR_ALL_INSTS(i, ip_vec, inst) |
469 | if (inst->vec == vec && inst->active) { |
470 | inst_deselect(); |
471 | inst_select_inst(inst); |
472 | return; |
473 | } |
474 | vec_edit(vec); |
475 | } |
476 | |
477 | |
478 | void inst_select_obj(struct obj *obj) |
479 | { |
480 | enum inst_prio prio; |
481 | struct inst *inst; |
482 | int i; |
483 | |
484 | if (obj->frame != active_frame) |
485 | select_frame(obj->frame); |
486 | FOR_INST_PRIOS_DOWN(prio) |
487 | FOR_ALL_INSTS(i, prio, inst) |
488 | if (inst->obj && inst->obj == obj && inst->active) |
489 | goto found; |
490 | obj_edit(obj); |
491 | return; |
492 | |
493 | found: |
494 | inst_deselect(); |
495 | inst_select_inst(inst); |
496 | } |
497 | |
498 | |
499 | /* ----- common status reporting ------------------------------------------- */ |
500 | |
501 | |
502 | static void rect_status(struct coord a, struct coord b, unit_type width, |
503 | int rounded) |
504 | { |
505 | const char *tip; |
506 | struct coord d = sub_vec(b, a); |
507 | double r; |
508 | unit_type diag; |
509 | |
510 | status_set_xy(d); |
511 | tip = "Angle of diagonal"; |
512 | if (!d.x && !d.y) |
513 | status_set_angle(tip, "a = 0 deg"); |
514 | else { |
515 | status_set_angle(tip, "a = %3.1f deg", theta(a, b)); |
516 | } |
517 | if (d.x < 0) |
518 | d.x = -d.x; |
519 | if (d.y < 0) |
520 | d.y = -d.y; |
521 | diag = hypot(d.x, d.y); |
522 | if (rounded) { |
523 | /* |
524 | * Only consider the part of the diagonal that is on the pad |
525 | * surface. |
526 | * |
527 | * The circle: (x-r)^2+(y-r)^2 = r^2 |
528 | * The diagonal: x = t*cos(theta), y = t*sin(theta) |
529 | * |
530 | * t is the distance from the corner of the surrounding |
531 | * rectangle to the half-circle: |
532 | * |
533 | * t = 2*r*(s+c-sqrt(2*s*c)) |
534 | * |
535 | * With s = sin(theta) and c = cos(theta). |
536 | * |
537 | * Since d.x = diag*cos(theta), we don't need to calculate the |
538 | * sinus and cosinus but can use d.x and d.y directly. |
539 | */ |
540 | r = (d.x > d.y ? d.y : d.x)/2; |
541 | diag -= 2*r*(d.x+d.y-sqrt(2*d.x*d.y))/diag; |
542 | } |
543 | set_with_units(status_set_r, "d = ", diag, "Length of diagonal"); |
544 | if (width != -1) { |
545 | status_set_type_entry(NULL, "width ="); |
546 | set_with_units(status_set_name, "", width, "Line width"); |
547 | } |
548 | } |
549 | |
550 | |
551 | static void rect_status_sort(struct coord a, struct coord b, unit_type width, |
552 | int rounded) |
553 | { |
554 | sort_coord(&a, &b); |
555 | rect_status(a, b, width, rounded); |
556 | } |
557 | |
558 | |
559 | /* ----- helper functions for instance creation ---------------------------- */ |
560 | |
561 | |
562 | static void update_bbox(struct bbox *bbox, struct coord coord) |
563 | { |
564 | if (bbox->min.x > coord.x) |
565 | bbox->min.x = coord.x; |
566 | if (bbox->max.x < coord.x) |
567 | bbox->max.x = coord.x; |
568 | if (bbox->min.y > coord.y) |
569 | bbox->min.y = coord.y; |
570 | if (bbox->max.y < coord.y) |
571 | bbox->max.y = coord.y; |
572 | } |
573 | |
574 | |
575 | static void propagate_bbox(const struct inst *inst) |
576 | { |
577 | struct inst *frame = |
578 | curr_frame ? curr_frame : curr_pkg->insts[ip_frame]; |
579 | |
580 | update_bbox(&frame->bbox, inst->bbox.min); |
581 | update_bbox(&frame->bbox, inst->bbox.max); |
582 | } |
583 | |
584 | |
585 | static void grow_bbox_by_width(struct bbox *bbox, unit_type width) |
586 | { |
587 | bbox->min.x -= width/2; |
588 | bbox->min.y -= width/2; |
589 | bbox->max.x += width/2; |
590 | bbox->max.y += width/2; |
591 | } |
592 | |
593 | |
594 | static struct inst *add_inst(const struct inst_ops *ops, enum inst_prio prio, |
595 | struct coord base) |
596 | { |
597 | struct inst *inst; |
598 | |
599 | inst = alloc_type(struct inst); |
600 | inst->ops = ops; |
601 | inst->prio = prio; |
602 | inst->vec = NULL; |
603 | inst->obj = NULL; |
604 | inst->base = inst->bbox.min = inst->bbox.max = base; |
605 | inst->outer = curr_frame; |
606 | inst->active = IS_ACTIVE; |
607 | inst->next = NULL; |
608 | *curr_pkg->next_inst[prio] = inst; |
609 | curr_pkg->next_inst[prio] = &inst->next; |
610 | return inst; |
611 | } |
612 | |
613 | |
614 | /* ----- vec --------------------------------------------------------------- */ |
615 | |
616 | |
617 | static int validate_vec_name(const char *s, void *ctx) |
618 | { |
619 | struct vec *vec = ctx; |
620 | const struct vec *walk; |
621 | |
622 | if (!is_id(s)) |
623 | return 0; |
624 | for (walk = vec->frame->vecs; walk; walk = walk->next) |
625 | if (walk->name && !strcmp(walk->name, s)) |
626 | return 0; |
627 | return 1; |
628 | } |
629 | |
630 | |
631 | static void vec_edit(struct vec *vec) |
632 | { |
633 | edit_x(&vec->x, "X distance"); |
634 | edit_y(&vec->y, "Y distance"); |
635 | edit_unique_null(&vec->name, validate_vec_name, vec, "Vector name"); |
636 | } |
637 | |
638 | |
639 | static void vec_op_select(struct inst *self) |
640 | { |
641 | status_set_type_entry(NULL, "ref ="); |
642 | status_set_name("Vector reference (name)", |
643 | "%s", self->vec->name ? self->vec->name : ""); |
644 | rect_status(self->base, self->u.vec.end, -1, 0); |
645 | vec_edit(self->vec); |
646 | } |
647 | |
648 | |
649 | /* |
650 | * @@@ The logic of gui_find_point_vec isn't great. Instead of selecting a |
651 | * point and then filtering, we should filter the candidates, so that a point |
652 | * that's close end eligible can win against one that's closer but not |
653 | * eligible. |
654 | */ |
655 | |
656 | static struct inst *find_point_vec(struct inst *self, struct coord pos) |
657 | { |
658 | struct inst *inst; |
659 | const struct vec *vec; |
660 | |
661 | inst = inst_find_point(pos); |
662 | if (!inst) |
663 | return NULL; |
664 | if (inst->ops == &frame_ops) |
665 | return inst; |
666 | for (vec = inst->vec; vec; vec = vec->base) |
667 | if (vec == self->vec) |
668 | return NULL; |
669 | return inst; |
670 | } |
671 | |
672 | |
673 | /* |
674 | * When instantiating and when dumping, we assume that bases appear in the |
675 | * frame->vecs list before vectors using them. A move may change this order. |
676 | * We therefore have to sort the list after the move. |
677 | * |
678 | * Since the list is already ordered, cleaning it up is just O(n). |
679 | */ |
680 | |
681 | |
682 | static void do_move_to_vec(struct inst *inst, struct inst *to, int i) |
683 | { |
684 | struct vec *to_vec = inst_get_vec(to); |
685 | struct vec *vec = inst->vec; |
686 | struct frame *frame = vec->frame; |
687 | struct vec *v, **anchor, **walk; |
688 | |
689 | assert(!i); |
690 | vec->base = to_vec; |
691 | |
692 | /* |
693 | * Mark the vector that's being rebased and all vectors that |
694 | * (recursively) depend on it. |
695 | * |
696 | * We're mainly interested in the range between the vector being moved |
697 | * and the new base. If the vector follows the base, the list is |
698 | * already in the correct order and nothing needs moving. |
699 | */ |
700 | for (v = frame->vecs; v != vec; v = v->next) |
701 | v->mark = 0; |
702 | vec->mark = 1; |
703 | for (v = vec->next; v && v != to_vec; v = v->next) |
704 | v->mark = v->base ? v->base->mark : 0; |
705 | if (!v) |
706 | return; |
707 | |
708 | /* |
709 | * All the marked vectors appearing on the list before the new base |
710 | * are moved after the new base, preserving their order. |
711 | * |
712 | * Start at frame->vecs, not "vec", so that we move the the vector |
713 | * being rebased as well. |
714 | */ |
715 | anchor = &to_vec->next; |
716 | walk = &frame->vecs; |
717 | while (*walk != to_vec) { |
718 | v = *walk; |
719 | if (!v->mark) |
720 | walk = &v->next; |
721 | else { |
722 | *walk = v->next; |
723 | v->next = *anchor; |
724 | *anchor = v; |
725 | anchor = &v->next; |
726 | } |
727 | } |
728 | } |
729 | |
730 | |
731 | static struct inst_ops vec_ops = { |
732 | .draw = gui_draw_vec, |
733 | .hover = gui_hover_vec, |
734 | .distance = gui_dist_vec, |
735 | .select = vec_op_select, |
736 | .find_point = find_point_vec, |
737 | .draw_move = draw_move_vec, |
738 | .do_move_to = do_move_to_vec, |
739 | }; |
740 | |
741 | |
742 | int inst_vec(struct vec *vec, struct coord base) |
743 | { |
744 | struct inst *inst; |
745 | |
746 | inst = add_inst(&vec_ops, ip_vec, base); |
747 | inst->vec = vec; |
748 | inst->u.vec.end = vec->pos; |
749 | find_inst(inst); |
750 | update_bbox(&inst->bbox, vec->pos); |
751 | propagate_bbox(inst); |
752 | return 1; |
753 | } |
754 | |
755 | |
756 | /* ----- line -------------------------------------------------------------- */ |
757 | |
758 | |
759 | static void obj_line_edit(struct obj *obj) |
760 | { |
761 | edit_dist_expr(&obj->u.line.width, "Line width"); |
762 | } |
763 | |
764 | |
765 | static void line_op_select(struct inst *self) |
766 | { |
767 | rect_status_sort(self->base, self->u.rect.end, self->u.rect.width, 0); |
768 | obj_line_edit(self->obj); |
769 | } |
770 | |
771 | |
772 | static struct inst_ops line_ops = { |
773 | .draw = gui_draw_line, |
774 | .distance = gui_dist_line, |
775 | .select = line_op_select, |
776 | .draw_move = draw_move_line, |
777 | }; |
778 | |
779 | |
780 | int inst_line(struct obj *obj, struct coord a, struct coord b, unit_type width) |
781 | { |
782 | struct inst *inst; |
783 | |
784 | inst = add_inst(&line_ops, ip_line, a); |
785 | inst->obj = obj; |
786 | inst->u.rect.end = b; |
787 | inst->u.rect.width = width; |
788 | find_inst(inst); |
789 | update_bbox(&inst->bbox, b); |
790 | grow_bbox_by_width(&inst->bbox, width); |
791 | propagate_bbox(inst); |
792 | return 1; |
793 | } |
794 | |
795 | |
796 | /* ----- rect -------------------------------------------------------------- */ |
797 | |
798 | |
799 | static void obj_rect_edit(struct obj *obj) |
800 | { |
801 | edit_dist_expr(&obj->u.rect.width, "Line width"); |
802 | } |
803 | |
804 | |
805 | static void rect_op_select(struct inst *self) |
806 | { |
807 | rect_status_sort(self->base, self->u.rect.end, self->u.rect.width, 0); |
808 | obj_rect_edit(self->obj); |
809 | } |
810 | |
811 | |
812 | static struct inst_ops rect_ops = { |
813 | .draw = gui_draw_rect, |
814 | .distance = gui_dist_rect, |
815 | .select = rect_op_select, |
816 | .draw_move = draw_move_rect, |
817 | }; |
818 | |
819 | |
820 | int inst_rect(struct obj *obj, struct coord a, struct coord b, unit_type width) |
821 | { |
822 | struct inst *inst; |
823 | |
824 | inst = add_inst(&rect_ops, ip_rect, a); |
825 | inst->obj = obj; |
826 | inst->u.rect.end = b; |
827 | inst->u.rect.width = width; |
828 | find_inst(inst); |
829 | update_bbox(&inst->bbox, b); |
830 | grow_bbox_by_width(&inst->bbox, width); |
831 | propagate_bbox(inst); |
832 | return 1; |
833 | } |
834 | |
835 | |
836 | /* ----- pad / rpad -------------------------------------------------------- */ |
837 | |
838 | |
839 | static int validate_pad_name(const char *s, void *ctx) |
840 | { |
841 | char *tmp; |
842 | |
843 | status_begin_reporting(); |
844 | tmp = expand(s, NULL); |
845 | if (!tmp) |
846 | return 0; |
847 | free(tmp); |
848 | return 1; |
849 | } |
850 | |
851 | |
852 | static void obj_pad_edit(struct obj *obj) |
853 | { |
854 | edit_pad_type(&obj->u.pad.type); |
855 | edit_name(&obj->u.pad.name, validate_pad_name, NULL, |
856 | "Pad name (template)"); |
857 | } |
858 | |
859 | |
860 | static void pad_op_select(struct inst *self) |
861 | { |
862 | status_set_type_entry(NULL, "label ="); |
863 | status_set_name("Pad name (actual)", "%s", self->u.pad.name); |
864 | rect_status_sort(self->base, self->u.pad.other, -1, 0); |
865 | obj_pad_edit(self->obj); |
866 | } |
867 | |
868 | |
869 | static struct inst_ops pad_ops = { |
870 | .draw = gui_draw_pad, |
871 | .distance = gui_dist_pad, |
872 | .select = pad_op_select, |
873 | .draw_move = draw_move_pad, |
874 | }; |
875 | |
876 | |
877 | static void rpad_op_select(struct inst *self) |
878 | { |
879 | status_set_type_entry(NULL, "label ="); |
880 | status_set_name("Pad name (actual)", "%s", self->u.pad.name); |
881 | rect_status_sort(self->base, self->u.pad.other, -1, 1); |
882 | obj_pad_edit(self->obj); |
883 | } |
884 | |
885 | |
886 | static struct inst_ops rpad_ops = { |
887 | .draw = gui_draw_rpad, |
888 | .distance = gui_dist_pad, /* @@@ */ |
889 | .select = rpad_op_select, |
890 | .draw_move = draw_move_rpad, |
891 | }; |
892 | |
893 | |
894 | int inst_pad(struct obj *obj, const char *name, struct coord a, struct coord b) |
895 | { |
896 | struct inst *inst; |
897 | |
898 | inst = add_inst(obj->u.pad.rounded ? &rpad_ops : &pad_ops, |
899 | obj->u.pad.type == pt_normal || obj->u.pad.type == pt_bare ? |
900 | ip_pad_copper : ip_pad_special, a); |
901 | inst->obj = obj; |
902 | inst->u.pad.name = stralloc(name); |
903 | inst->u.pad.other = b; |
904 | inst->u.pad.layers = pad_type_to_layers(obj->u.pad.type); |
905 | find_inst(inst); |
906 | update_bbox(&inst->bbox, b); |
907 | propagate_bbox(inst); |
908 | return 1; |
909 | } |
910 | |
911 | |
912 | /* ----- hole -------------------------------------------------------------- */ |
913 | |
914 | |
915 | static void hole_op_select(struct inst *self) |
916 | { |
917 | rect_status_sort(self->base, self->u.hole.other, -1, 1); |
918 | } |
919 | |
920 | |
921 | static struct inst_ops hole_ops = { |
922 | .draw = gui_draw_hole, |
923 | .distance = gui_dist_hole, |
924 | .select = hole_op_select, |
925 | .draw_move = draw_move_hole, |
926 | }; |
927 | |
928 | |
929 | int inst_hole(struct obj *obj, struct coord a, struct coord b) |
930 | { |
931 | struct inst *inst; |
932 | |
933 | inst = add_inst(&hole_ops, ip_hole, a); |
934 | inst->obj = obj; |
935 | inst->u.hole.other = b; |
936 | inst->u.pad.layers = mech_hole_layers(); |
937 | find_inst(inst); |
938 | update_bbox(&inst->bbox, b); |
939 | propagate_bbox(inst); |
940 | return 1; |
941 | } |
942 | |
943 | |
944 | /* ----- arc --------------------------------------------------------------- */ |
945 | |
946 | |
947 | static void obj_arc_edit(struct obj *obj) |
948 | { |
949 | edit_dist_expr(&obj->u.arc.width, "Line width"); |
950 | } |
951 | |
952 | |
953 | static void arc_op_select(struct inst *self) |
954 | { |
955 | status_set_xy(self->base); |
956 | status_set_angle("Angle", "a = %3.1f deg", |
957 | self->u.arc.a1 == self->u.arc.a2 ? 360 : |
958 | self->u.arc.a2-self->u.arc.a1); |
959 | set_with_units(status_set_r, "r = ", self->u.arc.r, "Radius"); |
960 | status_set_type_entry(NULL, "width ="); |
961 | set_with_units(status_set_name, "", self->u.arc.width, "Line width"); |
962 | obj_arc_edit(self->obj); |
963 | } |
964 | |
965 | |
966 | static struct inst_ops arc_ops = { |
967 | .draw = gui_draw_arc, |
968 | .distance = gui_dist_arc, |
969 | .select = arc_op_select, |
970 | .draw_move = draw_move_arc, |
971 | .do_move_to = do_move_to_arc, |
972 | }; |
973 | |
974 | |
975 | int inst_arc(struct obj *obj, struct coord center, struct coord start, |
976 | struct coord end, unit_type width) |
977 | { |
978 | struct inst *inst; |
979 | double r, a1, a2; |
980 | |
981 | a1 = theta(center, start); |
982 | a2 = theta(center, end); |
983 | inst = add_inst(&arc_ops, |
984 | fmod(a1, 360) == fmod(a2, 360) ? ip_circ : ip_arc, center); |
985 | inst->obj = obj; |
986 | r = hypot(start.x-center.x, start.y-center.y); |
987 | inst->u.arc.r = r; |
988 | inst->u.arc.a1 = a1; |
989 | inst->u.arc.a2 = a2; |
990 | inst->u.arc.width = width; |
991 | inst->bbox.min.x = center.x-r; |
992 | inst->bbox.max.x = center.x+r; |
993 | inst->bbox.min.y = center.y-r; |
994 | inst->bbox.max.y = center.y+r; |
995 | find_inst(inst); |
996 | grow_bbox_by_width(&inst->bbox, width); |
997 | propagate_bbox(inst); |
998 | return 1; |
999 | } |
1000 | |
1001 | |
1002 | /* ----- measurement ------------------------------------------------------- */ |
1003 | |
1004 | |
1005 | static void obj_meas_edit(struct obj *obj) |
1006 | { |
1007 | edit_dist_expr(&obj->u.meas.offset, "Measurement line offset"); |
1008 | } |
1009 | |
1010 | |
1011 | static void meas_op_select(struct inst *self) |
1012 | { |
1013 | rect_status_sort(self->base, self->u.meas.end, -1, 0); |
1014 | status_set_type_entry(NULL, "offset ="); |
1015 | set_with_units(status_set_name, "", self->u.meas.offset, |
1016 | "Measurement line offset"); |
1017 | obj_meas_edit(self->obj); |
1018 | } |
1019 | |
1020 | |
1021 | static struct inst_ops meas_ops = { |
1022 | .draw = gui_draw_meas, |
1023 | .distance = gui_dist_meas, |
1024 | .select = meas_op_select, |
1025 | .begin_drag_move= begin_drag_move_meas, |
1026 | .find_point = find_point_meas_move, |
1027 | .draw_move = draw_move_meas, |
1028 | .end_drag_move = end_drag_move_meas, |
1029 | .do_move_to = do_move_to_meas, |
1030 | }; |
1031 | |
1032 | |
1033 | static struct inst *find_meas_hint(const struct obj *obj) |
1034 | { |
1035 | struct inst *inst; |
1036 | |
1037 | for (inst = curr_pkg->insts[ip_meas]; inst; inst = inst->next) |
1038 | if (inst->obj == obj) |
1039 | break; |
1040 | return inst; |
1041 | } |
1042 | |
1043 | |
1044 | int inst_meas(struct obj *obj, struct coord from, struct coord to) |
1045 | { |
1046 | struct inst *inst; |
1047 | struct coord a1, b1; |
1048 | |
1049 | inst = find_meas_hint(obj); |
1050 | assert(inst); |
1051 | inst->base = from; |
1052 | inst->u.meas.end = to; |
1053 | /* @@@ we still need to consider the text size as well */ |
1054 | update_bbox(&inst->bbox, from); |
1055 | update_bbox(&inst->bbox, to); |
1056 | project_meas(inst, &a1, &b1); |
1057 | update_bbox(&inst->bbox, a1); |
1058 | update_bbox(&inst->bbox, b1); |
1059 | propagate_bbox(inst); |
1060 | return 1; |
1061 | } |
1062 | |
1063 | |
1064 | void inst_meas_hint(struct obj *obj, unit_type offset) |
1065 | { |
1066 | static const struct coord zero = { 0, 0 }; |
1067 | struct inst *inst; |
1068 | |
1069 | inst = find_meas_hint(obj); |
1070 | if (inst) |
1071 | return; |
1072 | inst = add_inst(&meas_ops, ip_meas, zero); |
1073 | inst->obj = obj; |
1074 | inst->u.meas.offset = offset; |
1075 | inst->active = 1; /* measurements are always active */ |
1076 | } |
1077 | |
1078 | |
1079 | /* ----- direct editing of objects ----------------------------------------- */ |
1080 | |
1081 | |
1082 | static void obj_edit(struct obj *obj) |
1083 | { |
1084 | switch (obj->type) { |
1085 | case ot_frame: |
1086 | break; |
1087 | case ot_line: |
1088 | obj_line_edit(obj); |
1089 | break; |
1090 | case ot_rect: |
1091 | obj_rect_edit(obj); |
1092 | break; |
1093 | case ot_arc: |
1094 | obj_arc_edit(obj); |
1095 | break; |
1096 | case ot_pad: |
1097 | obj_pad_edit(obj); |
1098 | break; |
1099 | case ot_meas: |
1100 | obj_meas_edit(obj); |
1101 | break; |
1102 | default: |
1103 | abort(); |
1104 | } |
1105 | } |
1106 | |
1107 | |
1108 | /* ----- active instance --------------------------------------------------- */ |
1109 | |
1110 | |
1111 | void inst_begin_active(int active) |
1112 | { |
1113 | active_set = (active_set << 1) | active; |
1114 | } |
1115 | |
1116 | |
1117 | void inst_end_active(void) |
1118 | { |
1119 | active_set >>= 1; |
1120 | } |
1121 | |
1122 | |
1123 | /* ----- frame ------------------------------------------------------------- */ |
1124 | |
1125 | |
1126 | static void frame_op_select(struct inst *self) |
1127 | { |
1128 | rect_status(self->bbox.min, self->bbox.max, -1, 0); |
1129 | status_set_type_entry(NULL, "name ="); |
1130 | status_set_name("Frame name", "%s", self->u.frame.ref->name); |
1131 | } |
1132 | |
1133 | |
1134 | static struct inst_ops frame_ops = { |
1135 | .draw = gui_draw_frame, |
1136 | .hover = gui_hover_frame, |
1137 | .distance = gui_dist_frame, |
1138 | .select = frame_op_select, |
1139 | .draw_move = draw_move_frame, |
1140 | }; |
1141 | |
1142 | |
1143 | void inst_begin_frame(struct obj *obj, struct frame *frame, |
1144 | struct coord base, int active, int is_active_frame) |
1145 | { |
1146 | struct inst *inst; |
1147 | |
1148 | inst = add_inst(&frame_ops, ip_frame, base); |
1149 | inst->obj = obj; |
1150 | inst->u.frame.ref = frame; |
1151 | inst->u.frame.active = is_active_frame; |
1152 | inst->active = active; |
1153 | find_inst(inst); |
1154 | curr_frame = inst; |
1155 | } |
1156 | |
1157 | |
1158 | void inst_end_frame(const struct frame *frame) |
1159 | { |
1160 | struct inst *inst = curr_frame; |
1161 | |
1162 | curr_frame = curr_frame->outer; |
1163 | if (curr_frame) |
1164 | propagate_bbox(inst); |
1165 | if (inst->u.frame.active && frame == active_frame) |
1166 | active_frame_bbox = inst->bbox; |
1167 | } |
1168 | |
1169 | |
1170 | /* ----- package ----------------------------------------------------------- */ |
1171 | |
1172 | |
1173 | void inst_select_pkg(const char *name) |
1174 | { |
1175 | struct pkg **pkg; |
1176 | enum inst_prio prio; |
1177 | |
1178 | name = name ? unique(name) : NULL; |
1179 | for (pkg = &pkgs; *pkg; pkg = &(*pkg)->next) |
1180 | if ((*pkg)->name == name) |
1181 | break; |
1182 | if (!*pkg) { |
1183 | *pkg = zalloc_type(struct pkg); |
1184 | (*pkg)->name = name; |
1185 | FOR_INST_PRIOS_UP(prio) |
1186 | (*pkg)->next_inst[prio] = &(*pkg)->insts[prio]; |
1187 | (*pkg)->samples = |
1188 | zalloc_size(sizeof(struct sample *)*n_samples); |
1189 | (*pkg)->n_samples = n_samples; |
1190 | } |
1191 | curr_pkg = *pkg; |
1192 | } |
1193 | |
1194 | |
1195 | /* ----- misc. ------------------------------------------------------------- */ |
1196 | |
1197 | |
1198 | struct bbox inst_get_bbox(void) |
1199 | { |
1200 | return pkgs->insts[ip_frame]->bbox; |
1201 | } |
1202 | |
1203 | |
1204 | static void cleanup_inst(enum inst_prio prio, const struct inst *inst) |
1205 | { |
1206 | switch (prio) { |
1207 | case ip_pad_copper: |
1208 | case ip_pad_special: |
1209 | free(inst->u.pad.name); |
1210 | break; |
1211 | default: |
1212 | break; |
1213 | } |
1214 | } |
1215 | |
1216 | |
1217 | static void free_pkgs(struct pkg *pkg) |
1218 | { |
1219 | enum inst_prio prio; |
1220 | struct pkg *next_pkg; |
1221 | struct inst *inst, *next; |
1222 | |
1223 | while (pkg) { |
1224 | next_pkg = pkg->next; |
1225 | FOR_INST_PRIOS_UP(prio) |
1226 | for (inst = pkg->insts[prio]; inst; inst = next) { |
1227 | next = inst->next; |
1228 | cleanup_inst(prio, inst); |
1229 | free(inst); |
1230 | } |
1231 | reset_samples(pkg->samples, pkg->n_samples); |
1232 | free(pkg->samples); |
1233 | free(pkg); |
1234 | pkg = next_pkg; |
1235 | } |
1236 | } |
1237 | |
1238 | |
1239 | void inst_start(void) |
1240 | { |
1241 | static struct bbox bbox_zero = { { 0, 0 }, { 0, 0 }}; |
1242 | |
1243 | active_frame_bbox = bbox_zero; |
1244 | prev_pkgs = pkgs; |
1245 | pkgs = NULL; |
1246 | inst_select_pkg(NULL); |
1247 | curr_pkg = pkgs; |
1248 | curr_frame = NULL; |
1249 | } |
1250 | |
1251 | |
1252 | void inst_commit(void) |
1253 | { |
1254 | struct pkg *pkg; |
1255 | |
1256 | if (active_pkg) { |
1257 | for (pkg = pkgs; pkg && pkg->name != active_pkg->name; |
1258 | pkg = pkg->next); |
1259 | active_pkg = pkg; |
1260 | } |
1261 | if (!active_pkg) |
1262 | active_pkg = pkgs->next; |
1263 | free_pkgs(prev_pkgs); |
1264 | } |
1265 | |
1266 | |
1267 | void inst_revert(void) |
1268 | { |
1269 | free_pkgs(pkgs); |
1270 | pkgs = prev_pkgs; |
1271 | } |
1272 | |
1273 | |
1274 | void inst_draw(void) |
1275 | { |
1276 | enum inst_prio prio; |
1277 | struct inst *inst; |
1278 | int i; |
1279 | |
1280 | FOR_INST_PRIOS_UP(prio) |
1281 | FOR_ALL_INSTS(i, prio, inst) |
1282 | if (show_this(inst)) |
1283 | if (show(prio) && !inst->active && |
1284 | inst->ops->draw) |
1285 | inst->ops->draw(inst); |
1286 | FOR_INST_PRIOS_UP(prio) |
1287 | FOR_ALL_INSTS(i, prio, inst) |
1288 | if (show(prio) && prio != ip_frame && inst->active && |
1289 | inst != selected_inst && inst->ops->draw) |
1290 | inst->ops->draw(inst); |
1291 | if (show_stuff) |
1292 | FOR_ALL_INSTS(i, ip_frame, inst) |
1293 | if (inst->active && inst != selected_inst && |
1294 | inst->ops->draw) |
1295 | inst->ops->draw(inst); |
1296 | if (selected_inst && selected_inst->ops->draw) |
1297 | selected_inst->ops->draw(selected_inst); |
1298 | } |
1299 | |
1300 | |
1301 | void inst_highlight_vecs(int (*pick)(struct inst *inst, void *user), void *user) |
1302 | { |
1303 | struct inst *inst; |
1304 | int i; |
1305 | |
1306 | FOR_ALL_INSTS(i, ip_vec, inst) { |
1307 | inst->u.vec.highlighted = pick(inst, user); |
1308 | if (inst->u.vec.highlighted) |
1309 | gui_highlight_vec(inst); |
1310 | } |
1311 | } |
1312 | |
1313 | |
1314 | struct inst *inst_find_vec(struct coord pos, |
1315 | int (*pick)(struct inst *inst, void *user), void *user) |
1316 | { |
1317 | struct inst *inst, *found; |
1318 | int best_dist = 0; /* keep gcc happy */ |
1319 | int dist, i; |
1320 | |
1321 | found = NULL; |
1322 | FOR_ALL_INSTS(i, ip_vec, inst) { |
1323 | if (!inst->ops->distance) |
1324 | continue; |
1325 | dist = inst->ops->distance(inst, pos, draw_ctx.scale); |
1326 | if (dist < 0 || (found && best_dist <= dist)) |
1327 | continue; |
1328 | if (!pick(inst, user)) |
1329 | continue; |
1330 | found = inst; |
1331 | best_dist = dist; |
1332 | } |
1333 | return found; |
1334 | } |
1335 | |
1336 | |
1337 | struct inst *insts_ip_vec(void) |
1338 | { |
1339 | return active_pkg->insts[ip_vec]; |
1340 | } |
1341 | |
1342 | |
1343 | struct pix_buf *inst_draw_move(struct inst *inst, struct coord pos, int i) |
1344 | { |
1345 | return inst->ops->draw_move(inst, pos, i); |
1346 | } |
1347 | |
1348 | |
1349 | int inst_do_move_to(struct inst *inst, struct inst *to, int i) |
1350 | { |
1351 | if (!inst->ops->do_move_to) |
1352 | return 0; |
1353 | inst->ops->do_move_to(inst, to, i); |
1354 | return 1; |
1355 | } |
1356 | |
1357 | |
1358 | struct pix_buf *inst_hover(struct inst *inst) |
1359 | { |
1360 | if (!inst->ops->hover) |
1361 | return NULL; |
1362 | return inst->ops->hover(inst); |
1363 | } |
1364 | |
1365 | |
1366 | void inst_begin_drag_move(struct inst *inst, int i) |
1367 | { |
1368 | if (inst->ops->begin_drag_move) |
1369 | inst->ops->begin_drag_move(inst, i); |
1370 | } |
1371 | |
1372 | |
1373 | void inst_delete(struct inst *inst) |
1374 | { |
1375 | if (inst->ops == &vec_ops) |
1376 | delete_vec(inst->vec); |
1377 | else |
1378 | delete_obj(inst->obj); |
1379 | } |
1380 |
Branches:
master