/* * gui_inst.c - GUI, instance functions * * Written 2009, 2010 by Werner Almesberger * Copyright 2009, 2010 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 #include #include #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 gui_draw_pad_text(struct inst *self) { struct coord min = translate(self->base); struct coord max = translate(self->u.pad.other); GdkGC *gc; struct coord c; unit_type h, w; int rot; w = self->bbox.max.x-self->bbox.min.x; h = self->bbox.max.y-self->bbox.min.y; rot = w/1.1 < h; gc = gc_ptext[get_mode(self)]; sort_coord(&min, &max); 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 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_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->name) 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); }