123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669 |
- /*
- * gui_inst.c - GUI, instance functions
- *
- * Written 2009-2012 by Werner Almesberger
- * Copyright 2009-2012 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 <math.h>
- #include <gtk/gtk.h>
- #include "util.h"
- #include "coord.h"
- #include "inst.h"
- #include "gui.h"
- #include "gui_util.h"
- #include "gui_style.h"
- #include "gui_status.h"
- #include "gui_inst.h"
- /* ----- coordinate translation -------------------------------------------- */
- struct coord translate(struct coord pos)
- {
- pos.x -= draw_ctx.center.x;
- pos.y -= draw_ctx.center.y;
- pos.x /= draw_ctx.scale;
- pos.y /= draw_ctx.scale;
- pos.y = -pos.y;
- pos.x += draw_ctx.widget->allocation.width/2;
- pos.y += draw_ctx.widget->allocation.height/2;
- return pos;
- }
- struct coord canvas_to_coord(int x, int y)
- {
- struct coord pos;
- x -= draw_ctx.widget->allocation.width/2;
- y -= draw_ctx.widget->allocation.height/2;
- y = -y;
- pos.x = x*draw_ctx.scale+draw_ctx.center.x;
- pos.y = y*draw_ctx.scale+draw_ctx.center.y;
- return pos;
- }
- /* ----- drawing primitives ------------------------------------------------ */
- static void draw_eye(GdkGC *gc, struct coord center, int r1, int r2)
- {
- draw_circle(DA, gc, TRUE, center.x, center.y, r1);
- draw_circle(DA, gc, FALSE, center.x, center.y, r2);
- }
- #define MAX_POINTS 10
- static void draw_poly(GdkGC *gc, int fill,
- const struct coord *points, int n_points)
- {
- GdkPoint gp[MAX_POINTS];
- int i;
- if (n_points > MAX_POINTS)
- abort();
- for (i = 0; i != n_points; i++) {
- gp[i].x = points[i].x;
- gp[i].y = points[i].y;
- }
- if (fill) {
- gdk_draw_polygon(DA, gc, fill, gp, n_points);
- } else {
- gdk_draw_line(DA, gc, gp[0].x, gp[0].y, gp[1].x, gp[1].y);
- gdk_draw_line(DA, gc, gp[1].x, gp[1].y, gp[2].x, gp[2].y);
- }
- }
- static void draw_arrow(GdkGC *gc, int fill,
- struct coord from, struct coord to, int len, double angle)
- {
- struct coord p[3];
- struct coord side;
- if (from.x == to.x && from.y == to.y) {
- side.x = 0;
- side.y = -len;
- } else {
- side = normalize(sub_vec(to, from), len);
- }
- p[0] = add_vec(to, rotate(side, 180-angle));
- p[1] = to;
- p[2] = add_vec(to, rotate(side, 180+angle));
- draw_poly(gc, fill, p, 3);
- }
- static enum mode get_mode(const struct inst *self)
- {
- if (selected_inst == self)
- return mode_selected;
- return self->active || bright(self) ? mode_active : mode_inactive;
- }
- /* ----- vec --------------------------------------------------------------- */
- unit_type gui_dist_vec(struct inst *self, struct coord pos, unit_type scale)
- {
- unit_type d;
- d = dist_point(pos, self->u.vec.end)/scale;
- return d > VEC_EYE_R ? -1 : d;
- }
- /*
- * The circles at a vector's tip enjoy the highest selection priority. However,
- * users will probably also expected a click on a nicely exposed stem to work.
- * So we give it second look after having exhausted all other options.
- */
- unit_type gui_dist_vec_fallback(struct inst *self, struct coord pos,
- unit_type scale)
- {
- unit_type d;
- d = dist_line(pos, self->base, self->u.vec.end)/scale;
- return d > SELECT_R ? -1 : d;
- }
- void gui_highlight_vec(struct inst *self)
- {
- struct coord center = translate(self->u.vec.end);
- draw_circle(DA, gc_highlight, FALSE, center.x, center.y, VEC_EYE_R);
- }
- void gui_draw_vec(struct inst *self)
- {
- struct coord from = translate(self->base);
- struct coord to = translate(self->u.vec.end);
- GdkGC *gc;
- gc = gc_vec[get_mode(self)];
- draw_arrow(gc, TRUE, from, to, VEC_ARROW_LEN, VEC_ARROW_ANGLE);
- gdk_draw_line(DA, gc, from.x, from.y, to.x, to.y);
- draw_circle(DA, gc, FALSE, to.x, to.y, VEC_EYE_R);
- }
- /* ----- line -------------------------------------------------------------- */
- unit_type gui_dist_line(struct inst *self, struct coord pos, unit_type scale)
- {
- unit_type r, d;
- r = self->u.rect.width/scale/2;
- if (r < SELECT_R)
- r = SELECT_R;
- d = dist_line(pos, self->base, self->u.rect.end)/scale;
- return d > r ? -1 : d;
- }
- void gui_draw_line(struct inst *self)
- {
- struct coord min = translate(self->base);
- struct coord max = translate(self->u.rect.end);
- GdkGC *gc;
- gc = gc_obj[get_mode(self)];
- set_width(gc, self->u.rect.width/draw_ctx.scale);
- gdk_draw_line(DA, gc, min.x, min.y, max.x, max.y);
- }
- /* ----- rect -------------------------------------------------------------- */
- unit_type gui_dist_rect(struct inst *self, struct coord pos, unit_type scale)
- {
- unit_type r, d;
- r = self->u.rect.width/scale/2;
- if (r < SELECT_R)
- r = SELECT_R;
- d = dist_rect(pos, self->base, self->u.rect.end)/scale;
- return d > r ? -1 : d;
- }
- void gui_draw_rect(struct inst *self)
- {
- struct coord min = translate(self->base);
- struct coord max = translate(self->u.rect.end);
- GdkGC *gc;
- sort_coord(&min, &max);
- gc = gc_obj[get_mode(self)];
- set_width(gc, self->u.rect.width/draw_ctx.scale);
- gdk_draw_rectangle(DA, gc, FALSE,
- min.x, min.y, max.x-min.x, max.y-min.y);
- }
- /* ----- pad --------------------------------------------------------------- */
- unit_type gui_dist_pad(struct inst *self, struct coord pos, unit_type scale)
- {
- unit_type d;
- if (inside_rect(pos, self->base, self->u.pad.other))
- return SELECT_R;
- d = dist_rect(pos, self->base, self->u.pad.other)/scale;
- return d > SELECT_R ? -1 : d;
- }
- static void pad_text_in_rect(struct inst *self,
- struct coord min, struct coord max)
- {
- GdkGC *gc;
- struct coord c;
- unit_type h, w;
- int rot;
- w = max.x-min.x;
- h = max.y-min.y;
- rot = w/1.1 < h;
- gc = gc_ptext[get_mode(self)];
- c = add_vec(min, max);
- h = max.y-min.y;
- w = max.x-min.x;
- render_text(DA, gc, c.x/2, c.y/2, rot ? 0 : 90,
- self->u.pad.name, PAD_FONT, 0.5, 0.5,
- w-2*PAD_BORDER, h-2*PAD_BORDER);
- }
- static void maximize_box(struct coord *min_box, struct coord *max_box,
- unit_type x_min, unit_type y_min, unit_type x_max, unit_type y_max)
- {
- unit_type d_box, d_new, d;
- d_box = max_box->x-min_box->x;
- d = max_box->y-min_box->y;
- if (d < d_box)
- d_box = d;
- d_new = x_max-x_min;
- d = y_max-y_min;
- if (d < d_new)
- d_new = d;
- if (d_new < d_box)
- return;
- min_box->x = x_min;
- min_box->y = y_min;
- max_box->x = x_max;
- max_box->y = y_max;
- }
- static void gui_draw_pad_text(struct inst *self)
- {
- struct coord pad_min = translate(self->base);
- struct coord pad_max = translate(self->u.pad.other);
- struct coord hole_min, hole_max;
- struct coord box_min, box_max;
- sort_coord(&pad_min, &pad_max);
- if (!self->u.pad.hole) {
- pad_text_in_rect(self, pad_min, pad_max);
- return;
- }
- hole_min = translate(self->u.pad.hole->base);
- hole_max = translate(self->u.pad.hole->u.hole.other);
- sort_coord(&hole_min, &hole_max);
- box_min.x = pad_min.x; /* top */
- box_min.y = pad_min.y;
- box_max.x = pad_max.x;
- box_max.y = hole_min.y;
- maximize_box(&box_min, &box_max,
- pad_min.x, hole_max.y, pad_max.x, pad_max.y); /* bottom */
- maximize_box(&box_min, &box_max,
- pad_min.x, pad_min.y, hole_min.x, pad_max.y); /* left */
- maximize_box(&box_min, &box_max,
- hole_max.x, pad_min.y, pad_max.x, pad_max.y); /* right */
- pad_text_in_rect(self, box_min, box_max);
- }
- static GdkGC *pad_gc(const struct inst *inst, int *fill)
- {
- *fill = TRUE;
- switch (layers_to_pad_type(inst->u.pad.layers)) {
- case pt_bare:
- return gc_pad_bare[get_mode(inst)];
- case pt_trace:
- return gc_pad_trace[get_mode(inst)];
- case pt_mask:
- *fill = FALSE;
- return gc_pad_mask[get_mode(inst)];
- default:
- return gc_pad[get_mode(inst)];
- }
- }
- void gui_draw_pad(struct inst *self)
- {
- struct coord min = translate(self->base);
- struct coord max = translate(self->u.pad.other);
- GdkGC *gc;
- int fill;
- gc = pad_gc(self, &fill);
- sort_coord(&min, &max);
- gdk_draw_rectangle(DA, gc, fill,
- min.x, min.y, max.x-min.x, max.y-min.y);
- gui_draw_pad_text(self);
- }
- static void draw_rounded_rect(GdkGC *gc, struct coord a, struct coord b,
- int fill)
- {
- struct coord min = translate(a);
- struct coord max = translate(b);
- unit_type h, w, r;
- sort_coord(&min, &max);
- h = max.y-min.y;
- w = max.x-min.x;
- if (h > w) {
- r = w/2;
- draw_arc(DA, gc, fill, min.x+r, max.y-r, r, 180, 0);
- if (fill) {
- gdk_draw_rectangle(DA, gc, fill,
- min.x, min.y+r, w, h-2*r);
- } else {
- gdk_draw_line(DA, gc, min.x, min.y+r, min.x, max.y-r);
- gdk_draw_line(DA, gc, max.x, min.y+r, max.x, max.y-r);
- }
- draw_arc(DA, gc, fill, min.x+r, min.y+r, r, 0, 180);
- } else {
- r = h/2;
- draw_arc(DA, gc, fill, min.x+r, min.y+r, r, 90, 270);
- if (fill) {
- gdk_draw_rectangle(DA, gc, fill,
- min.x+r, min.y, w-2*r, h);
- } else {
- gdk_draw_line(DA, gc, min.x+r, min.y, max.x-r, min.y);
- gdk_draw_line(DA, gc, min.x+r, max.y, max.x-r, max.y);
- }
- draw_arc(DA, gc, fill, max.x-r, min.y+r, r, 270, 90);
- }
- }
- void gui_draw_rpad(struct inst *self)
- {
- GdkGC *gc;
- int fill;
- gc = pad_gc(self, &fill);
- draw_rounded_rect(gc, self->base, self->u.pad.other, fill);
- gui_draw_pad_text(self);
- }
- /* ----- hole -------------------------------------------------------------- */
- unit_type gui_dist_hole(struct inst *self, struct coord pos, unit_type scale)
- {
- unit_type d;
- /* @@@ not quite right ... */
- if (inside_rect(pos, self->base, self->u.hole.other))
- return SELECT_R;
- d = dist_rect(pos, self->base, self->u.hole.other)/scale;
- return d > SELECT_R ? -1 : d;
- }
- void gui_draw_hole(struct inst *self)
- {
- draw_rounded_rect(gc_hole[get_mode(self)],
- self->base, self->u.hole.other, 1);
- draw_rounded_rect(gc_rim[get_mode(self)],
- self->base, self->u.hole.other, 0);
- }
- /* ----- arc --------------------------------------------------------------- */
- unit_type gui_dist_arc(struct inst *self, struct coord pos, unit_type scale)
- {
- struct coord c = self->base;
- struct coord p;
- unit_type r, d_min, d;
- double angle, a1, a2;
- r = self->u.arc.width/scale/2;
- if (r < SELECT_R)
- r = SELECT_R;
- /* check endpoints */
- p = rotate_r(c, self->u.arc.r, self->u.arc.a1);
- d_min = hypot(pos.x-p.x, pos.y-p.y);
- p = rotate_r(c, self->u.arc.r, self->u.arc.a2);
- d = hypot(pos.x-p.x, pos.y-p.y);
- if (d < d_min)
- d_min = d;
- if (d_min/scale <= r)
- return d;
- /* distance from the circle containing the arc */
- d = dist_circle(pos, c, self->u.arc.r)/scale;
- if (d > r)
- return -1;
- if (self->u.arc.a1 == self->u.arc.a2)
- return d;
- /* see if we're close to the part that's actually drawn */
- angle = theta(c, pos);
- a1 = self->u.arc.a1;
- a2 = self->u.arc.a2;
- if (angle < 0)
- angle += 360;
- if (a2 < a1)
- a2 += 360;
- if (angle < a1)
- angle += 360;
- return angle >= a1 && angle <= a2 ? d : -1;
- }
- void gui_draw_arc(struct inst *self)
- {
- struct coord center = translate(self->base);
- GdkGC *gc;
- gc = gc_obj[get_mode(self)];
- set_width(gc, self->u.arc.width/draw_ctx.scale);
- draw_arc(DA, gc, FALSE, center.x, center.y,
- self->u.arc.r/draw_ctx.scale, self->u.arc.a1, self->u.arc.a2);
- }
- /* ----- meas -------------------------------------------------------------- */
- static struct coord offset_vec(struct coord a, struct coord b,
- const struct inst *self)
- {
- struct coord res;
- double f;
- res.x = a.y-b.y;
- res.y = b.x-a.x;
- if (res.x == 0 && res.y == 0)
- return res;
- f = self->u.meas.offset/hypot(res.x, res.y);
- res.x *= f;
- res.y *= f;
- return res;
- }
- void project_meas(const struct inst *inst, struct coord *a1, struct coord *b1)
- {
- const struct meas *meas = &inst->obj->u.meas;
- struct coord off;
- *a1 = inst->base;
- *b1 = inst->u.meas.end;
- switch (meas->type) {
- case mt_xy_next:
- case mt_xy_max:
- break;
- case mt_x_next:
- case mt_x_max:
- b1->y = a1->y;
- break;
- case mt_y_next:
- case mt_y_max:
- b1->x = a1->x;
- break;
- default:
- abort();
- }
- off = offset_vec(*a1, *b1, inst);
- *a1 = add_vec(*a1, off);
- *b1 = add_vec(*b1, off);
- }
- unit_type gui_dist_meas(struct inst *self, struct coord pos, unit_type scale)
- {
- struct coord a1, b1;
- unit_type d;
- project_meas(self, &a1, &b1);
- d = dist_line(pos, a1, b1)/scale;
- return d > SELECT_R ? -1 : d;
- }
- char *format_len(const char *label, unit_type len, enum curr_unit unit)
- {
- const char *u = "";
- double n;
- int mm;
- switch (unit) {
- case curr_unit_mm:
- n = units_to_mm(len);
- mm = 1;
- break;
- case curr_unit_mil:
- n = units_to_mil(len);
- mm = 0;
- break;
- case curr_unit_auto:
- n = units_to_best(len, &mm);
- u = mm ? "mm" : "mil";
- break;
- default:
- abort();
- }
- return stralloc_printf(mm ?
- "%s" MM_FORMAT_SHORT "%s" :
- "%s" MIL_FORMAT_SHORT "%s",
- label, n, u);
- }
- void gui_draw_meas(struct inst *self)
- {
- const struct meas *meas = &self->obj->u.meas;
- struct coord a0, b0, a1, b1, c, d;
- GdkGC *gc;
- double len;
- char *s;
- a0 = translate(self->base);
- b0 = translate(self->u.meas.end);
- project_meas(self, &a1, &b1);
- len = dist_point(a1, b1);
- a1 = translate(a1);
- b1 = translate(b1);
- gc = gc_meas[get_mode(self)];
- gdk_draw_line(DA, gc, a0.x, a0.y, a1.x, a1.y);
- gdk_draw_line(DA, gc, b0.x, b0.y, b1.x, b1.y);
- gdk_draw_line(DA, gc, a1.x, a1.y, b1.x, b1.y);
- draw_arrow(gc, FALSE, a1, b1, MEAS_ARROW_LEN, MEAS_ARROW_ANGLE);
- draw_arrow(gc, FALSE, b1, a1, MEAS_ARROW_LEN, MEAS_ARROW_ANGLE);
- c = add_vec(a1, b1);
- d = sub_vec(b1, a1);
- s = format_len(meas->label ? meas->label : "", len, curr_unit);
- render_text(DA, gc, c.x/2, c.y/2, -atan2(d.y, d.x)/M_PI*180, s,
- MEAS_FONT, 0.5, -MEAS_BASELINE_OFFSET,
- dist_point(a1, b1)-1.5*MEAS_ARROW_LEN, 0);
- free(s);
- }
- /* ----- frame ------------------------------------------------------------- */
- unit_type gui_dist_frame_eye(struct inst *self, struct coord pos,
- unit_type scale)
- {
- unit_type d;
- d = dist_point(pos, self->base)/scale;
- return d > FRAME_EYE_R2 ? -1 : d;
- }
- static unit_type dist_from_corner_line(struct inst *self, struct coord pos,
- struct coord vec, unit_type scale)
- {
- struct coord ref;
- ref.x = self->bbox.min.x;
- ref.y = self->bbox.max.y;
- return dist_line(pos, ref, add_vec(ref, vec))/scale;
- }
- unit_type gui_dist_frame(struct inst *self, struct coord pos, unit_type scale)
- {
- unit_type d_min, d;
- struct coord vec;
- d_min = dist_point(pos, self->base)/scale;
- vec.x = FRAME_SHORT_X*scale;
- vec.y = 0;
- d = dist_from_corner_line(self, pos, vec, scale);
- if (d < d_min)
- d_min = d;
- vec.x = 0;
- vec.y = FRAME_SHORT_Y*scale;
- d = dist_from_corner_line(self, pos, vec, scale);
- if (d < d_min)
- d_min = d;
- return d_min > SELECT_R ? -1 : d_min;
- }
- void gui_draw_frame(struct inst *self)
- {
- struct coord center = translate(self->base);
- struct coord corner = { self->bbox.min.x, self->bbox.max.y };
- GdkGC *gc;
- gc = self->u.frame.active ? gc_active_frame : gc_frame[get_mode(self)];
- draw_eye(gc, center, FRAME_EYE_R1, FRAME_EYE_R2);
- if (self->u.frame.ref == frames)
- return;
- corner = translate(corner);
- corner.x -= FRAME_CLEARANCE;
- corner.y -= FRAME_CLEARANCE;
- gdk_draw_line(DA, gc, corner.x, corner.y,
- corner.x+FRAME_SHORT_X, corner.y);
- gdk_draw_line(DA, gc, corner.x, corner.y,
- corner.x, corner.y+FRAME_SHORT_Y);
- render_text(DA, gc, corner.x, corner.y, 0, self->u.frame.ref->name,
- FRAME_FONT, 0, -FRAME_BASELINE_OFFSET, 0, 0);
- }
|