mirror of
git://projects.qi-hardware.com/fped.git
synced 2024-11-05 06:18:26 +02:00
6c2734f6ac
- make_var now sets up all back pointers, so we don't crash when deselecting a variable (this bug was uncovered by cleanup in the previous commit) - report_parse_error didn't print a newline at the end of the message - introduced frame reference selection and the data structures needed for it - bounding box calculation of arcs used x for y - added frame reference selection example quad.fpd git-svn-id: http://svn.openmoko.org/trunk/eda/fped@5377 99fdad57-331a-0410-800a-d7fa5415bdb3
666 lines
14 KiB
C
666 lines
14 KiB
C
/*
|
|
* inst.c - Instance structures
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <math.h>
|
|
|
|
#include "util.h"
|
|
#include "coord.h"
|
|
#include "expr.h"
|
|
#include "obj.h"
|
|
#include "gui_status.h"
|
|
#include "gui_inst.h"
|
|
#include "inst.h"
|
|
|
|
|
|
struct inst_ops {
|
|
void (*debug)(struct inst *self);
|
|
void (*save)(FILE *file, struct inst *self);
|
|
void (*draw)(struct inst *self, struct draw_ctx *ctx);
|
|
unit_type (*distance)(struct inst *self, struct coord pos,
|
|
unit_type scale);
|
|
void (*select)(struct inst *self);
|
|
};
|
|
|
|
enum inst_prio {
|
|
ip_frame, /* frames have their own selection */
|
|
ip_pad, /* pads also accept clicks inside */
|
|
ip_circ, /* circles don't overlap easily */
|
|
ip_arc, /* arc are like circles, just shorter */
|
|
ip_rect, /* rectangles have plenty of sides */
|
|
ip_meas, /* mesurements are like lines but set a bit apart */
|
|
ip_line, /* lines are easly overlapped by other things */
|
|
ip_vec, /* vectors only have the end point */
|
|
ip_n, /* number of priorities */
|
|
};
|
|
|
|
|
|
#define FOR_INST_PRIOS_UP(prio) \
|
|
for (prio = 0; prio != ip_n; prio++)
|
|
|
|
#define FOR_INST_PRIOS_DOWN(prio) \
|
|
for (prio = ip_n-1; prio != (enum inst_prio) -1; prio--)
|
|
|
|
#define FOR_INSTS_UP(prio, inst) \
|
|
FOR_INST_PRIOS_UP(prio) \
|
|
for (inst = insts[prio]; inst; inst = inst->next)
|
|
|
|
#define FOR_INSTS_DOWN(prio, inst) \
|
|
FOR_INST_PRIOS_DOWN(prio) \
|
|
for (inst = insts[prio]; inst; inst = inst->next)
|
|
|
|
|
|
struct inst *selected_inst = NULL;
|
|
|
|
static struct inst *curr_frame = NULL;
|
|
static struct inst *insts[ip_n], **next_inst[ip_n];
|
|
static struct inst *prev_insts[ip_n];
|
|
|
|
static unsigned long active_set = 0;
|
|
|
|
|
|
#define IS_ACTIVE ((active_set & 1))
|
|
|
|
|
|
/* ----- 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;
|
|
}
|
|
|
|
|
|
/* ----- selection --------------------------------------------------------- */
|
|
|
|
|
|
static struct inst_ops vec_ops;
|
|
static struct inst_ops frame_ops;
|
|
|
|
|
|
static void set_path(int on)
|
|
{
|
|
struct inst *inst;
|
|
return;
|
|
if (inst->ops != &vec_ops && inst->ops != &frame_ops)
|
|
return;
|
|
/* @@@ wrong */
|
|
for (inst = selected_inst; inst; inst = inst->outer) {
|
|
if (inst->ops != &vec_ops && inst->ops != &frame_ops)
|
|
break;
|
|
inst->in_path = on;
|
|
}
|
|
}
|
|
|
|
|
|
int inst_select(const struct draw_ctx *ctx, struct coord pos)
|
|
{
|
|
enum inst_prio prio;
|
|
struct inst *inst;
|
|
int best_dist, dist;
|
|
|
|
deselect_outside();
|
|
edit_nothing();
|
|
selected_inst = NULL;
|
|
FOR_INST_PRIOS_DOWN(prio) {
|
|
for (inst = insts[prio]; inst; inst = inst->next) {
|
|
if (!inst->active || !inst->ops->distance)
|
|
continue;
|
|
dist = inst->ops->distance(inst, pos, ctx->scale);
|
|
if (dist >= 0 && (!selected_inst || best_dist > dist)) {
|
|
selected_inst = inst;
|
|
best_dist = dist;
|
|
}
|
|
}
|
|
if (selected_inst)
|
|
goto selected;
|
|
}
|
|
|
|
/* give vectors a second chance */
|
|
|
|
for (inst = insts[ip_vec]; inst; inst = inst->next) {
|
|
if (!inst->active)
|
|
continue;
|
|
dist = gui_dist_vec_fallback(inst, pos, ctx->scale);
|
|
if (dist >= 0 && (!selected_inst || best_dist > dist)) {
|
|
selected_inst = inst;
|
|
best_dist = dist;
|
|
}
|
|
}
|
|
|
|
if (!selected_inst)
|
|
return 0;
|
|
|
|
selected:
|
|
set_path(1);
|
|
if (selected_inst->ops->select)
|
|
selected_inst->ops->select(selected_inst);
|
|
return 1;
|
|
}
|
|
|
|
|
|
void inst_deselect(void)
|
|
{
|
|
if (selected_inst)
|
|
set_path(0);
|
|
deselect_outside();
|
|
status_set_name("");
|
|
status_set_x("");
|
|
status_set_y("");
|
|
status_set_r("");
|
|
status_set_angle("");
|
|
selected_inst = NULL;
|
|
edit_nothing();
|
|
}
|
|
|
|
|
|
static void rect_status(struct coord a, struct coord b, unit_type width)
|
|
{
|
|
struct coord d = sub_vec(b, a);
|
|
double angle;
|
|
|
|
status_set_xy(d);
|
|
if (!d.x && !d.y)
|
|
status_set_angle("a = 0 deg");
|
|
else {
|
|
angle = atan2(d.y, d.x)/M_PI*180.0;
|
|
if (angle < 0)
|
|
angle += 360;
|
|
status_set_angle("a = %3.1f deg", angle);
|
|
}
|
|
status_set_r("r = %5.2f mm", hypot(units_to_mm(d.x), units_to_mm(d.y)));
|
|
if (width != -1)
|
|
status_set_name("width = %5.2f mm", units_to_mm(width));
|
|
}
|
|
|
|
|
|
/* ----- 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)
|
|
{
|
|
update_bbox(&curr_frame->bbox, inst->bbox.min);
|
|
update_bbox(&curr_frame->bbox, inst->bbox.max);
|
|
}
|
|
|
|
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->base = inst->bbox.min = inst->bbox.max = base;
|
|
inst->outer = curr_frame;
|
|
inst->active = IS_ACTIVE;
|
|
inst->in_path = 0;
|
|
inst->next = NULL;
|
|
*next_inst[prio] = inst;
|
|
next_inst[prio] = &inst->next;
|
|
return inst;
|
|
}
|
|
|
|
|
|
/* ----- vec --------------------------------------------------------------- */
|
|
|
|
|
|
static void vec_op_debug(struct inst *self)
|
|
{
|
|
printf("vec %lg, %lg -> %lg, %lg\n",
|
|
units_to_mm(self->base.x), units_to_mm(self->base.y),
|
|
units_to_mm(self->u.rect.end.x), units_to_mm(self->u.rect.end.y));
|
|
}
|
|
|
|
|
|
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_op_select(struct inst *self)
|
|
{
|
|
status_set_name(self->vec->name ? self->vec->name : "");
|
|
rect_status(self->base, self->u.rect.end, -1);
|
|
edit_unique_null(&self->vec->name, validate_vec_name, self->vec);
|
|
}
|
|
|
|
|
|
static struct inst_ops vec_ops = {
|
|
.debug = vec_op_debug,
|
|
.draw = gui_draw_vec,
|
|
.distance = gui_dist_vec,
|
|
.select = vec_op_select,
|
|
};
|
|
|
|
|
|
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.rect.end = vec->pos;
|
|
update_bbox(&inst->bbox, vec->pos);
|
|
propagate_bbox(inst);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* ----- line -------------------------------------------------------------- */
|
|
|
|
|
|
static void line_op_debug(struct inst *self)
|
|
{
|
|
printf("line %lg, %lg / %lg, %lg\n",
|
|
units_to_mm(self->base.x), units_to_mm(self->base.y),
|
|
units_to_mm(self->u.rect.end.x), units_to_mm(self->u.rect.end.y));
|
|
}
|
|
|
|
|
|
static void line_op_select(struct inst *self)
|
|
{
|
|
rect_status(self->bbox.min, self->bbox.max, self->u.rect.width);
|
|
edit_expr(&self->obj->u.line.width);
|
|
}
|
|
|
|
|
|
static struct inst_ops line_ops = {
|
|
.debug = line_op_debug,
|
|
.draw = gui_draw_line,
|
|
.distance = gui_dist_line,
|
|
.select = line_op_select,
|
|
};
|
|
|
|
|
|
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;
|
|
update_bbox(&inst->bbox, b);
|
|
propagate_bbox(inst);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* ----- rect -------------------------------------------------------------- */
|
|
|
|
|
|
static void rect_op_debug(struct inst *self)
|
|
{
|
|
printf("rect %lg, %lg / %lg, %lg\n",
|
|
units_to_mm(self->base.x), units_to_mm(self->base.y),
|
|
units_to_mm(self->u.rect.end.x), units_to_mm(self->u.rect.end.y));
|
|
}
|
|
|
|
|
|
static void rect_op_select(struct inst *self)
|
|
{
|
|
rect_status(self->bbox.min, self->bbox.max, self->u.rect.width);
|
|
edit_expr(&self->obj->u.rect.width);
|
|
}
|
|
|
|
|
|
static struct inst_ops rect_ops = {
|
|
.debug = rect_op_debug,
|
|
.draw = gui_draw_rect,
|
|
.distance = gui_dist_rect,
|
|
.select = rect_op_select,
|
|
};
|
|
|
|
|
|
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;
|
|
update_bbox(&inst->bbox, b);
|
|
propagate_bbox(inst);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* ----- pad --------------------------------------------------------------- */
|
|
|
|
|
|
static void pad_op_debug(struct inst *self)
|
|
{
|
|
printf("pad \"%s\" %lg, %lg / %lg, %lg\n", self->u.name,
|
|
units_to_mm(self->bbox.min.x), units_to_mm(self->bbox.min.y),
|
|
units_to_mm(self->bbox.max.x), units_to_mm(self->bbox.max.y));
|
|
}
|
|
|
|
|
|
static int validate_pad_name(const char *s, void *ctx)
|
|
{
|
|
char *tmp;
|
|
|
|
tmp = expand(s, NULL);
|
|
if (!tmp)
|
|
return 0;
|
|
free(tmp);
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void pad_op_select(struct inst *self)
|
|
{
|
|
status_set_name(self->u.name);
|
|
rect_status(self->bbox.min, self->bbox.max, -1);
|
|
edit_name(&self->obj->u.pad.name, validate_pad_name, NULL);
|
|
}
|
|
|
|
|
|
static struct inst_ops pad_ops = {
|
|
.debug = pad_op_debug,
|
|
.draw = gui_draw_pad,
|
|
.distance = gui_dist_pad,
|
|
.select = pad_op_select,
|
|
};
|
|
|
|
|
|
int inst_pad(struct obj *obj, const char *name, struct coord a, struct coord b)
|
|
{
|
|
struct inst *inst;
|
|
|
|
inst = add_inst(&pad_ops, ip_pad, a);
|
|
inst->obj = obj;
|
|
inst->u.name = stralloc(name);
|
|
update_bbox(&inst->bbox, b);
|
|
propagate_bbox(inst);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* ----- arc --------------------------------------------------------------- */
|
|
|
|
|
|
static void arc_op_debug(struct inst *self)
|
|
{
|
|
printf("arc %lg, %lg radius %lg %lg ... %lg\n",
|
|
units_to_mm(self->base.x), units_to_mm(self->base.y),
|
|
units_to_mm(self->u.arc.r), self->u.arc.a1, self->u.arc.a2);
|
|
}
|
|
|
|
|
|
static void arc_op_select(struct inst *self)
|
|
{
|
|
status_set_xy(self->base);
|
|
status_set_angle("a = %3.1f deg",
|
|
self->u.arc.a1 == self->u.arc.a2 ? 360 :
|
|
self->u.arc.a2-self->u.arc.a1);
|
|
status_set_r("r = %5.2f mm", units_to_mm(self->u.arc.r));
|
|
status_set_name("width = %5.2f mm", units_to_mm(self->u.arc.width));
|
|
edit_expr(&self->obj->u.arc.width);
|
|
}
|
|
|
|
|
|
static struct inst_ops arc_ops = {
|
|
.debug = arc_op_debug,
|
|
.draw = gui_draw_arc,
|
|
.distance = gui_dist_arc,
|
|
.select = arc_op_select,
|
|
};
|
|
|
|
|
|
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;
|
|
|
|
inst = add_inst(&arc_ops, ip_arc, center);
|
|
inst->obj = obj;
|
|
r = hypot(start.x-center.x, start.y-center.y);
|
|
a1 = atan2(start.y-center.y, start.x-center.x)/M_PI*180.0;
|
|
a2 = atan2(end.y-center.y, end.x-center.x)/M_PI*180.0;
|
|
if (a1 < 0)
|
|
a1 += 360.0;
|
|
if (a2 < 0)
|
|
a2 += 360.0;
|
|
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;
|
|
propagate_bbox(inst);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* ----- measurement ------------------------------------------------------- */
|
|
|
|
|
|
static void meas_op_debug(struct inst *self)
|
|
{
|
|
printf("meas %lg, %lg / %lg, %lg offset %lg\n",
|
|
units_to_mm(self->base.x), units_to_mm(self->base.y),
|
|
units_to_mm(self->u.meas.end.x), units_to_mm(self->u.meas.end.y),
|
|
units_to_mm(self->u.meas.offset));
|
|
}
|
|
|
|
|
|
static void meas_op_select(struct inst *self)
|
|
{
|
|
rect_status(self->bbox.min, self->bbox.max, -1);
|
|
status_set_name("offset = %5.2f mm", units_to_mm(self->u.meas.offset));
|
|
edit_expr(&self->obj->u.meas.offset);
|
|
}
|
|
|
|
|
|
static struct inst_ops meas_ops = {
|
|
.debug = meas_op_debug,
|
|
.draw = gui_draw_meas,
|
|
.distance = gui_dist_meas,
|
|
.select = meas_op_select,
|
|
};
|
|
|
|
|
|
int inst_meas(struct obj *obj, struct coord from, struct coord to,
|
|
unit_type offset)
|
|
{
|
|
struct inst *inst;
|
|
|
|
inst = add_inst(&meas_ops, ip_meas, from);
|
|
inst->obj = obj;
|
|
inst->u.meas.end = to;
|
|
inst->u.meas.offset = offset;
|
|
/* @@@ our bbox is actually a bit more complex than this */
|
|
update_bbox(&inst->bbox, to);
|
|
propagate_bbox(inst);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* ----- 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_debug(struct inst *self)
|
|
{
|
|
printf("frame %s @ %lg, %lg\n",
|
|
self->u.frame.ref->name ? self->u.frame.ref->name : "(root)",
|
|
units_to_mm(self->base.x), units_to_mm(self->base.y));
|
|
}
|
|
|
|
|
|
static struct inst_ops frame_ops = {
|
|
.debug = frame_op_debug,
|
|
.draw = gui_draw_frame,
|
|
};
|
|
|
|
|
|
void inst_begin_frame(const struct frame *frame, struct coord base, int active)
|
|
{
|
|
struct inst *inst;
|
|
|
|
inst = add_inst(&frame_ops, ip_frame, base);
|
|
inst->u.frame.ref = frame;
|
|
inst->active = active;
|
|
curr_frame = inst;
|
|
}
|
|
|
|
|
|
void inst_end_frame(const struct frame *frame)
|
|
{
|
|
struct inst *inst = curr_frame;
|
|
|
|
curr_frame = curr_frame->outer;
|
|
if (curr_frame)
|
|
propagate_bbox(inst);
|
|
}
|
|
|
|
|
|
/* ----- misc. ------------------------------------------------------------- */
|
|
|
|
|
|
struct bbox inst_get_bbox(void)
|
|
{
|
|
return insts[ip_frame]->bbox;
|
|
}
|
|
|
|
|
|
static void inst_free(struct inst *list[ip_n])
|
|
{
|
|
enum inst_prio prio;
|
|
struct inst *next;
|
|
|
|
FOR_INST_PRIOS_UP(prio)
|
|
while (list[prio]) {
|
|
next = list[prio]->next;
|
|
free(list[prio]);
|
|
list[prio] = next;
|
|
}
|
|
}
|
|
|
|
|
|
void inst_start(void)
|
|
{
|
|
enum inst_prio prio;
|
|
|
|
FOR_INST_PRIOS_UP(prio) {
|
|
prev_insts[prio] = insts[prio];
|
|
insts[prio] = NULL;
|
|
next_inst[prio] = &insts[prio];
|
|
}
|
|
}
|
|
|
|
|
|
void inst_commit(void)
|
|
{
|
|
inst_free(prev_insts);
|
|
}
|
|
|
|
|
|
void inst_revert(void)
|
|
{
|
|
enum inst_prio prio;
|
|
|
|
inst_free(insts);
|
|
FOR_INST_PRIOS_UP(prio)
|
|
insts[prio] = prev_insts[prio];
|
|
}
|
|
|
|
|
|
void inst_draw(struct draw_ctx *ctx)
|
|
{
|
|
enum inst_prio prio;
|
|
struct inst *inst;
|
|
|
|
FOR_INSTS_UP(prio, inst)
|
|
if (!inst->active && inst->ops->draw)
|
|
inst->ops->draw(inst, ctx);
|
|
FOR_INSTS_UP(prio, inst)
|
|
if (prio != ip_frame && inst->active &&
|
|
inst != selected_inst && inst->ops->draw)
|
|
inst->ops->draw(inst, ctx);
|
|
for (inst = insts[ip_frame]; inst; inst = inst->next)
|
|
if (inst->active && inst != selected_inst && inst->ops->draw)
|
|
inst->ops->draw(inst, ctx);
|
|
if (selected_inst && selected_inst->ops->draw)
|
|
selected_inst->ops->draw(selected_inst, ctx);
|
|
}
|
|
|
|
|
|
void inst_debug(void)
|
|
{
|
|
enum inst_prio prio;
|
|
struct inst *inst;
|
|
|
|
FOR_INSTS_UP(prio, inst)
|
|
inst->ops->debug(inst);
|
|
}
|