Root/inst.c

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

Archive Download this file

Branches:
master



interactive