1
0
mirror of git://projects.qi-hardware.com/fped.git synced 2024-07-01 10:44:31 +03:00
fped/gui_inst.c
werner 5d7ab083a3 - README: described use of semicolons
- README: added that loops can also execute zero times
- accept labels only at the beginning of a line
- rectangles and lines no longer use the bounding box for drawing (caused
  offset problems since we now correct for the line width)
- dist_rect and inside_rect no longer require their input pre-sorted
- pad instances now have their own structure and no longer abuse the bounding
  box to know the pad coordinates
- Makefile: use $(GEN) for fig2dev, to reduce chattiness
- when dragging a point, the symbol is now adjusted accordingly
- added moving of rects, pads, circles, and arcs
- added creation of pads 
- moved rotate_r from gui_inst.c to coord.c
- new function "theta" that combines most of the angle calculations
- save_pix_buf: y < 0 clipping changed width, not height



git-svn-id: http://svn.openmoko.org/trunk/eda/fped@5386 99fdad57-331a-0410-800a-d7fa5415bdb3
2009-08-04 21:45:33 +00:00

459 lines
11 KiB
C

/*
* gui_inst.c - GUI, instance functions
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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 "inst.h"
#include "gui.h"
#include "gui_util.h"
#include "gui_style.h"
#include "gui_inst.h"
#define DA GDK_DRAWABLE(ctx->widget->window)
/* ----- coordinate translation -------------------------------------------- */
struct coord translate(const struct draw_ctx *ctx, struct coord pos)
{
pos.x -= ctx->center.x;
pos.y -= ctx->center.y;
pos.x /= ctx->scale;
pos.y /= ctx->scale;
pos.y = -pos.y;
pos.x += ctx->widget->allocation.width/2;
pos.y += ctx->widget->allocation.height/2;
//fprintf(stderr, "%d %d\n", (int) pos.x, (int) pos.y);
return pos;
}
struct coord canvas_to_coord(const struct draw_ctx *ctx, int x, int y)
{
struct coord pos;
x -= ctx->widget->allocation.width/2;
y -= ctx->widget->allocation.height/2;
y = -y;
pos.x = x*ctx->scale+ctx->center.x;
pos.y = y*ctx->scale+ctx->center.y;
return pos;
}
/* ----- drawing primitives ------------------------------------------------ */
static void draw_eye(struct draw_ctx *ctx, 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(struct draw_ctx *ctx, 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(struct draw_ctx *ctx, GdkGC *gc, int fill,
struct coord from, struct coord to, int len, double angle)
{
struct coord p[3];
struct coord side;
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(ctx, gc, fill, p, 3);
}
static enum mode get_mode(struct inst *self)
{
if (selected_inst == self)
return mode_selected;
if (self->active)
return self->in_path ? mode_active_in_path : mode_active;
return self->in_path ? mode_inactive_in_path : 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.rect.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.rect.end)/scale;
return d > SELECT_R ? -1 : d;
}
void gui_hover_vec(struct inst *self, struct draw_ctx *ctx)
{
struct coord center = translate(ctx, self->u.rect.end);
GdkGC *gc;
gc = gc_vec[mode_hover];
draw_circle(DA, gc, FALSE, center.x, center.y, VEC_EYE_R);
}
void gui_draw_vec(struct inst *self, struct draw_ctx *ctx)
{
struct coord from = translate(ctx, self->base);
struct coord to = translate(ctx, self->u.rect.end);
GdkGC *gc;
gc = gc_vec[get_mode(self)];
draw_arrow(ctx, 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 draw_ctx *ctx)
{
struct coord min = translate(ctx, self->base);
struct coord max = translate(ctx, self->u.rect.end);
GdkGC *gc;
gc = gc_obj[get_mode(self)];
set_width(gc, self->u.rect.width/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 draw_ctx *ctx)
{
struct coord min = translate(ctx, self->base);
struct coord max = translate(ctx, self->u.rect.end);
GdkGC *gc;
sort_coord(&min, &max);
gc = gc_obj[get_mode(self)];
set_width(gc, self->u.rect.width/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;
}
void gui_draw_pad(struct inst *self, struct draw_ctx *ctx)
{
struct coord min = translate(ctx, self->base);
struct coord max = translate(ctx, self->u.pad.other);
GdkGC *gc;
struct coord c;
unit_type h, w;
gc = gc_pad[get_mode(self)];
sort_coord(&min, &max);
gdk_draw_rectangle(DA, gc, TRUE,
min.x, min.y, max.x-min.x, max.y-min.y);
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, w <= h*1.1 ? 0 : 90,
self->u.pad.name, PAD_FONT, 0.5, 0.5,
w-2*PAD_BORDER, h-2*PAD_BORDER);
}
/* ----- 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, 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);
a2 = self->u.arc.a2;
if (a2 < self->u.arc.a1)
a2 += 180;
return angle >= self->u.arc.a1 && angle <= a2 ? d : -1;
}
void gui_draw_arc(struct inst *self, struct draw_ctx *ctx)
{
struct coord center = translate(ctx, self->base);
GdkGC *gc;
gc = gc_obj[get_mode(self)];
set_width(gc, self->u.arc.width/ctx->scale);
draw_arc(DA, gc, FALSE, center.x, center.y,
self->u.arc.r/ctx->scale, self->u.arc.a1, self->u.arc.a2);
}
/* ----- meas -------------------------------------------------------------- */
static struct coord offset_vec(const struct inst *self)
{
struct coord a, b, res;
double f;
a = self->base;
b = self->u.meas.end;
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;
}
unit_type gui_dist_meas(struct inst *self, struct coord pos, unit_type scale)
{
struct coord a, b, off;
unit_type d;
off = offset_vec(self);
a = add_vec(self->base, off);
b = add_vec(self->u.meas.end, off);
d = dist_line(pos, a, b)/scale;
return d > SELECT_R ? -1 : d;
}
void gui_draw_meas(struct inst *self, struct draw_ctx *ctx)
{
struct coord a0, b0, a1, b1, off, c, d;
GdkGC *gc;
char *s;
off = offset_vec(self);
a0 = translate(ctx, self->base);
b0 = translate(ctx, self->u.meas.end);
a1 = translate(ctx, add_vec(self->base, off));
b1 = translate(ctx, add_vec(self->u.meas.end, off));
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(ctx, gc, FALSE, a1, b1, MEAS_ARROW_LEN, MEAS_ARROW_ANGLE);
draw_arrow(ctx, gc, FALSE, b1, a1, MEAS_ARROW_LEN, MEAS_ARROW_ANGLE);
c = add_vec(a1, b1);
d = sub_vec(b0, a0);
s = stralloc_printf("%lgmm",
units_to_mm(dist_point(self->base, self->u.meas.end)));
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_hover_frame(struct inst *self, struct draw_ctx *ctx)
{
struct coord center = translate(ctx, self->base);
GdkGC *gc;
gc = gc_frame[mode_hover];
draw_circle(DA, gc, FALSE, center.x, center.y, FRAME_EYE_R2);
}
void gui_draw_frame(struct inst *self, struct draw_ctx *ctx)
{
struct coord center = translate(ctx, 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(ctx, gc, center, FRAME_EYE_R1, FRAME_EYE_R2);
if (!self->u.frame.ref->name)
return;
corner = translate(ctx, 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);
}