12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424 |
- /*
- * inst.c - Instance structures
- *
- * Written 2009-2012, 2015 by Werner Almesberger
- * Copyright 2009-2012, 2015 by Werner Almesberger
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <math.h>
- #include "util.h"
- #include "error.h"
- #include "coord.h"
- #include "expr.h"
- #include "layer.h"
- #include "obj.h"
- #include "delete.h"
- #include "gui_util.h"
- #include "gui_status.h"
- #include "gui_canvas.h"
- #include "gui_tool.h"
- #include "gui_meas.h"
- #include "gui_inst.h"
- #include "gui_frame.h"
- #include "gui.h"
- #include "inst.h"
- struct inst *selected_inst = NULL;
- struct bbox active_frame_bbox;
- struct pkg *pkgs, *active_pkg, *curr_pkg;
- struct pkg *reachable_pkg = NULL;
- struct inst *frame_instantiating = NULL;
- static struct pkg *prev_pkgs, *prev_reachable_pkg;
- static unsigned long active_set = 0;
- static struct inst_ops vec_ops;
- static struct inst_ops frame_ops;
- static struct inst_ops meas_ops;
- #define IS_ACTIVE ((active_set & 1))
- /* ----- selective visibility ---------------------------------------------- */
- static int show(enum inst_prio prio)
- {
- switch (prio) {
- case ip_vec:
- case ip_frame:
- return show_stuff;
- case ip_meas:
- return show_meas;
- default:
- return 1;
- }
- }
- int bright(const struct inst *inst)
- {
- if (!show_bright)
- return 0;
- return inst->ops != &vec_ops && inst->ops != &frame_ops &&
- inst->ops != &meas_ops;
- }
- static int show_this(const struct inst *inst)
- {
- if (show_all)
- return 1;
- if (inst->ops == &frame_ops && inst->u.frame.ref == active_frame)
- return 1;
- if (!inst->outer)
- return active_frame == frames;
- return inst->outer->u.frame.ref == active_frame;
- }
- /* ----- selection of items not on the canvas ------------------------------ */
- static void *selected_outside = NULL;
- static void (*outside_deselect)(void *item);
- static void deselect_outside(void)
- {
- if (selected_outside && outside_deselect)
- outside_deselect(selected_outside);
- selected_outside = NULL;
- }
- void inst_select_outside(void *item, void (*deselect)(void *item))
- {
- if (item == selected_outside)
- return;
- deselect_outside();
- inst_deselect();
- selected_outside = item;
- outside_deselect = deselect;
- }
- /* ----- check connectedness ----------------------------------------------- */
- /*
- * After an instantiation failure, the instances can get out of sync with the
- * object tree, and attempts to select an item on the canvas can cause accesses
- * to objects that aren't there anymore. So we need to check if we can still
- * reach the corresponding object.
- *
- * Note: even this isn't bullet-proof. Theoretically, we may get a new object
- * in the old place. However, this probably doesn't do any serious damage.
- */
- static int inst_connected(const struct inst *inst)
- {
- const struct frame *frame;
- const struct vec *vec;
- const struct obj *obj;
- for (frame = frames; frame; frame = frame->next) {
- if (inst->ops == &vec_ops) {
- for (vec = frame->vecs; vec; vec = vec->next)
- if (vec == inst->vec)
- return 1;
- } else {
- for (obj = frame->objs; obj; obj = obj->next)
- if (obj == inst->obj)
- return 1;
- }
- }
- return 0;
- }
- /* ----- selection --------------------------------------------------------- */
- static void inst_select_inst(struct inst *inst)
- {
- selected_inst = inst;
- tool_selected_inst(inst);
- gui_frame_select_inst(inst);
- if (inst->ops->select)
- selected_inst->ops->select(inst);
- status_set_icon(get_icon_by_inst(inst));
- }
- /*
- * @@@ This logic is overly complicated and should be simplified. The general
- * idea was to avoid making unnecessary changes to the user's selections, but
- * that risk doesn't exist. Furthermore, the way activate_item is used, its
- * preconditions aren't met. It works anyway but it could be simpler as a
- * consequence.
- *
- * activate_item tries to activate the path through the frame references,
- * leading to a specific instance. It returns whether this is failed or whether
- * it may have been successful.
- *
- * The initial condition is that we want to activate an item on a frame
- * instance that's not active. Since the frame has been instantiated, there
- * must be a way to activate it. We just have to find out how.
- *
- * The first test eliminates the root frame. If we're at the root frame and
- * still haven't figured out what to do, something is wrong and we give up.
- *
- * The next test skips references that are already right. Since we know that
- * there must be at least one reference that leads elsewhere, and we haven't
- * found it yet, the recursion will tell us whether it can find it at all.
- *
- * Finally, if we've found a mismatch, we correct it. We then try to fix any
- * further mismatches. Since we've made progress, we return 1, even if the
- * other fixes should fail (or reach the root frame).
- *
- */
- static int activate_item(struct inst *inst)
- {
- if (!inst->outer)
- return 0;
- if (inst->outer->u.frame.ref->active_ref == inst->outer->obj)
- return activate_item(inst->outer);
- inst->outer->u.frame.ref->active_ref = inst->outer->obj;
- activate_item(inst->outer);
- return 1;
- }
- static int __inst_select(struct coord pos, int tries)
- {
- enum inst_prio prio;
- const struct inst *prev;
- struct inst *inst;
- struct inst *first = NULL; /* first active item */
- struct inst *next = NULL; /* active item after currently sel. */
- struct inst *any_first = NULL; /* first item, active or inactive */
- struct inst *any_same_frame = NULL; /* first item on active frame */
- struct frame *frame;
- int best_dist = 0; /* keep gcc happy */
- int select_next;
- int dist, i;
- if (!tries) {
- fprintf(stderr, "__inst_select: tries exhausted\n");
- return 0;
- }
- prev = selected_inst;
- deselect_outside();
- edit_nothing();
- if (selected_inst) {
- gui_frame_deselect_inst(selected_inst);
- tool_selected_inst(NULL);
- }
- inst_deselect();
- select_next = 0;
- FOR_INST_PRIOS_DOWN(prio) {
- if (!show(prio))
- continue;
- FOR_ALL_INSTS(i, prio, inst) {
- if (!show_this(inst))
- continue;
- if (!inst->ops->distance)
- continue;
- if (!inst_connected(inst))
- continue;
- dist = inst->ops->distance(inst, pos, draw_ctx.scale);
- if (dist >= 0) {
- if (!any_first)
- any_first = inst;
- if (!any_same_frame && inst->outer &&
- inst->outer->u.frame.ref == active_frame)
- any_same_frame = inst;
- if (!inst->active)
- continue;
- if (!first)
- first = inst;
- if (!next && select_next)
- next = inst;
- if (inst == prev)
- select_next = 1;
- if (!selected_inst || best_dist > dist) {
- selected_inst = inst;
- best_dist = dist;
- }
- }
- }
- }
- if (select_next) {
- selected_inst = next ? next : first;
- goto selected;
- }
- if (selected_inst)
- goto selected;
- /* give vectors a second chance */
- if (show_stuff) {
- FOR_ALL_INSTS(i, ip_vec, inst) {
- if (!inst->active)
- continue;
- if (!inst_connected(inst))
- continue;
- dist = gui_dist_vec_fallback(inst, pos, draw_ctx.scale);
- if (dist >= 0 && (!selected_inst || best_dist > dist)) {
- selected_inst = inst;
- best_dist = dist;
- }
- }
- if (selected_inst)
- goto selected;
- }
- if (!show_all)
- return 0;
- if (any_same_frame) {
- activate_item(any_same_frame);
- search_inst(any_same_frame);
- instantiate();
- change_world();
- return __inst_select(pos, tries-1);
- }
- if (any_first) {
- frame = any_first->outer ? any_first->outer->u.frame.ref : NULL;
- if (frame != active_frame) {
- select_frame(frame);
- return __inst_select(pos, tries-1);
- }
- }
- return 0;
- selected:
- inst_select_inst(selected_inst);
- return 1;
- }
- int inst_select(struct coord pos)
- {
- /*
- * We shouldn't need more than 2 tries to select any item, so 5 is more
- * than enough. This can still fail, but then it would for any number
- * of tries.
- */
- return __inst_select(pos, 5);
- }
- struct inst *inst_find_point(struct coord pos)
- {
- struct inst *inst, *found;
- int best_dist = 0; /* keep gcc happy */
- int dist, i;
- found = NULL;
- FOR_ALL_INSTS(i, ip_frame, inst) {
- if (!inst->u.frame.active)
- continue;
- dist = gui_dist_frame_eye(inst, pos, draw_ctx.scale);
- if (dist >= 0 && (!found || best_dist > dist)) {
- found = inst;
- best_dist = dist;
- }
- }
- if (found)
- return found;
- FOR_ALL_INSTS(i, ip_vec, inst) {
- if (!inst->active || !inst->ops->distance)
- continue;
- dist = inst->ops->distance(inst, pos, draw_ctx.scale);
- if (dist >= 0 && (!found || best_dist > dist)) {
- found = inst;
- best_dist = dist;
- }
- }
- return found;
- }
- int inst_find_point_selected(struct coord pos, struct inst **res)
- {
- struct vec **anchors[3];
- int n, best_i, i;
- struct inst *best = NULL;
- struct inst *inst;
- int d_min, d, j;
- assert(selected_inst);
- n = inst_anchors(selected_inst, anchors);
- for (i = 0; i != n; i++) {
- if (*anchors[i]) {
- FOR_ALL_INSTS(j, ip_vec, inst) {
- if (inst->vec != *anchors[i])
- continue;
- d = gui_dist_vec(inst, pos, draw_ctx.scale);
- if (d != -1 && (!best || d < d_min)) {
- best = inst;
- best_i = i;
- d_min = d;
- }
- }
- } else {
- FOR_ALL_INSTS(j, ip_frame, inst) {
- if (inst != selected_inst->outer)
- continue;
- d = gui_dist_frame(inst, pos, draw_ctx.scale);
- if (d != -1 && (!best || d < d_min)) {
- best = inst;
- best_i = i;
- d_min = d;
- }
- }
- }
- }
- if (!best)
- return -1;
- if (res)
- *res = best;
- return best_i;
- }
- struct coord inst_get_point(const struct inst *inst)
- {
- if (inst->ops == &vec_ops)
- return inst->u.vec.end;
- if (inst->ops == &frame_ops)
- return inst->base;
- abort();
- }
- struct vec *inst_get_vec(const struct inst *inst)
- {
- if (inst->ops == &vec_ops)
- return inst->vec;
- if (inst->ops == &frame_ops)
- return NULL;
- abort();
- }
- int inst_anchors(struct inst *inst, struct vec ***anchors)
- {
- if (inst->vec) {
- anchors[0] = &inst->vec->base;
- return 1;
- }
- return obj_anchors(inst->obj, anchors);
- }
- void inst_deselect(void)
- {
- if (selected_inst) {
- tool_selected_inst(NULL);
- gui_frame_deselect_inst(selected_inst);
- }
- deselect_outside();
- status_set_type_x(NULL, "");
- status_set_type_y(NULL, "");
- status_set_type_entry(NULL, "");
- status_set_name(NULL, "");
- status_set_x(NULL, "");
- status_set_y(NULL, "");
- status_set_r(NULL, "");
- status_set_angle(NULL, "");
- selected_inst = NULL;
- edit_nothing();
- refresh_pos();
- status_set_icon(NULL);
- }
- /* ----- select instance by vector/object ---------------------------------- */
- static void vec_edit(struct vec *vec);
- static void obj_edit(struct obj *obj);
- void inst_select_vec(struct vec *vec)
- {
- struct inst *inst;
- int i;
- if (vec->frame != active_frame)
- select_frame(vec->frame);
- FOR_ALL_INSTS(i, ip_vec, inst)
- if (inst->vec == vec && inst->active) {
- inst_deselect();
- inst_select_inst(inst);
- return;
- }
- vec_edit(vec);
- }
- void inst_select_obj(struct obj *obj)
- {
- enum inst_prio prio;
- struct inst *inst;
- int i;
- if (obj->frame != active_frame)
- select_frame(obj->frame);
- FOR_INST_PRIOS_DOWN(prio)
- FOR_ALL_INSTS(i, prio, inst)
- if (inst->obj && inst->obj == obj && inst->active)
- goto found;
- obj_edit(obj);
- return;
- found:
- inst_deselect();
- inst_select_inst(inst);
- }
- /* ----- common status reporting ------------------------------------------- */
- static void rect_status(struct coord a, struct coord b, unit_type width,
- int rounded)
- {
- const char *tip;
- struct coord d = sub_vec(b, a);
- double r;
- unit_type diag;
- status_set_xy(d);
- tip = "Angle of diagonal";
- if (!d.x && !d.y) {
- status_set_angle(tip, "a = 0 deg");
- } else {
- status_set_angle(tip, "a = %3.1f deg", theta(a, b));
- }
- if (d.x < 0)
- d.x = -d.x;
- if (d.y < 0)
- d.y = -d.y;
- diag = hypot(d.x, d.y);
- if (rounded) {
- /*
- * Only consider the part of the diagonal that is on the pad
- * surface.
- *
- * The circle: (x-r)^2+(y-r)^2 = r^2
- * The diagonal: x = t*cos(theta), y = t*sin(theta)
- *
- * t is the distance from the corner of the surrounding
- * rectangle to the half-circle:
- *
- * t = 2*r*(s+c-sqrt(2*s*c))
- *
- * With s = sin(theta) and c = cos(theta).
- *
- * Since d.x = diag*cos(theta), we don't need to calculate the
- * sinus and cosinus but can use d.x and d.y directly.
- */
- r = (d.x > d.y ? d.y : d.x)/2;
- diag -= 2*r*(d.x+d.y-sqrt(2*d.x*d.y))/diag;
- }
- set_with_units(status_set_r, "d = ", diag, "Length of diagonal");
- if (width != -1) {
- status_set_type_entry(NULL, "width =");
- set_with_units(status_set_name, "", width, "Line width");
- }
- }
- static void rect_status_sort(struct coord a, struct coord b, unit_type width,
- int rounded)
- {
- sort_coord(&a, &b);
- rect_status(a, b, width, rounded);
- }
- /* ----- helper functions for instance creation ---------------------------- */
- static void update_bbox(struct bbox *bbox, struct coord coord)
- {
- if (bbox->min.x > coord.x)
- bbox->min.x = coord.x;
- if (bbox->max.x < coord.x)
- bbox->max.x = coord.x;
- if (bbox->min.y > coord.y)
- bbox->min.y = coord.y;
- if (bbox->max.y < coord.y)
- bbox->max.y = coord.y;
- }
- static void propagate_bbox(const struct inst *inst)
- {
- struct inst *frame = frame_instantiating ?
- frame_instantiating : curr_pkg->insts[ip_frame];
- update_bbox(&frame->bbox, inst->bbox.min);
- update_bbox(&frame->bbox, inst->bbox.max);
- if (curr_pkg->bbox.min.x || curr_pkg->bbox.min.y ||
- curr_pkg->bbox.max.x || curr_pkg->bbox.max.y) {
- update_bbox(&curr_pkg->bbox, inst->bbox.min);
- update_bbox(&curr_pkg->bbox, inst->bbox.max);
- } else {
- curr_pkg->bbox = inst->bbox;
- }
- }
- static void grow_bbox_by_width(struct bbox *bbox, unit_type width)
- {
- bbox->min.x -= width/2;
- bbox->min.y -= width/2;
- bbox->max.x += width/2;
- bbox->max.y += width/2;
- }
- static int zero_sized(struct coord a, struct coord b, const char *fmt,
- const char *arg)
- {
- if (a.x == b.x && a.y == b.y) {
- fail(fmt, "zero-sized", arg);
- return 1;
- }
- if (a.x == b.x) {
- fail(fmt, "zero-width", arg);
- return 1;
- }
- if (a.y == b.y) {
- fail(fmt, "zero-height", arg);
- return 1;
- }
- return 0;
- }
- static struct inst *add_inst(const struct inst_ops *ops, enum inst_prio prio,
- struct coord base)
- {
- struct inst *inst;
- inst = alloc_type(struct inst);
- inst->ops = ops;
- inst->prio = prio;
- inst->vec = NULL;
- inst->obj = NULL;
- inst->base = inst->bbox.min = inst->bbox.max = base;
- inst->outer = frame_instantiating;
- inst->active = IS_ACTIVE;
- inst->next = NULL;
- *curr_pkg->next_inst[prio] = inst;
- curr_pkg->next_inst[prio] = &inst->next;
- return inst;
- }
- /* ----- vec --------------------------------------------------------------- */
- static int validate_vec_name(const char *s, void *ctx)
- {
- struct vec *vec = ctx;
- const struct vec *walk;
- if (!is_id(s))
- return 0;
- for (walk = vec->frame->vecs; walk; walk = walk->next)
- if (walk->name && !strcmp(walk->name, s))
- return 0;
- return 1;
- }
- static void vec_edit(struct vec *vec)
- {
- edit_x(&vec->x, "X distance");
- edit_y(&vec->y, "Y distance");
- edit_unique_null(&vec->name, validate_vec_name, vec, "Vector name");
- }
- static void vec_op_select(struct inst *self)
- {
- status_set_type_entry(NULL, "ref =");
- status_set_name("Vector reference (name)",
- "%s", self->vec->name ? self->vec->name : "");
- rect_status(self->base, self->u.vec.end, -1, 0);
- vec_edit(self->vec);
- }
- /*
- * @@@ The logic of gui_find_point_vec isn't great. Instead of selecting a
- * point and then filtering, we should filter the candidates, so that a point
- * that's close end eligible can win against one that's closer but not
- * eligible.
- */
- static struct inst *find_point_vec(struct inst *self, struct coord pos)
- {
- struct inst *inst;
- const struct vec *vec;
- inst = inst_find_point(pos);
- if (!inst)
- return NULL;
- if (inst->ops == &frame_ops)
- return inst;
- for (vec = inst->vec; vec; vec = vec->base)
- if (vec == self->vec)
- return NULL;
- return inst;
- }
- /*
- * When instantiating and when dumping, we assume that bases appear in the
- * frame->vecs list before vectors using them. A move may change this order.
- * We therefore have to sort the list after the move.
- *
- * Since the list is already ordered, cleaning it up is just O(n).
- */
- static void do_move_to_vec(struct inst *inst, struct inst *to, int i)
- {
- struct vec *to_vec = inst_get_vec(to);
- struct vec *vec = inst->vec;
- struct frame *frame = vec->frame;
- struct vec *v, **anchor, **walk;
- assert(!i);
- vec->base = to_vec;
- /*
- * Mark the vector that's being rebased and all vectors that
- * (recursively) depend on it.
- *
- * We're mainly interested in the range between the vector being moved
- * and the new base. If the vector follows the base, the list is
- * already in the correct order and nothing needs moving.
- */
- for (v = frame->vecs; v != vec; v = v->next)
- v->mark = 0;
- vec->mark = 1;
- for (v = vec->next; v && v != to_vec; v = v->next)
- v->mark = v->base ? v->base->mark : 0;
- if (!v)
- return;
- /*
- * All the marked vectors appearing on the list before the new base
- * are moved after the new base, preserving their order.
- *
- * Start at frame->vecs, not "vec", so that we move the the vector
- * being rebased as well.
- */
- anchor = &to_vec->next;
- walk = &frame->vecs;
- while (*walk != to_vec) {
- v = *walk;
- if (!v->mark) {
- walk = &v->next;
- } else {
- *walk = v->next;
- v->next = *anchor;
- *anchor = v;
- anchor = &v->next;
- }
- }
- }
- static struct inst_ops vec_ops = {
- .draw = gui_draw_vec,
- .hover = gui_hover_vec,
- .distance = gui_dist_vec,
- .select = vec_op_select,
- .find_point = find_point_vec,
- .draw_move = draw_move_vec,
- .do_move_to = do_move_to_vec,
- };
- int inst_vec(struct vec *vec, struct coord base)
- {
- struct inst *inst;
- inst = add_inst(&vec_ops, ip_vec, base);
- inst->vec = vec;
- inst->u.vec.end = vec->pos;
- find_inst(inst);
- update_bbox(&inst->bbox, vec->pos);
- propagate_bbox(inst);
- return 1;
- }
- /* ----- line -------------------------------------------------------------- */
- static void obj_line_edit(struct obj *obj)
- {
- edit_dist_expr(&obj->u.line.width, "Line width");
- }
- static void line_op_select(struct inst *self)
- {
- rect_status_sort(self->base, self->u.rect.end, self->u.rect.width, 0);
- obj_line_edit(self->obj);
- }
- static struct inst_ops line_ops = {
- .draw = gui_draw_line,
- .distance = gui_dist_line,
- .select = line_op_select,
- .draw_move = draw_move_line,
- };
- int inst_line(struct obj *obj, struct coord a, struct coord b, unit_type width)
- {
- struct inst *inst;
- inst = add_inst(&line_ops, ip_line, a);
- inst->obj = obj;
- inst->u.rect.end = b;
- inst->u.rect.width = width;
- find_inst(inst);
- update_bbox(&inst->bbox, b);
- grow_bbox_by_width(&inst->bbox, width);
- propagate_bbox(inst);
- return 1;
- }
- /* ----- rect -------------------------------------------------------------- */
- static void obj_rect_edit(struct obj *obj)
- {
- edit_dist_expr(&obj->u.rect.width, "Line width");
- }
- static void rect_op_select(struct inst *self)
- {
- rect_status_sort(self->base, self->u.rect.end, self->u.rect.width, 0);
- obj_rect_edit(self->obj);
- }
- static struct inst_ops rect_ops = {
- .draw = gui_draw_rect,
- .distance = gui_dist_rect,
- .select = rect_op_select,
- .draw_move = draw_move_rect,
- };
- int inst_rect(struct obj *obj, struct coord a, struct coord b, unit_type width)
- {
- struct inst *inst;
- inst = add_inst(&rect_ops, ip_rect, a);
- inst->obj = obj;
- inst->u.rect.end = b;
- inst->u.rect.width = width;
- find_inst(inst);
- update_bbox(&inst->bbox, b);
- grow_bbox_by_width(&inst->bbox, width);
- propagate_bbox(inst);
- return 1;
- }
- /* ----- pad / rpad -------------------------------------------------------- */
- static int validate_pad_name(const char *s, void *ctx)
- {
- char *tmp;
- status_begin_reporting();
- tmp = expand(s, NULL);
- if (!tmp)
- return 0;
- free(tmp);
- return 1;
- }
- static void obj_pad_edit(struct obj *obj)
- {
- edit_pad_type(&obj->u.pad.type);
- edit_name(&obj->u.pad.name, validate_pad_name, NULL,
- "Pad name (template)");
- }
- static void pad_op_select(struct inst *self)
- {
- status_set_type_entry(NULL, "label =");
- status_set_name("Pad name (actual)", "%s", self->u.pad.name);
- rect_status_sort(self->base, self->u.pad.other, -1, 0);
- obj_pad_edit(self->obj);
- }
- static struct inst_ops pad_ops = {
- .draw = gui_draw_pad,
- .distance = gui_dist_pad,
- .select = pad_op_select,
- .draw_move = draw_move_pad,
- };
- static void rpad_op_select(struct inst *self)
- {
- status_set_type_entry(NULL, "label =");
- status_set_name("Pad name (actual)", "%s", self->u.pad.name);
- rect_status_sort(self->base, self->u.pad.other, -1, 1);
- obj_pad_edit(self->obj);
- }
- static struct inst_ops rpad_ops = {
- .draw = gui_draw_rpad,
- .distance = gui_dist_pad, /* @@@ */
- .select = rpad_op_select,
- .draw_move = draw_move_rpad,
- };
- int inst_pad(struct obj *obj, const char *name, struct coord a, struct coord b)
- {
- struct inst *inst;
- if (zero_sized(a, b, "%s pad \"%s\"", name))
- return 0;
- inst = add_inst(obj->u.pad.rounded ? &rpad_ops : &pad_ops,
- obj->u.pad.type == pt_normal || obj->u.pad.type == pt_bare ||
- obj->u.pad.type == pt_trace ?
- ip_pad_copper : ip_pad_special, a);
- inst->obj = obj;
- inst->u.pad.name = stralloc(name);
- inst->u.pad.other = b;
- inst->u.pad.layers = pad_type_to_layers(obj->u.pad.type);
- find_inst(inst);
- update_bbox(&inst->bbox, b);
- propagate_bbox(inst);
- return 1;
- }
- /* ----- hole -------------------------------------------------------------- */
- static void hole_op_select(struct inst *self)
- {
- rect_status_sort(self->base, self->u.hole.other, -1, 1);
- }
- static struct inst_ops hole_ops = {
- .draw = gui_draw_hole,
- .distance = gui_dist_hole,
- .select = hole_op_select,
- .draw_move = draw_move_hole,
- };
- int inst_hole(struct obj *obj, struct coord a, struct coord b)
- {
- struct inst *inst;
- if (zero_sized(a, b, "%s hole", NULL))
- return 0;
- inst = add_inst(&hole_ops, ip_hole, a);
- inst->obj = obj;
- inst->u.hole.other = b;
- inst->u.hole.layers = mech_hole_layers();
- find_inst(inst);
- update_bbox(&inst->bbox, b);
- propagate_bbox(inst);
- return 1;
- }
- /* ----- arc --------------------------------------------------------------- */
- static void obj_arc_edit(struct obj *obj)
- {
- edit_dist_expr(&obj->u.arc.width, "Line width");
- }
- static void arc_op_select(struct inst *self)
- {
- status_set_xy(self->base);
- status_set_angle("Angle", "a = %3.1f deg",
- self->u.arc.a1 == self->u.arc.a2 ? 360 :
- self->u.arc.a2-self->u.arc.a1);
- set_with_units(status_set_r, "r = ", self->u.arc.r, "Radius");
- status_set_type_entry(NULL, "width =");
- set_with_units(status_set_name, "", self->u.arc.width, "Line width");
- obj_arc_edit(self->obj);
- }
- static struct inst_ops arc_ops = {
- .draw = gui_draw_arc,
- .distance = gui_dist_arc,
- .select = arc_op_select,
- .draw_move = draw_move_arc,
- .do_move_to = do_move_to_arc,
- };
- int inst_arc(struct obj *obj, struct coord center, struct coord start,
- struct coord end, unit_type width)
- {
- struct inst *inst;
- double r, a1, a2;
- a1 = theta(center, start);
- a2 = theta(center, end);
- inst = add_inst(&arc_ops,
- fmod(a1, 360) == fmod(a2, 360) ? ip_circ : ip_arc, center);
- inst->obj = obj;
- r = hypot(start.x-center.x, start.y-center.y);
- inst->u.arc.r = r;
- inst->u.arc.a1 = a1;
- inst->u.arc.a2 = a2;
- inst->u.arc.width = width;
- inst->bbox.min.x = center.x-r;
- inst->bbox.max.x = center.x+r;
- inst->bbox.min.y = center.y-r;
- inst->bbox.max.y = center.y+r;
- find_inst(inst);
- grow_bbox_by_width(&inst->bbox, width);
- propagate_bbox(inst);
- return 1;
- }
- /* ----- measurement ------------------------------------------------------- */
- static void obj_meas_edit(struct obj *obj)
- {
- edit_dist_expr(&obj->u.meas.offset, "Measurement line offset");
- }
- static void meas_op_select(struct inst *self)
- {
- rect_status_sort(self->base, self->u.meas.end, -1, 0);
- status_set_type_entry(NULL, "offset =");
- set_with_units(status_set_name, "", self->u.meas.offset,
- "Measurement line offset");
- obj_meas_edit(self->obj);
- }
- static struct inst_ops meas_ops = {
- .draw = gui_draw_meas,
- .distance = gui_dist_meas,
- .select = meas_op_select,
- .begin_drag_move= begin_drag_move_meas,
- .find_point = find_point_meas_move,
- .draw_move = draw_move_meas,
- .end_drag_move = end_drag_move_meas,
- .do_move_to = do_move_to_meas,
- };
- struct inst *find_meas_hint(const struct obj *obj)
- {
- struct inst *inst;
- for (inst = curr_pkg->insts[ip_meas]; inst; inst = inst->next)
- if (inst->obj == obj)
- break;
- return inst;
- }
- int inst_meas(struct obj *obj, struct coord from, struct coord to)
- {
- struct inst *inst;
- struct coord a1, b1;
- inst = find_meas_hint(obj);
- assert(inst);
- inst->base = from;
- inst->u.meas.end = to;
- inst->u.meas.valid = 1;
- /* @@@ we still need to consider the text size as well */
- update_bbox(&inst->bbox, from);
- update_bbox(&inst->bbox, to);
- project_meas(inst, &a1, &b1);
- update_bbox(&inst->bbox, a1);
- update_bbox(&inst->bbox, b1);
- propagate_bbox(inst);
- return 1;
- }
- void inst_meas_hint(struct obj *obj, unit_type offset)
- {
- static const struct coord zero = { 0, 0 };
- struct inst *inst;
- inst = find_meas_hint(obj);
- if (inst)
- return;
- inst = add_inst(&meas_ops, ip_meas, zero);
- inst->obj = obj;
- inst->u.meas.offset = offset;
- inst->u.meas.valid = 0;
- inst->active = 1; /* measurements are always active */
- }
- /* ----- direct editing of objects ----------------------------------------- */
- static void obj_edit(struct obj *obj)
- {
- switch (obj->type) {
- case ot_frame:
- break;
- case ot_line:
- obj_line_edit(obj);
- break;
- case ot_rect:
- obj_rect_edit(obj);
- break;
- case ot_arc:
- obj_arc_edit(obj);
- break;
- case ot_pad:
- obj_pad_edit(obj);
- break;
- case ot_meas:
- obj_meas_edit(obj);
- break;
- default:
- abort();
- }
- }
- /* ----- active instance --------------------------------------------------- */
- void inst_begin_active(int active)
- {
- active_set = (active_set << 1) | active;
- }
- void inst_end_active(void)
- {
- active_set >>= 1;
- }
- /* ----- frame ------------------------------------------------------------- */
- static void frame_op_select(struct inst *self)
- {
- rect_status(self->bbox.min, self->bbox.max, -1, 0);
- status_set_type_entry(NULL, "name =");
- status_set_name("Frame name", "%s", self->u.frame.ref->name);
- }
- static struct inst_ops frame_ops = {
- .draw = gui_draw_frame,
- .hover = gui_hover_frame,
- .distance = gui_dist_frame,
- .select = frame_op_select,
- .draw_move = draw_move_frame,
- };
- void inst_begin_frame(struct obj *obj, struct frame *frame,
- struct coord base, int active, int is_active_frame)
- {
- struct inst *inst;
- inst = add_inst(&frame_ops, ip_frame, base);
- inst->obj = obj;
- inst->u.frame.ref = frame;
- inst->u.frame.active = is_active_frame;
- inst->active = active;
- find_inst(inst);
- frame_instantiating = inst;
- }
- void inst_end_frame(const struct frame *frame)
- {
- struct inst *inst = frame_instantiating;
- frame_instantiating = frame_instantiating->outer;
- if (frame_instantiating)
- propagate_bbox(inst);
- if (inst->u.frame.active && frame == active_frame)
- active_frame_bbox = inst->bbox;
- }
- /* ----- package ----------------------------------------------------------- */
- void inst_select_pkg(const char *name, int active)
- {
- struct pkg **pkg;
- enum inst_prio prio;
- name = name ? unique(name) : NULL;
- for (pkg = &pkgs; *pkg; pkg = &(*pkg)->next)
- if ((*pkg)->name == name)
- break;
- if (!*pkg) {
- *pkg = zalloc_type(struct pkg);
- (*pkg)->name = name;
- FOR_INST_PRIOS_UP(prio)
- (*pkg)->next_inst[prio] = &(*pkg)->insts[prio];
- (*pkg)->samples =
- zalloc_size(sizeof(struct sample *)*n_samples);
- (*pkg)->n_samples = n_samples;
- }
- curr_pkg = *pkg;
- if (active && name)
- reachable_pkg = curr_pkg;
- }
- /* ----- misc. ------------------------------------------------------------- */
- struct bbox inst_get_bbox(const struct pkg *pkg)
- {
- if (pkg)
- return pkg->bbox;
- else
- return pkgs->insts[ip_frame]->bbox;
- }
- static void cleanup_inst(enum inst_prio prio, const struct inst *inst)
- {
- switch (prio) {
- case ip_pad_copper:
- case ip_pad_special:
- free(inst->u.pad.name);
- break;
- default:
- break;
- }
- }
- static void free_pkgs(struct pkg *pkg)
- {
- enum inst_prio prio;
- struct pkg *next_pkg;
- struct inst *inst, *next;
- while (pkg) {
- next_pkg = pkg->next;
- FOR_INST_PRIOS_UP(prio)
- for (inst = pkg->insts[prio]; inst; inst = next) {
- next = inst->next;
- cleanup_inst(prio, inst);
- free(inst);
- }
- reset_samples(pkg->samples, pkg->n_samples);
- free(pkg->samples);
- free(pkg);
- pkg = next_pkg;
- }
- }
- void inst_start(void)
- {
- static struct bbox bbox_zero = { { 0, 0 }, { 0, 0 }};
- active_frame_bbox = bbox_zero;
- prev_pkgs = pkgs;
- prev_reachable_pkg = reachable_pkg;
- pkgs = NULL;
- reachable_pkg = NULL;
- inst_select_pkg(NULL, 0);
- curr_pkg = pkgs;
- frame_instantiating = NULL;
- }
- void inst_commit(void)
- {
- struct pkg *pkg;
- if (active_pkg) {
- for (pkg = pkgs; pkg && pkg->name != active_pkg->name;
- pkg = pkg->next);
- active_pkg = pkg;
- }
- if (!active_pkg)
- active_pkg = pkgs->next;
- free_pkgs(prev_pkgs);
- }
- void inst_revert(void)
- {
- free_pkgs(pkgs);
- pkgs = prev_pkgs;
- reachable_pkg = prev_reachable_pkg;
- }
- void inst_draw(void)
- {
- enum inst_prio prio;
- struct inst *inst;
- int i;
- FOR_INST_PRIOS_UP(prio)
- FOR_ALL_INSTS(i, prio, inst)
- if (show_this(inst))
- if (show(prio) && !inst->active &&
- inst->ops->draw)
- inst->ops->draw(inst);
- FOR_INST_PRIOS_UP(prio)
- FOR_ALL_INSTS(i, prio, inst)
- if (show(prio) && prio != ip_frame && inst->active &&
- inst != selected_inst && inst->ops->draw)
- inst->ops->draw(inst);
- if (show_stuff)
- FOR_ALL_INSTS(i, ip_frame, inst)
- if (inst->active && inst != selected_inst &&
- inst->ops->draw)
- inst->ops->draw(inst);
- if (selected_inst && selected_inst->ops->draw)
- selected_inst->ops->draw(selected_inst);
- }
- void inst_highlight_vecs(int (*pick)(struct inst *inst, void *user), void *user)
- {
- struct inst *inst;
- int i;
- FOR_ALL_INSTS(i, ip_vec, inst) {
- inst->u.vec.highlighted = pick(inst, user);
- if (inst->u.vec.highlighted)
- gui_highlight_vec(inst);
- }
- }
- struct inst *inst_find_vec(struct coord pos,
- int (*pick)(struct inst *inst, void *user), void *user)
- {
- struct inst *inst, *found;
- int best_dist = 0; /* keep gcc happy */
- int dist, i;
- found = NULL;
- FOR_ALL_INSTS(i, ip_vec, inst) {
- if (!inst->ops->distance)
- continue;
- dist = inst->ops->distance(inst, pos, draw_ctx.scale);
- if (dist < 0 || (found && best_dist <= dist))
- continue;
- if (!pick(inst, user))
- continue;
- found = inst;
- best_dist = dist;
- }
- return found;
- }
- struct inst *insts_ip_vec(void)
- {
- return active_pkg->insts[ip_vec];
- }
- struct pix_buf *inst_draw_move(struct inst *inst, struct coord pos, int i)
- {
- return inst->ops->draw_move(inst, pos, i);
- }
- int inst_do_move_to(struct inst *inst, struct inst *to, int i)
- {
- if (!inst->ops->do_move_to)
- return 0;
- inst->ops->do_move_to(inst, to, i);
- return 1;
- }
- struct pix_buf *inst_hover(struct inst *inst)
- {
- if (!inst->ops->hover)
- return NULL;
- return inst->ops->hover(inst);
- }
- void inst_begin_drag_move(struct inst *inst, int i)
- {
- if (inst->ops->begin_drag_move)
- inst->ops->begin_drag_move(inst, i);
- }
- void inst_delete(struct inst *inst)
- {
- if (inst->ops == &vec_ops)
- delete_vec(inst->vec);
- else
- delete_obj(inst->obj);
- }
|