Root/gui_inst.c

Source at commit 377b7f81193685f23c93546767a059e194419446 created 3 years 4 months ago.
By Werner Almesberger, fped.c: new option -m (for -p and -P) to suppress showing measurements
1/*
2 * gui_inst.c - GUI, instance functions
3 *
4 * Written 2009-2012 by Werner Almesberger
5 * Copyright 2009-2012 by Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#include <stdlib.h>
15#include <math.h>
16#include <gtk/gtk.h>
17
18#include "util.h"
19#include "coord.h"
20#include "inst.h"
21#include "gui.h"
22#include "gui_util.h"
23#include "gui_style.h"
24#include "gui_status.h"
25#include "gui_inst.h"
26
27
28/* ----- coordinate translation -------------------------------------------- */
29
30
31struct coord translate(struct coord pos)
32{
33    pos.x -= draw_ctx.center.x;
34    pos.y -= draw_ctx.center.y;
35    pos.x /= draw_ctx.scale;
36    pos.y /= draw_ctx.scale;
37    pos.y = -pos.y;
38    pos.x += draw_ctx.widget->allocation.width/2;
39    pos.y += draw_ctx.widget->allocation.height/2;
40    return pos;
41}
42
43
44struct coord canvas_to_coord(int x, int y)
45{
46    struct coord pos;
47
48    x -= draw_ctx.widget->allocation.width/2;
49    y -= draw_ctx.widget->allocation.height/2;
50    y = -y;
51    pos.x = x*draw_ctx.scale+draw_ctx.center.x;
52    pos.y = y*draw_ctx.scale+draw_ctx.center.y;
53    return pos;
54}
55
56
57/* ----- drawing primitives ------------------------------------------------ */
58
59
60static void draw_eye(GdkGC *gc, struct coord center, int r1, int r2)
61{
62    draw_circle(DA, gc, TRUE, center.x, center.y, r1);
63    draw_circle(DA, gc, FALSE, center.x, center.y, r2);
64}
65
66
67#define MAX_POINTS 10
68
69
70static void draw_poly(GdkGC *gc, int fill,
71    const struct coord *points, int n_points)
72{
73    GdkPoint gp[MAX_POINTS];
74    int i;
75
76    if (n_points > MAX_POINTS)
77        abort();
78    for (i = 0; i != n_points; i++) {
79        gp[i].x = points[i].x;
80        gp[i].y = points[i].y;
81    }
82    if (fill) {
83        gdk_draw_polygon(DA, gc, fill, gp, n_points);
84    } else {
85        gdk_draw_line(DA, gc, gp[0].x, gp[0].y, gp[1].x, gp[1].y);
86        gdk_draw_line(DA, gc, gp[1].x, gp[1].y, gp[2].x, gp[2].y);
87    }
88}
89
90
91static void draw_arrow(GdkGC *gc, int fill,
92    struct coord from, struct coord to, int len, double angle)
93{
94    struct coord p[3];
95    struct coord side;
96
97    if (from.x == to.x && from.y == to.y) {
98        side.x = 0;
99        side.y = -len;
100    } else {
101        side = normalize(sub_vec(to, from), len);
102    }
103    p[0] = add_vec(to, rotate(side, 180-angle));
104    p[1] = to;
105    p[2] = add_vec(to, rotate(side, 180+angle));
106    draw_poly(gc, fill, p, 3);
107}
108
109
110static enum mode get_mode(const struct inst *self)
111{
112    if (selected_inst == self)
113        return mode_selected;
114    return self->active || bright(self) ? mode_active : mode_inactive;
115}
116
117
118/* ----- vec --------------------------------------------------------------- */
119
120
121unit_type gui_dist_vec(struct inst *self, struct coord pos, unit_type scale)
122{
123    unit_type d;
124
125    d = dist_point(pos, self->u.vec.end)/scale;
126    return d > VEC_EYE_R ? -1 : d;
127}
128
129
130/*
131 * The circles at a vector's tip enjoy the highest selection priority. However,
132 * users will probably also expected a click on a nicely exposed stem to work.
133 * So we give it second look after having exhausted all other options.
134 */
135
136unit_type gui_dist_vec_fallback(struct inst *self, struct coord pos,
137    unit_type scale)
138{
139    unit_type d;
140
141    d = dist_line(pos, self->base, self->u.vec.end)/scale;
142    return d > SELECT_R ? -1 : d;
143}
144
145
146void gui_highlight_vec(struct inst *self)
147{
148    struct coord center = translate(self->u.vec.end);
149
150    draw_circle(DA, gc_highlight, FALSE, center.x, center.y, VEC_EYE_R);
151}
152
153
154void gui_draw_vec(struct inst *self)
155{
156    struct coord from = translate(self->base);
157    struct coord to = translate(self->u.vec.end);
158    GdkGC *gc;
159
160    gc = gc_vec[get_mode(self)];
161    draw_arrow(gc, TRUE, from, to, VEC_ARROW_LEN, VEC_ARROW_ANGLE);
162    gdk_draw_line(DA, gc, from.x, from.y, to.x, to.y);
163    draw_circle(DA, gc, FALSE, to.x, to.y, VEC_EYE_R);
164}
165
166
167/* ----- line -------------------------------------------------------------- */
168
169
170unit_type gui_dist_line(struct inst *self, struct coord pos, unit_type scale)
171{
172    unit_type r, d;
173
174    r = self->u.rect.width/scale/2;
175    if (r < SELECT_R)
176        r = SELECT_R;
177    d = dist_line(pos, self->base, self->u.rect.end)/scale;
178    return d > r ? -1 : d;
179}
180
181
182void gui_draw_line(struct inst *self)
183{
184    struct coord min = translate(self->base);
185    struct coord max = translate(self->u.rect.end);
186    GdkGC *gc;
187
188    gc = gc_obj[get_mode(self)];
189    set_width(gc, self->u.rect.width/draw_ctx.scale);
190    gdk_draw_line(DA, gc, min.x, min.y, max.x, max.y);
191}
192
193
194/* ----- rect -------------------------------------------------------------- */
195
196
197unit_type gui_dist_rect(struct inst *self, struct coord pos, unit_type scale)
198{
199    unit_type r, d;
200
201    r = self->u.rect.width/scale/2;
202    if (r < SELECT_R)
203        r = SELECT_R;
204    d = dist_rect(pos, self->base, self->u.rect.end)/scale;
205    return d > r ? -1 : d;
206}
207
208
209void gui_draw_rect(struct inst *self)
210{
211    struct coord min = translate(self->base);
212    struct coord max = translate(self->u.rect.end);
213    GdkGC *gc;
214
215    sort_coord(&min, &max);
216    gc = gc_obj[get_mode(self)];
217    set_width(gc, self->u.rect.width/draw_ctx.scale);
218    gdk_draw_rectangle(DA, gc, FALSE,
219        min.x, min.y, max.x-min.x, max.y-min.y);
220}
221
222
223/* ----- pad --------------------------------------------------------------- */
224
225
226unit_type gui_dist_pad(struct inst *self, struct coord pos, unit_type scale)
227{
228    unit_type d;
229
230    if (inside_rect(pos, self->base, self->u.pad.other))
231        return SELECT_R;
232    d = dist_rect(pos, self->base, self->u.pad.other)/scale;
233    return d > SELECT_R ? -1 : d;
234}
235
236
237static void pad_text_in_rect(struct inst *self,
238    struct coord min, struct coord max)
239{
240    GdkGC *gc;
241    struct coord c;
242    unit_type h, w;
243    int rot;
244
245    w = max.x-min.x;
246    h = max.y-min.y;
247    rot = w/1.1 < h;
248    gc = gc_ptext[get_mode(self)];
249    c = add_vec(min, max);
250    h = max.y-min.y;
251    w = max.x-min.x;
252    render_text(DA, gc, c.x/2, c.y/2, rot ? 0 : 90,
253        self->u.pad.name, PAD_FONT, 0.5, 0.5,
254        w-2*PAD_BORDER, h-2*PAD_BORDER);
255}
256
257
258static void maximize_box(struct coord *min_box, struct coord *max_box,
259    unit_type x_min, unit_type y_min, unit_type x_max, unit_type y_max)
260{
261    unit_type d_box, d_new, d;
262
263    d_box = max_box->x-min_box->x;
264    d = max_box->y-min_box->y;
265    if (d < d_box)
266        d_box = d;
267
268    d_new = x_max-x_min;
269    d = y_max-y_min;
270    if (d < d_new)
271        d_new = d;
272
273    if (d_new < d_box)
274        return;
275
276    min_box->x = x_min;
277    min_box->y = y_min;
278    max_box->x = x_max;
279    max_box->y = y_max;
280}
281
282
283static void gui_draw_pad_text(struct inst *self)
284{
285    struct coord pad_min = translate(self->base);
286    struct coord pad_max = translate(self->u.pad.other);
287    struct coord hole_min, hole_max;
288    struct coord box_min, box_max;
289
290    sort_coord(&pad_min, &pad_max);
291    if (!self->u.pad.hole) {
292        pad_text_in_rect(self, pad_min, pad_max);
293        return;
294    }
295
296    hole_min = translate(self->u.pad.hole->base);
297    hole_max = translate(self->u.pad.hole->u.hole.other);
298    sort_coord(&hole_min, &hole_max);
299
300    box_min.x = pad_min.x; /* top */
301    box_min.y = pad_min.y;
302    box_max.x = pad_max.x;
303    box_max.y = hole_min.y;
304
305    maximize_box(&box_min, &box_max,
306        pad_min.x, hole_max.y, pad_max.x, pad_max.y); /* bottom */
307    maximize_box(&box_min, &box_max,
308        pad_min.x, pad_min.y, hole_min.x, pad_max.y); /* left */
309    maximize_box(&box_min, &box_max,
310        hole_max.x, pad_min.y, pad_max.x, pad_max.y); /* right */
311
312    pad_text_in_rect(self, box_min, box_max);
313}
314
315
316static GdkGC *pad_gc(const struct inst *inst, int *fill)
317{
318    *fill = TRUE;
319    switch (layers_to_pad_type(inst->u.pad.layers)) {
320    case pt_bare:
321        return gc_pad_bare[get_mode(inst)];
322    case pt_trace:
323        return gc_pad_trace[get_mode(inst)];
324    case pt_mask:
325        *fill = FALSE;
326        return gc_pad_mask[get_mode(inst)];
327    default:
328        return gc_pad[get_mode(inst)];
329    }
330}
331
332
333void gui_draw_pad(struct inst *self)
334{
335    struct coord min = translate(self->base);
336    struct coord max = translate(self->u.pad.other);
337    GdkGC *gc;
338    int fill;
339
340    gc = pad_gc(self, &fill);
341    sort_coord(&min, &max);
342    gdk_draw_rectangle(DA, gc, fill,
343        min.x, min.y, max.x-min.x, max.y-min.y);
344
345    gui_draw_pad_text(self);
346}
347
348
349static void draw_rounded_rect(GdkGC *gc, struct coord a, struct coord b,
350     int fill)
351{
352    struct coord min = translate(a);
353    struct coord max = translate(b);
354    unit_type h, w, r;
355
356    sort_coord(&min, &max);
357    h = max.y-min.y;
358    w = max.x-min.x;
359    if (h > w) {
360        r = w/2;
361        draw_arc(DA, gc, fill, min.x+r, max.y-r, r, 180, 0);
362        if (fill) {
363            gdk_draw_rectangle(DA, gc, fill,
364                min.x, min.y+r, w, h-2*r);
365        } else {
366            gdk_draw_line(DA, gc, min.x, min.y+r, min.x, max.y-r);
367            gdk_draw_line(DA, gc, max.x, min.y+r, max.x, max.y-r);
368        }
369        draw_arc(DA, gc, fill, min.x+r, min.y+r, r, 0, 180);
370    } else {
371        r = h/2;
372        draw_arc(DA, gc, fill, min.x+r, min.y+r, r, 90, 270);
373        if (fill) {
374            gdk_draw_rectangle(DA, gc, fill,
375                min.x+r, min.y, w-2*r, h);
376        } else {
377            gdk_draw_line(DA, gc, min.x+r, min.y, max.x-r, min.y);
378            gdk_draw_line(DA, gc, min.x+r, max.y, max.x-r, max.y);
379        }
380        draw_arc(DA, gc, fill, max.x-r, min.y+r, r, 270, 90);
381    }
382}
383
384
385void gui_draw_rpad(struct inst *self)
386{
387    GdkGC *gc;
388    int fill;
389
390    gc = pad_gc(self, &fill);
391    draw_rounded_rect(gc, self->base, self->u.pad.other, fill);
392    gui_draw_pad_text(self);
393}
394
395
396/* ----- hole -------------------------------------------------------------- */
397
398
399unit_type gui_dist_hole(struct inst *self, struct coord pos, unit_type scale)
400{
401    unit_type d;
402
403    /* @@@ not quite right ... */
404    if (inside_rect(pos, self->base, self->u.hole.other))
405        return SELECT_R;
406    d = dist_rect(pos, self->base, self->u.hole.other)/scale;
407    return d > SELECT_R ? -1 : d;
408}
409
410
411void gui_draw_hole(struct inst *self)
412{
413    draw_rounded_rect(gc_hole[get_mode(self)],
414        self->base, self->u.hole.other, 1);
415    draw_rounded_rect(gc_rim[get_mode(self)],
416        self->base, self->u.hole.other, 0);
417}
418
419
420/* ----- arc --------------------------------------------------------------- */
421
422
423unit_type gui_dist_arc(struct inst *self, struct coord pos, unit_type scale)
424{
425    struct coord c = self->base;
426    struct coord p;
427    unit_type r, d_min, d;
428    double angle, a1, a2;
429
430    r = self->u.arc.width/scale/2;
431    if (r < SELECT_R)
432        r = SELECT_R;
433
434    /* check endpoints */
435
436    p = rotate_r(c, self->u.arc.r, self->u.arc.a1);
437    d_min = hypot(pos.x-p.x, pos.y-p.y);
438
439    p = rotate_r(c, self->u.arc.r, self->u.arc.a2);
440    d = hypot(pos.x-p.x, pos.y-p.y);
441    if (d < d_min)
442        d_min = d;
443
444    if (d_min/scale <= r)
445        return d;
446
447    /* distance from the circle containing the arc */
448
449    d = dist_circle(pos, c, self->u.arc.r)/scale;
450    if (d > r)
451        return -1;
452    if (self->u.arc.a1 == self->u.arc.a2)
453        return d;
454
455    /* see if we're close to the part that's actually drawn */
456
457    angle = theta(c, pos);
458    a1 = self->u.arc.a1;
459    a2 = self->u.arc.a2;
460    if (angle < 0)
461        angle += 360;
462    if (a2 < a1)
463        a2 += 360;
464    if (angle < a1)
465        angle += 360;
466    return angle >= a1 && angle <= a2 ? d : -1;
467}
468
469
470void gui_draw_arc(struct inst *self)
471{
472    struct coord center = translate(self->base);
473    GdkGC *gc;
474
475    gc = gc_obj[get_mode(self)];
476    set_width(gc, self->u.arc.width/draw_ctx.scale);
477    draw_arc(DA, gc, FALSE, center.x, center.y,
478        self->u.arc.r/draw_ctx.scale, self->u.arc.a1, self->u.arc.a2);
479}
480
481
482/* ----- meas -------------------------------------------------------------- */
483
484
485static struct coord offset_vec(struct coord a, struct coord b,
486    const struct inst *self)
487{
488    struct coord res;
489    double f;
490
491    res.x = a.y-b.y;
492    res.y = b.x-a.x;
493    if (res.x == 0 && res.y == 0)
494        return res;
495    f = self->u.meas.offset/hypot(res.x, res.y);
496    res.x *= f;
497    res.y *= f;
498    return res;
499}
500
501
502void project_meas(const struct inst *inst, struct coord *a1, struct coord *b1)
503{
504    const struct meas *meas = &inst->obj->u.meas;
505    struct coord off;
506
507    *a1 = inst->base;
508    *b1 = inst->u.meas.end;
509    switch (meas->type) {
510    case mt_xy_next:
511    case mt_xy_max:
512        break;
513    case mt_x_next:
514    case mt_x_max:
515        b1->y = a1->y;
516        break;
517    case mt_y_next:
518    case mt_y_max:
519        b1->x = a1->x;
520        break;
521    default:
522        abort();
523    }
524    off = offset_vec(*a1, *b1, inst);
525    *a1 = add_vec(*a1, off);
526    *b1 = add_vec(*b1, off);
527}
528
529
530unit_type gui_dist_meas(struct inst *self, struct coord pos, unit_type scale)
531{
532    struct coord a1, b1;
533    unit_type d;
534
535    project_meas(self, &a1, &b1);
536    d = dist_line(pos, a1, b1)/scale;
537    return d > SELECT_R ? -1 : d;
538}
539
540
541char *format_len(const char *label, unit_type len, enum curr_unit unit)
542{
543    const char *u = "";
544    double n;
545    int mm;
546
547    switch (unit) {
548    case curr_unit_mm:
549        n = units_to_mm(len);
550        mm = 1;
551        break;
552    case curr_unit_mil:
553        n = units_to_mil(len);
554        mm = 0;
555        break;
556    case curr_unit_auto:
557        n = units_to_best(len, &mm);
558        u = mm ? "mm" : "mil";
559        break;
560    default:
561        abort();
562    }
563    return stralloc_printf(mm ?
564        "%s" MM_FORMAT_SHORT "%s" :
565        "%s" MIL_FORMAT_SHORT "%s",
566        label, n, u);
567}
568
569
570void gui_draw_meas(struct inst *self)
571{
572    const struct meas *meas = &self->obj->u.meas;
573    struct coord a0, b0, a1, b1, c, d;
574    GdkGC *gc;
575    double len;
576    char *s;
577
578    a0 = translate(self->base);
579    b0 = translate(self->u.meas.end);
580    project_meas(self, &a1, &b1);
581
582    len = dist_point(a1, b1);
583    a1 = translate(a1);
584    b1 = translate(b1);
585    gc = gc_meas[get_mode(self)];
586    gdk_draw_line(DA, gc, a0.x, a0.y, a1.x, a1.y);
587    gdk_draw_line(DA, gc, b0.x, b0.y, b1.x, b1.y);
588    gdk_draw_line(DA, gc, a1.x, a1.y, b1.x, b1.y);
589    draw_arrow(gc, FALSE, a1, b1, MEAS_ARROW_LEN, MEAS_ARROW_ANGLE);
590    draw_arrow(gc, FALSE, b1, a1, MEAS_ARROW_LEN, MEAS_ARROW_ANGLE);
591
592    c = add_vec(a1, b1);
593    d = sub_vec(b1, a1);
594    s = format_len(meas->label ? meas->label : "", len, curr_unit);
595    render_text(DA, gc, c.x/2, c.y/2, -atan2(d.y, d.x)/M_PI*180, s,
596        MEAS_FONT, 0.5, -MEAS_BASELINE_OFFSET,
597        dist_point(a1, b1)-1.5*MEAS_ARROW_LEN, 0);
598    free(s);
599}
600
601
602/* ----- frame ------------------------------------------------------------- */
603
604
605unit_type gui_dist_frame_eye(struct inst *self, struct coord pos,
606    unit_type scale)
607{
608    unit_type d;
609
610    d = dist_point(pos, self->base)/scale;
611    return d > FRAME_EYE_R2 ? -1 : d;
612}
613
614
615static unit_type dist_from_corner_line(struct inst *self, struct coord pos,
616    struct coord vec, unit_type scale)
617{
618    struct coord ref;
619
620    ref.x = self->bbox.min.x;
621    ref.y = self->bbox.max.y;
622    return dist_line(pos, ref, add_vec(ref, vec))/scale;
623}
624
625
626unit_type gui_dist_frame(struct inst *self, struct coord pos, unit_type scale)
627{
628    unit_type d_min, d;
629    struct coord vec;
630
631    d_min = dist_point(pos, self->base)/scale;
632
633    vec.x = FRAME_SHORT_X*scale;
634    vec.y = 0;
635    d = dist_from_corner_line(self, pos, vec, scale);
636    if (d < d_min)
637        d_min = d;
638
639    vec.x = 0;
640    vec.y = FRAME_SHORT_Y*scale;
641    d = dist_from_corner_line(self, pos, vec, scale);
642    if (d < d_min)
643        d_min = d;
644
645    return d_min > SELECT_R ? -1 : d_min;
646}
647
648
649void gui_draw_frame(struct inst *self)
650{
651    struct coord center = translate(self->base);
652    struct coord corner = { self->bbox.min.x, self->bbox.max.y };
653    GdkGC *gc;
654
655    gc = self->u.frame.active ? gc_active_frame : gc_frame[get_mode(self)];
656    draw_eye(gc, center, FRAME_EYE_R1, FRAME_EYE_R2);
657    if (self->u.frame.ref == frames)
658        return;
659    corner = translate(corner);
660    corner.x -= FRAME_CLEARANCE;
661    corner.y -= FRAME_CLEARANCE;
662    gdk_draw_line(DA, gc, corner.x, corner.y,
663        corner.x+FRAME_SHORT_X, corner.y);
664    gdk_draw_line(DA, gc, corner.x, corner.y,
665        corner.x, corner.y+FRAME_SHORT_Y);
666    render_text(DA, gc, corner.x, corner.y, 0, self->u.frame.ref->name,
667        FRAME_FONT, 0, -FRAME_BASELINE_OFFSET, 0, 0);
668}
669

Archive Download this file

Branches:
master



interactive