Graphviz 16.1.1~dev.20261017.1642
Loading...
Searching...
No Matches
shapes.c
Go to the documentation of this file.
1
4/*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v2.0
8 * which accompanies this distribution, and is available at
9 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
10 *
11 * Contributors: Details at https://graphviz.org
12 *************************************************************************/
13
14#include "config.h"
15
16#include <assert.h>
17#include <common/render.h>
18#include <common/htmltable.h>
19#include <float.h>
20#include <limits.h>
21#include <math.h>
22#include <stddef.h>
23#include <stdbool.h>
24#include <string.h>
25#include <util/alloc.h>
26#include <util/gv_math.h>
27#include <util/overflow.h>
28#include <util/streq.h>
29#include <util/unreachable.h>
30
31#define RBCONST 12
32#define RBCURVE .5
33
34typedef struct {
35 pointf (*size_gen) (pointf);
36 void (*vertex_gen) (pointf*, pointf*);
38
39static port Center = {.theta = -1, .clip = true};
40
41#define ATTR_SET(a,n) ((a) && (*(agxget(n,a->index)) != '\0'))
42 /* Default point size = 0.05 inches or 3.6 points */
43#define DEF_POINT 0.05
44 /* Minimum point size = 0.0003 inches or 0.02 points
45 * This will make the radius 0.01 points, which is the smallest
46 * non-zero number output by gvprintdouble in gvdevice.c
47 */
48#define MIN_POINT 0.0003
49 /* extra null character needed to avoid style emitter from thinking
50 * there are arguments.
51 */
52static char *point_style[3] = { "invis\0", "filled\0", 0 };
53
54/* forward declarations of functions used in shapes tables */
55
56static void poly_init(node_t * n);
57static void poly_free(node_t * n);
58static port poly_port(node_t * n, char *portname, char *);
59static bool poly_inside(inside_t * inside_context, pointf p);
60static int poly_path(node_t * n, port * p, int side, boxf rv[], int *kptr);
61static void poly_gencode(GVJ_t * job, node_t * n);
62
63static void record_init(node_t * n);
64static void record_free(node_t * n);
65static port record_port(node_t * n, char *portname, char *);
66static bool record_inside(inside_t * inside_context, pointf p);
67static int record_path(node_t * n, port * p, int side, boxf rv[],
68 int *kptr);
69static void record_gencode(GVJ_t * job, node_t * n);
70
71static void point_init(node_t * n);
72static void point_gencode(GVJ_t * job, node_t * n);
73static bool point_inside(inside_t * inside_context, pointf p);
74
75static bool epsf_inside(inside_t * inside_context, pointf p);
76static void epsf_gencode(GVJ_t * job, node_t * n);
77
78static pointf star_size (pointf);
79static void star_vertices (pointf*, pointf*);
80static bool star_inside(inside_t * inside_context, pointf p);
85
87static void cylinder_vertices (pointf*, pointf*);
88static void cylinder_draw(GVJ_t *job, pointf *AF, size_t sides, int filled);
93
94/* polygon descriptions. "polygon" with 0 sides takes all user control */
95
96/* regul perip sides orien disto skew */
98
99/* builtin polygon descriptions */
100static polygon_t p_ellipse = {.peripheries = 1, .sides = 1};
101static polygon_t p_circle = {.regular = true, .peripheries = 1, .sides = 1};
102static polygon_t p_egg = {.peripheries = 1, .sides = 1, .distortion = -0.3};
103static polygon_t p_triangle = {.peripheries = 1, .sides = 3};
104static polygon_t p_box = {.peripheries = 1, .sides = 4};
105static polygon_t p_square = {.regular = true, .peripheries = 1, .sides = 4};
107static polygon_t p_plain = {.sides = 4};
108static polygon_t p_diamond = {.peripheries = 1, .sides = 4, .orientation = 45.0};
109static polygon_t p_trapezium = {.peripheries = 1, .sides = 4, .distortion = -0.4};
110static polygon_t p_parallelogram = {.peripheries = 1, .sides = 4, .skew = 0.6};
111static polygon_t p_house = {.peripheries = 1, .sides = 5, .distortion = -0.64};
112static polygon_t p_pentagon = {.peripheries = 1, .sides = 5};
113static polygon_t p_hexagon = {.peripheries = 1, .sides = 6};
114static polygon_t p_septagon = {.peripheries = 1, .sides = 7};
115static polygon_t p_octagon = {.peripheries = 1, .sides = 8};
117 .peripheries = 1, .sides = 4, .option = {.shape = DOGEAR}};
118static polygon_t p_tab = {
119 .peripheries = 1, .sides = 4, .option = {.shape = TAB}};
121 .peripheries = 1, .sides = 4, .option = {.shape = FOLDER}};
123 .peripheries = 1, .sides = 4, .option = {.shape = BOX3D}};
125 .peripheries = 1, .sides = 4, .option = {.shape = COMPONENT}};
127 .peripheries = 1, .sides = 4, .option = {.underline = true}};
129 .sides = 19,
130 .option = {.shape = CYLINDER},
131 .vertices = (pointf *)&cylinder_gen};
132
133/* redundant and undocumented builtin polygons */
135 .regular = true, .peripheries = 2, .sides = 1};
137 .peripheries = 1, .sides = 3, .orientation = 180.0};
139 .peripheries = 1, .sides = 4, .orientation = 180.0, .distortion = -0.4};
141 .peripheries = 1, .sides = 5, .orientation = 180.0, .distortion = -0.64};
142static polygon_t p_doubleoctagon = {.peripheries = 2, .sides = 8};
143static polygon_t p_tripleoctagon = {.peripheries = 3, .sides = 8};
145 .peripheries = 1,
146 .sides = 4,
147 .orientation = 45.0,
148 .option = {.diagonals = true, .auxlabels = true}};
149static polygon_t p_Msquare = {.regular = true,
150 .peripheries = 1,
151 .sides = 4,
152 .option = {.diagonals = true}};
153static polygon_t p_Mcircle = {.regular = true,
154 .peripheries = 1,
155 .sides = 1,
156 .option = {.diagonals = true, .auxlabels = true}};
157
158/* non-convex polygons */
160 .peripheries = 1, .sides = 10, .vertices = (pointf *)&star_gen};
161
162/* biological circuit shapes, as specified by SBOLv*/
165 .peripheries = 1, .sides = 4, .option = {.shape = PROMOTER}};
166static polygon_t p_cds = {
167 .peripheries = 1, .sides = 4, .option = {.shape = CDS}};
169 .peripheries = 1, .sides = 4, .option = {.shape = TERMINATOR}};
170static polygon_t p_utr = {
171 .peripheries = 1, .sides = 4, .option = {.shape = UTR}};
173 .peripheries = 1, .sides = 4, .option = {.shape = INSULATOR}};
175 .peripheries = 1, .sides = 4, .option = {.shape = RIBOSITE}};
177 .peripheries = 1, .sides = 4, .option = {.shape = RNASTAB}};
179 .peripheries = 1, .sides = 4, .option = {.shape = PROTEASESITE}};
181 .peripheries = 1, .sides = 4, .option = {.shape = PROTEINSTAB}};
184 .peripheries = 1, .sides = 4, .option = {.shape = PRIMERSITE}};
186 .peripheries = 1, .sides = 4, .option = {.shape = RESTRICTIONSITE}};
188 .peripheries = 1, .sides = 4, .option = {.shape = FIVEPOVERHANG}};
190 .peripheries = 1, .sides = 4, .option = {.shape = THREEPOVERHANG}};
192 .peripheries = 1, .sides = 4, .option = {.shape = NOVERHANG}};
194 .peripheries = 1, .sides = 4, .option = {.shape = ASSEMBLY}};
196 .peripheries = 1, .sides = 4, .option = {.shape = SIGNATURE}};
198 .peripheries = 1, .sides = 4, .option = {.shape = RPROMOTER}};
200 .peripheries = 1, .sides = 4, .option = {.shape = RARROW}};
202 .peripheries = 1, .sides = 4, .option = {.shape = LARROW}};
204 .peripheries = 1, .sides = 4, .option = {.shape = LPROMOTER}};
205
206static bool IS_BOX(node_t *n) {
207 return ND_shape(n)->polygon == &p_box;
208}
209
210static bool IS_PLAIN(node_t *n) {
211 return ND_shape(n)->polygon == &p_plain;
212}
213
216 return style.rounded || style.diagonals || style.shape != 0;
217}
218
219/*
220 * every shape has these functions:
221 *
222 * void SHAPE_init(node_t *n)
223 * initialize the shape (usually at least its size).
224 * void SHAPE_free(node_t *n)
225 * free all memory used by the shape
226 * port SHAPE_port(node_t *n, char *portname)
227 * return the aiming point and slope (if constrained)
228 * of a port.
229 * int SHAPE_inside(inside_t *inside_context, pointf p, edge_t *e);
230 * test if point is inside the node shape which is
231 * assumed convex.
232 * the point is relative to the node center. the edge
233 * is passed in case the port affects spline clipping.
234 * int SHAPE_path(node *n, edge_t *e, int pt, boxf path[], int *nbox)
235 * create a path for the port of e that touches n,
236 * return side
237 * void SHAPE_gencode(GVJ_t *job, node_t *n)
238 * generate graphics code for a node.
239 *
240 * some shapes, polygons in particular, use additional shape control data *
241 *
242 */
243
269 epsf_init,
270 epsf_free,
271 poly_port,
273 NULL,
275};
292
293static shape_desc Shapes[] = { /* first entry is default for no such shape */
294 {.name = "box", .fns = &poly_fns, .polygon = &p_box},
295 {.name = "polygon", .fns = &poly_fns, .polygon = &p_polygon},
296 {.name = "ellipse", .fns = &poly_fns, .polygon = &p_ellipse},
297 {.name = "oval", .fns = &poly_fns, .polygon = &p_ellipse},
298 {.name = "circle", .fns = &poly_fns, .polygon = &p_circle},
299 {.name = "point", .fns = &point_fns, .polygon = &p_circle},
300 {.name = "egg", .fns = &poly_fns, .polygon = &p_egg},
301 {.name = "triangle", .fns = &poly_fns, .polygon = &p_triangle},
302 {.name = "none", .fns = &poly_fns, .polygon = &p_plaintext},
303 {.name = "plaintext", .fns = &poly_fns, .polygon = &p_plaintext},
304 {.name = "plain", .fns = &poly_fns, .polygon = &p_plain},
305 {.name = "diamond", .fns = &poly_fns, .polygon = &p_diamond},
306 {.name = "trapezium", .fns = &poly_fns, .polygon = &p_trapezium},
307 {.name = "parallelogram", .fns = &poly_fns, .polygon = &p_parallelogram},
308 {.name = "house", .fns = &poly_fns, .polygon = &p_house},
309 {.name = "pentagon", .fns = &poly_fns, .polygon = &p_pentagon},
310 {.name = "hexagon", .fns = &poly_fns, .polygon = &p_hexagon},
311 {.name = "septagon", .fns = &poly_fns, .polygon = &p_septagon},
312 {.name = "octagon", .fns = &poly_fns, .polygon = &p_octagon},
313 {.name = "note", .fns = &poly_fns, .polygon = &p_note},
314 {.name = "tab", .fns = &poly_fns, .polygon = &p_tab},
315 {.name = "folder", .fns = &poly_fns, .polygon = &p_folder},
316 {.name = "box3d", .fns = &poly_fns, .polygon = &p_box3d},
317 {.name = "component", .fns = &poly_fns, .polygon = &p_component},
318 {.name = "cylinder", .fns = &cylinder_fns, .polygon = &p_cylinder},
319 {.name = "rect", .fns = &poly_fns, .polygon = &p_box},
320 {.name = "rectangle", .fns = &poly_fns, .polygon = &p_box},
321 {.name = "square", .fns = &poly_fns, .polygon = &p_square},
322 {.name = "doublecircle", .fns = &poly_fns, .polygon = &p_doublecircle},
323 {.name = "doubleoctagon", .fns = &poly_fns, .polygon = &p_doubleoctagon},
324 {.name = "tripleoctagon", .fns = &poly_fns, .polygon = &p_tripleoctagon},
325 {.name = "invtriangle", .fns = &poly_fns, .polygon = &p_invtriangle},
326 {.name = "invtrapezium", .fns = &poly_fns, .polygon = &p_invtrapezium},
327 {.name = "invhouse", .fns = &poly_fns, .polygon = &p_invhouse},
328 {.name = "underline", .fns = &poly_fns, .polygon = &p_underline},
329 {.name = "Mdiamond", .fns = &poly_fns, .polygon = &p_Mdiamond},
330 {.name = "Msquare", .fns = &poly_fns, .polygon = &p_Msquare},
331 {.name = "Mcircle", .fns = &poly_fns, .polygon = &p_Mcircle},
332 /* biological circuit shapes, as specified by SBOLv*/
334 {.name = "promoter", .fns = &poly_fns, .polygon = &p_promoter},
335 {.name = "cds", .fns = &poly_fns, .polygon = &p_cds},
336 {.name = "terminator", .fns = &poly_fns, .polygon = &p_terminator},
337 {.name = "utr", .fns = &poly_fns, .polygon = &p_utr},
338 {.name = "insulator", .fns = &poly_fns, .polygon = &p_insulator},
339 {.name = "ribosite", .fns = &poly_fns, .polygon = &p_ribosite},
340 {.name = "rnastab", .fns = &poly_fns, .polygon = &p_rnastab},
341 {.name = "proteasesite", .fns = &poly_fns, .polygon = &p_proteasesite},
342 {.name = "proteinstab", .fns = &poly_fns, .polygon = &p_proteinstab},
344 {.name = "primersite", .fns = &poly_fns, .polygon = &p_primersite},
345 {.name = "restrictionsite", .fns = &poly_fns, .polygon = &p_restrictionsite},
346 {.name = "fivepoverhang", .fns = &poly_fns, .polygon = &p_fivepoverhang},
347 {.name = "threepoverhang", .fns = &poly_fns, .polygon = &p_threepoverhang},
348 {.name = "noverhang", .fns = &poly_fns, .polygon = &p_noverhang},
349 {.name = "assembly", .fns = &poly_fns, .polygon = &p_assembly},
350 {.name = "signature", .fns = &poly_fns, .polygon = &p_signature},
351 {.name = "rpromoter", .fns = &poly_fns, .polygon = &p_rpromoter},
352 {.name = "larrow", .fns = &poly_fns, .polygon = &p_larrow},
353 {.name = "rarrow", .fns = &poly_fns, .polygon = &p_rarrow},
354 {.name = "lpromoter", .fns = &poly_fns, .polygon = &p_lpromoter},
355 /* *** shapes other than polygons *** */
356 {.name = "record", .fns = &record_fns, .polygon = NULL},
357 {.name = "Mrecord", .fns = &record_fns, .polygon = NULL},
358 {.name = "epsf", .fns = &epsf_fns, .polygon = NULL},
359 {.name = "star", .fns = &star_fns, .polygon = &p_star},
360 {0}
361};
362
363static void unrecognized(node_t * n, char *p)
364{
365 agwarningf("node %s, port %s unrecognized\n", agnameof(n), p);
366}
367
368static double quant(double val, double q)
369{
370 return ceil(val / q) * q;
371}
372
373/* test if both p0 and p1 are on the same side of the line L0,L1 */
374static int same_side(pointf p0, pointf p1, pointf L0, pointf L1)
375{
376 int s0, s1;
377 double a, b, c;
378
379 /* a x + b y = c */
380 a = -(L1.y - L0.y);
381 b = L1.x - L0.x;
382 c = a * L0.x + b * L0.y;
383
384 s0 = a * p0.x + b * p0.y - c >= 0;
385 s1 = a * p1.x + b * p1.y - c >= 0;
386 return s0 == s1;
387}
388
389static
390char* penColor(GVJ_t * job, node_t * n)
391{
392 char *color;
393
394 color = late_nnstring(n, N_color, "");
395 if (!color[0])
398 return color;
399}
400
401static
402char *findFillDflt(node_t * n, char *dflt)
403{
404 char *color;
405
407 if (!color[0]) {
408 /* for backward compatibility, default fill is same as pen */
409 color = late_nnstring(n, N_color, "");
410 if (!color[0]) {
411 color = dflt;
412 }
413 }
414 return color;
415}
416
417static
418char *findFill(node_t * n)
419{
420 return findFillDflt(n, DEFAULT_FILL);
421}
422
423static bool isBox(node_t *n) {
424 polygon_t *p;
425
426 if ((p = ND_shape(n)->polygon)) {
427 return p->sides == 4 && fabs(fmod(p->orientation, 90)) < 0.5 &&
429 }
430 return false;
431}
432
433static bool isEllipse(node_t *n) {
434 polygon_t *p;
435
436 if ((p = ND_shape(n)->polygon)) {
437 return p->sides <= 2;
438 }
439 return false;
440}
441
445
446 // bitwise-or-ing the shape does not make sense, so there better only be one
447 assert(a.shape == 0 || b.shape == 0);
448
450 .filled = a.filled || b.filled,
451 .radial = a.radial || b.radial,
452 .rounded = a.rounded || b.rounded,
453 .diagonals = a.diagonals || b.diagonals,
454 .auxlabels = a.auxlabels || b.auxlabels,
455 .invisible = a.invisible || b.invisible,
456 .striped = a.striped || b.striped,
457 .dotted = a.dotted || b.dotted,
458 .dashed = a.dashed || b.dashed,
459 .wedged = a.wedged || b.wedged,
460 .underline = a.underline || b.underline,
461 .fixedshape = a.fixedshape || b.fixedshape,
462 .shape = a.shape | b.shape,
463 };
464}
465
466static char **checkStyle(node_t *n, graphviz_polygon_style_t *flagp) {
467 char *style;
468 char **pstyle = 0;
469 graphviz_polygon_style_t istyle = {0};
471
472 style = late_nnstring(n, N_style, "");
473 if (style[0]) {
474 char **pp;
475 char **qp;
476 char *p;
477 pp = pstyle = parse_style(style);
478 while ((p = *pp)) {
479 if (streq(p, "filled")) {
480 istyle.filled = true;
481 pp++;
482 } else if (streq(p, "rounded")) {
483 istyle.rounded = true;
484 qp = pp; /* remove rounded from list passed to renderer */
485 do {
486 qp++;
487 *(qp - 1) = *qp;
488 } while (*qp);
489 } else if (streq(p, "diagonals")) {
490 istyle.diagonals = true;
491 qp = pp; /* remove diagonals from list passed to renderer */
492 do {
493 qp++;
494 *(qp - 1) = *qp;
495 } while (*qp);
496 } else if (streq(p, "invis")) {
497 istyle.invisible = true;
498 pp++;
499 } else if (streq(p, "radial")) {
500 istyle.radial = true;
501 istyle.filled = true;
502 qp = pp; /* remove radial from list passed to renderer */
503 do {
504 qp++;
505 *(qp - 1) = *qp;
506 } while (*qp);
507 } else if (streq(p, "striped") && isBox(n)) {
508 istyle.striped = true;
509 qp = pp; /* remove striped from list passed to renderer */
510 do {
511 qp++;
512 *(qp - 1) = *qp;
513 } while (*qp);
514 } else if (streq(p, "wedged") && isEllipse(n)) {
515 istyle.wedged = true;
516 qp = pp; /* remove wedged from list passed to renderer */
517 do {
518 qp++;
519 *(qp - 1) = *qp;
520 } while (*qp);
521 } else
522 pp++;
523 }
524 }
525 if ((poly = ND_shape(n)->polygon))
526 istyle = style_or(istyle, poly->option);
527
528 *flagp = istyle;
529 return pstyle;
530}
531
533 char **pstyle, *s;
534 graphviz_polygon_style_t istyle = {0};
535 double penwidth;
536
537 if ((pstyle = checkStyle(n, &istyle)))
538 gvrender_set_style(job, pstyle);
539
540 if (N_penwidth && (s = agxget(n, N_penwidth)) && s[0]) {
541 penwidth = late_double(n, N_penwidth, 1.0, 0.0);
543 }
544
545 return istyle;
546}
547
548static void Mcircle_hack(GVJ_t * job, node_t * n)
549{
550 double x, y;
551 pointf AF[2], p;
552
553 y = .7500;
554 x = .6614; /* x^2 + y^2 = 1.0 */
555 p.y = y * ND_ht(n) / 2.0;
556 p.x = ND_rw(n) * x; /* assume node is symmetric */
557
558 AF[0] = add_pointf(p, ND_coord(n));
559 AF[1].y = AF[0].y;
560 AF[1].x = AF[0].x - 2 * p.x;
561 gvrender_polyline(job, AF, 2);
562 AF[0].y -= 2 * p.y;
563 AF[1].y = AF[0].y;
564 gvrender_polyline(job, AF, 2);
565}
566
567static pointf * alloc_interpolation_points(pointf *AF, size_t sides,
568 graphviz_polygon_style_t style, bool rounded)
569{
570 pointf *B = gv_calloc(4 * sides + 4, sizeof(pointf));
571 size_t i = 0;
572 pointf p0, p1;
573 double dx, dy, t;
574 /* rbconst is distance offset from a corner of the polygon.
575 * It should be the same for every corner, and also never
576 * bigger than one-third the length of a side.
577 */
578 double rbconst = RBCONST;
579 for (size_t seg = 0; seg < sides; seg++) {
580 p0 = AF[seg];
581 if (seg + 1 < sides)
582 p1 = AF[seg + 1];
583 else
584 p1 = AF[0];
585 dx = p1.x - p0.x;
586 dy = p1.y - p0.y;
587 const double d = hypot(dx, dy);
588 rbconst = fmin(rbconst, d / 3.0);
589 }
590 for (size_t seg = 0; seg < sides; seg++) {
591 p0 = AF[seg];
592 if (seg + 1 < sides)
593 p1 = AF[seg + 1];
594 else
595 p1 = AF[0];
596 dx = p1.x - p0.x;
597 dy = p1.y - p0.y;
598 const double d = hypot(dx, dy);
599 t = rbconst / d;
600 if (style.shape == BOX3D || style.shape == COMPONENT)
601 t /= 3;
602 else if (style.shape == DOGEAR)
603 t /= 2;
604 if (!rounded)
605 B[i++] = p0;
606 else
607 B[i++] = interpolate_pointf(RBCURVE * t, p0, p1);
608 B[i++] = interpolate_pointf(t, p0, p1);
609 B[i++] = interpolate_pointf(1.0 - t, p0, p1);
610 if (rounded)
611 B[i++] = interpolate_pointf(1.0 - RBCURVE * t, p0, p1);
612 }
613 B[i++] = B[0];
614 B[i++] = B[1];
615 B[i++] = B[2];
616
617 return B;
618}
619
625static void diagonals_draw(GVJ_t *job, pointf *AF, size_t sides,
626 graphviz_polygon_style_t style, int filled)
627{
628 pointf *B = alloc_interpolation_points(AF, sides, style, false);
629 gvrender_polygon(job, AF, sides, filled);
630
631 for (size_t seg = 0; seg < sides; seg++) {
632 pointf C[] = {B[3 * seg + 2], B[3 * seg + 4]};
633 gvrender_polyline(job, C, 2);
634 }
635 free(B);
636}
637
644static void rounded_draw(GVJ_t *job, pointf *AF, size_t sides,
645 graphviz_polygon_style_t style, int filled)
646{
647 size_t i = 0;
648
649 pointf *B = alloc_interpolation_points(AF, sides, style, true);
650 pointf *pts = gv_calloc(6 * sides + 2, sizeof(pointf));
651 for (size_t seg = 0; seg < sides; seg++) {
652 pts[i++] = B[4 * seg];
653 pts[i++] = B[4 * seg + 1];
654 pts[i++] = B[4 * seg + 1];
655 pts[i++] = B[4 * seg + 2];
656 pts[i++] = B[4 * seg + 2];
657 pts[i++] = B[4 * seg + 3];
658 }
659 pts[i++] = pts[0];
660 pts[i++] = pts[1];
661 gvrender_beziercurve(job, pts + 1, i - 1, filled);
662 free(pts);
663 free(B);
664}
665
688static double mid_x(const pointf line[2]) {
689 return (line[0].x + line[1].x) / 2;
690}
691
697static double mid_y(const pointf line[2]) {
698 return (line[0].y + line[1].y) / 2;
699}
700
710void round_corners(GVJ_t *job, pointf *AF, size_t sides,
711 graphviz_polygon_style_t style, int filled) {
712 assert(job != NULL);
713 assert(AF != NULL);
714 assert(sides > 0);
715 assert(memcmp(&style, &(graphviz_polygon_style_t){0}, sizeof(style)) != 0);
716
717 pointf *B, C[5], *D;
718
719 struct {
720 unsigned shape: 7;
721 } mode = {0};
722
723 if (style.diagonals) {
724 diagonals_draw(job, AF, sides, style, filled);
725 return;
726 } else if (style.shape != 0) {
727 mode.shape = style.shape;
728 } else if (style.rounded) {
729 rounded_draw(job, AF, sides, style, filled);
730 return;
731 } else {
732 UNREACHABLE();
733 }
734
735 if (mode.shape == CYLINDER) {
736 cylinder_draw(job, AF, sides, filled);
737 return;
738 }
739 B = alloc_interpolation_points(AF, sides, style, false);
740 switch (mode.shape) {
741 case DOGEAR:
742 /* Add the cutoff edge. */
743 D = gv_calloc(sides + 1, sizeof(pointf));
744 for (size_t seg = 1; seg < sides; seg++)
745 D[seg] = AF[seg];
746 D[0] = B[3 * (sides - 1) + 4];
747 D[sides] = B[3 * (sides - 1) + 2];
748 gvrender_polygon(job, D, sides + 1, filled);
749 free(D);
750
751 /* Draw the inner edge. */
752 const size_t sseg = sides - 1;
753 C[0] = B[3 * sseg + 2];
754 C[1] = B[3 * sseg + 4];
755 C[2].x = C[1].x + (C[0].x - B[3 * sseg + 3].x);
756 C[2].y = C[1].y + (C[0].y - B[3 * sseg + 3].y);
757 gvrender_polyline(job, C + 1, 2);
758 C[1] = C[2];
759 gvrender_polyline(job, C, 2);
760 break;
761 case TAB:
762 /*
763 * Adjust the perimeter for the protrusions.
764 *
765 * D[3] ×──× D[2]
766 * │ │ B[1]
767 * B[3] ×──×──────────×──× AF[0]=B[0]=D[0]
768 * │ B[2]=D[1] │
769 * B[4] × │
770 * │ │
771 * B[5] × │
772 * └────────────────┘
773 *
774 */
775 /* Add the tab edges. */
776 D = gv_calloc(sides + 2, sizeof(pointf));
777 D[0] = AF[0];
778 D[1] = B[2];
779 D[2].x = B[2].x + (B[3].x - B[4].x) / 3;
780 D[2].y = B[2].y + (B[3].y - B[4].y) / 3;
781 D[3].x = B[3].x + (B[3].x - B[4].x) / 3;
782 D[3].y = B[3].y + (B[3].y - B[4].y) / 3;
783 for (size_t seg = 4; seg < sides + 2; seg++)
784 D[seg] = AF[seg - 2];
785 gvrender_polygon(job, D, sides + 2, filled);
786 free(D);
787
788
789 /* Draw the inner edge. */
790 C[0] = B[3];
791 C[1] = B[2];
792 gvrender_polyline(job, C, 2);
793 break;
794 case FOLDER:
795 /*
796 * Adjust the perimeter for the protrusions.
797 *
798 * D[2] ×────× D[1]
799 * B[3]= ╱ ╲
800 * D[4] ×──×────× × × AF[0]=B[0]=D[0]
801 * │ B[2] D[3] B[1]│
802 * B[4] × │
803 * │ │
804 * B[5] × │
805 * └────────────────┘
806 *
807 */
808 /* Add the folder edges. */
809 D = gv_calloc(sides + 3, sizeof(pointf));
810 D[0] = AF[0];
811 D[1].x = AF[0].x - (AF[0].x - B[1].x) / 4;
812 D[1].y = AF[0].y + (B[3].y - B[4].y) / 3;
813 D[2].x = AF[0].x - 2 * (AF[0].x - B[1].x);
814 D[2].y = D[1].y;
815 D[3].x = AF[0].x - 2.25 * (AF[0].x - B[1].x);
816 D[3].y = B[3].y;
817 D[4].x = B[3].x;
818 D[4].y = B[3].y;
819 for (size_t seg = 4; seg < sides + 3; seg++)
820 D[seg] = AF[seg - 3];
821 gvrender_polygon(job, D, sides + 3, filled);
822 free(D);
823 break;
824 case BOX3D:
825 assert(sides == 4);
826 /* Adjust for the cutoff edges. */
827 D = gv_calloc(sides + 2, sizeof(pointf));
828 D[0] = AF[0];
829 D[1] = B[2];
830 D[2] = B[4];
831 D[3] = AF[2];
832 D[4] = B[8];
833 D[5] = B[10];
834 gvrender_polygon(job, D, sides + 2, filled);
835 free(D);
836
837 /* Draw the inner vertices. */
838 C[0].x = B[1].x + (B[11].x - B[0].x);
839 C[0].y = B[1].y + (B[11].y - B[0].y);
840 C[1] = B[4];
841 gvrender_polyline(job, C, 2);
842 C[1] = B[8];
843 gvrender_polyline(job, C, 2);
844 C[1] = B[0];
845 gvrender_polyline(job, C, 2);
846 break;
847 case COMPONENT:
848 assert(sides == 4);
849 /*
850 * Adjust the perimeter for the protrusions.
851 *
852 * D[1] ×────────────────× D[0]
853 * │ │
854 * 3×───×2──┐ │
855 * │ │ │
856 * 4×───×5──┘ │
857 * │ │
858 * 7×───×6──┐ │
859 * │ │ │
860 * 8×───×9──┘ │
861 * │ │
862 * 10×────────────────× D[11]
863 *
864 */
865 D = gv_calloc(sides + 8, sizeof(pointf));
866 D[0] = AF[0];
867 D[1] = AF[1];
868 D[2].x = B[3].x + (B[4].x - B[3].x);
869 D[2].y = B[3].y + (B[4].y - B[3].y);
870 D[3].x = D[2].x + (B[3].x - B[2].x);
871 D[3].y = D[2].y + (B[3].y - B[2].y);
872 D[4].x = D[3].x + (B[4].x - B[3].x);
873 D[4].y = D[3].y + (B[4].y - B[3].y);
874 D[5].x = D[4].x + (D[2].x - D[3].x);
875 D[5].y = D[4].y + (D[2].y - D[3].y);
876
877 D[9].x = B[6].x + (B[5].x - B[6].x);
878 D[9].y = B[6].y + (B[5].y - B[6].y);
879 D[8].x = D[9].x + (B[6].x - B[7].x);
880 D[8].y = D[9].y + (B[6].y - B[7].y);
881 D[7].x = D[8].x + (B[5].x - B[6].x);
882 D[7].y = D[8].y + (B[5].y - B[6].y);
883 D[6].x = D[7].x + (D[9].x - D[8].x);
884 D[6].y = D[7].y + (D[9].y - D[8].y);
885
886 D[10] = AF[2];
887 D[11] = AF[3];
888 gvrender_polygon(job, D, sides + 8, filled);
889
890 /* Draw the internal vertices. */
891 C[0] = D[2];
892 C[1].x = D[2].x - (D[3].x - D[2].x);
893 C[1].y = D[2].y - (D[3].y - D[2].y);
894 C[2].x = C[1].x + (D[4].x - D[3].x);
895 C[2].y = C[1].y + (D[4].y - D[3].y);
896 C[3] = D[5];
897 gvrender_polyline(job, C, 4);
898 C[0] = D[6];
899 C[1].x = D[6].x - (D[7].x - D[6].x);
900 C[1].y = D[6].y - (D[7].y - D[6].y);
901 C[2].x = C[1].x + (D[8].x - D[7].x);
902 C[2].y = C[1].y + (D[8].y - D[7].y);
903 C[3] = D[9];
904 gvrender_polyline(job, C, 4);
905
906 free(D);
907 break;
908
909 case PROMOTER:
910 /*
911 * L-shaped arrow on a center line, scales in the x direction
912 *
913 *
914 * D[1] │╲
915 * ×────────────────× ╲
916 * │ D[0] ╲
917 * │ ╲
918 * │ ╱
919 * │ D[5] ╱
920 * │ ┌───────× ╱
921 * │ │ │╱
922 * ─────┴────────┴─────────────
923 */
924 /* Add the tab edges. */
925
926 //the arrow's thickness is (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
927 // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
928 // in the y with label length
929 D = gv_calloc(sides + 5, sizeof(pointf));
930 D[0].x = mid_x(AF) + (AF[0].x - AF[1].x)/8; //x_center + width
931 D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)*3/2; //D[4].y + width
932 D[1].x = mid_x(AF) - (AF[0].x - AF[1].x)/4; //x_center - 2*width
933 D[1].y = D[0].y;
934 D[2].x = D[1].x;
935 D[2].y = mid_y(&AF[1]);
936 D[3].x = D[2].x + (B[2].x - B[3].x)/2; //D[2].x + width
937 D[3].y = mid_y(&AF[1]);
938 D[4].x = D[3].x;
939 D[4].y = mid_y(&AF[1]) + (B[3].y-B[4].y); //highest cds point
940 D[5].x = D[0].x;
941 D[5].y = D[4].y; //highest cds point
942 D[6].x = D[0].x;
943 D[6].y = D[4].y - (B[3].y-B[4].y)/4; //D[4].y - width/2
944 D[7].x = D[6].x + (B[2].x - B[3].x); //D[6].x + 2*width
945 D[7].y = D[6].y + (B[3].y - B[4].y)/2; //D[6].y + width
946 D[8].x = D[0].x;
947 D[8].y = D[0].y + (B[3].y - B[4].y)/4;//D[0].y + width/2
948 gvrender_polygon(job, D, sides + 5, filled);
949
950 /*dsDNA line*/
951 C[0].x = AF[1].x;
952 C[0].y = mid_y(&AF[1]);
953 C[1].x = AF[0].x;
954 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
955 gvrender_polyline(job, C, 2);
956 free(D);
957
958 break;
959
960 case CDS:
961 /*
962 * arrow without the protrusions, scales normally
963 *
964 *
965 * D[1] = AF[1]
966 * ×────────────────×╲
967 * │ D[0]╲
968 * │ ╲
969 * │ ╱
970 * │ ╱
971 * ×────────────────×╱
972 * D[3]
973 *
974 */
975 D = gv_calloc(sides + 1, sizeof(pointf));
976 D[0].x = B[1].x;
977 D[0].y = B[1].y - (B[3].y - B[4].y)/2;
978 D[1].x = B[3].x;
979 D[1].y = B[3].y - (B[3].y - B[4].y)/2;
980 D[2].x = AF[2].x;
981 D[2].y = AF[2].y + (B[3].y - B[4].y)/2;
982 D[3].x = B[1].x;
983 D[3].y = AF[2].y + (B[3].y - B[4].y)/2;
984 D[4].y = AF[0].y - (AF[0].y - AF[3].y)/2;
985 D[4].x = AF[0].x;
986
987 gvrender_polygon(job, D, sides + 1, filled);
988 free(D);
989
990 break;
991
992 case TERMINATOR:
993 /*
994 * T-shape, does not scale, always in the center
995 *
996 *
997 * D[4]
998 * ×────────────────×
999 * │ D[3]
1000 * │ │
1001 * │ │
1002 * │ D[6] D[1] │
1003 * D[5]×───× ×────× D[2]
1004 * │ │
1005 * ─────────┴───────×─D[0]──────
1006 */
1007 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1008 D = gv_calloc(sides + 4, sizeof(pointf));
1009 D[0].x = mid_x(AF) + (B[2].x-B[3].x)/4; //x_center + width/2
1010 D[0].y = mid_y(&AF[1]);
1011 D[1].x = D[0].x;
1012 D[1].y = D[0].y + (B[3].y-B[4].y)/2;
1013 D[2].x = D[1].x + (B[2].x-B[3].x)/2;
1014 D[2].y = D[1].y;
1015 D[3].x = D[2].x;
1016 D[3].y = D[2].y + (B[3].y-B[4].y)/2;
1017 D[4].x = mid_x(AF) - (B[2].x-B[3].x)*3/4; //D[3].y mirrored across the center
1018 D[4].y = D[3].y;
1019 D[5].x = D[4].x;
1020 D[5].y = D[2].y;
1021 D[6].x = mid_x(AF) - (B[2].x-B[3].x)/4; //D[1].x mirrored across the center
1022 D[6].y = D[1].y;
1023 D[7].x = D[6].x;
1024 D[7].y = D[0].y;
1025 gvrender_polygon(job, D, sides + 4, filled);
1026
1027 /*dsDNA line*/
1028 C[0].x = AF[1].x;
1029 C[0].y = mid_y(&AF[1]);
1030 C[1].x = AF[0].x;
1031 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1032 gvrender_polyline(job, C, 2);
1033 free(D);
1034
1035 break;
1036
1037 case UTR:
1038 /*
1039 * half-octagon with line, does not scale, always in center
1040 *
1041 * D[3]
1042 * ───── D[2]
1043 * ╱ ╲
1044 * ╱ ╲ D[1]
1045 * │ │
1046 * ─────┴───────┴───────
1047 * D[0]
1048 *
1049 *
1050 *
1051 */
1052 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1053 D = gv_calloc(sides + 2, sizeof(pointf));
1054 D[0].x = mid_x(AF) + (B[2].x-B[3].x)*3/4; //x_center+width
1055 D[0].y = mid_y(&AF[1]);
1056 D[1].x = D[0].x;
1057 D[1].y = D[0].y + (B[3].y-B[4].y)/4; //D[0].y+width/2
1058 D[2].x = mid_x(AF) + (B[2].x-B[3].x)/4; //x_center+width/2
1059 D[2].y = D[1].y + (B[3].y-B[4].y)/2; //D[1].y+width
1060 D[3].x = mid_x(AF) - (B[2].x-B[3].x)/4; //D[2].x mirrored across the center
1061 D[3].y = D[2].y;
1062 D[4].x = mid_x(AF) - (B[2].x-B[3].x)*3/4;
1063 D[4].y = D[1].y;
1064 D[5].x = D[4].x;
1065 D[5].y = D[0].y;
1066 gvrender_polygon(job, D, sides + 2, filled);
1067
1068 /*dsDNA line*/
1069 C[0].x = AF[1].x;
1070 C[0].y = mid_y(&AF[1]);
1071 C[1].x = AF[0].x;
1072 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1073 gvrender_polyline(job, C, 2);
1074 free(D);
1075
1076 break;
1077 case PRIMERSITE:
1078 /*
1079 * half arrow shape, scales in the x-direction
1080 * D[1]
1081 * │╲
1082 * │ ╲
1083 * │ ╲
1084 * ┌───────────┘ ╲
1085 * │ ╲
1086 * └─────────────────╲ D[0]
1087 *
1088 * ────────────────────────────────
1089 *
1090 */
1091 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1092 // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
1093 // in the y with label length
1094 D = gv_calloc(sides + 1, sizeof(pointf));
1095 D[0].x = mid_x(AF) + (B[2].x-B[3].x);//x_center + width*2
1096 D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/4;//y_center + 1/2 width
1097 D[1].x = D[0].x - (B[2].x-B[3].x); //x_center
1098 D[1].y = D[0].y + (B[3].y-B[4].y);
1099 D[2].x = D[1].x;
1100 D[2].y = D[0].y + (B[3].y-B[4].y)/2;
1101 D[3].x = mid_x(AF) - (AF[0].x - AF[1].x)/4;//x_center - 2*(scalable width)
1102 D[3].y = D[2].y;
1103 D[4].x = D[3].x;
1104 D[4].y = D[0].y;
1105 gvrender_polygon(job, D, sides + 1, filled);
1106
1107 /*dsDNA line*/
1108 C[0].x = AF[1].x;
1109 C[0].y = mid_y(&AF[1]);
1110 C[1].x = AF[0].x;
1111 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1112 gvrender_polyline(job, C, 2);
1113 free(D);
1114
1115 break;
1116 case RESTRICTIONSITE:
1117 /*
1118 * zigzag shape, scales in the x-direction (only the middle section)
1119 *
1120 *
1121 * ┌───D[2]
1122 * │ └──────── D[0]
1123 * ────┤ ├────
1124 * └────────┐ │
1125 * D[4] └─── D[7]
1126 *
1127 *
1128 *
1129 */
1130 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1131 // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
1132 // in the y with label length
1133 D = gv_calloc(sides + 4, sizeof(pointf));
1134 D[0].x = mid_x(AF) + (AF[0].x - AF[1].x)/8 + (B[2].x-B[3].x)/2;//x_center + scalable_width + width
1135 D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/4;//y_center + 1/2 width
1136 D[1].x = mid_x(AF) - (AF[0].x - AF[1].x)/8; //x_center - width
1137 D[1].y = D[0].y;
1138 D[2].x = D[1].x;
1139 D[2].y = D[1].y + (B[3].y-B[4].y)/2;
1140 D[3].x = D[2].x - (B[2].x-B[3].x)/2; //D[2].x - width
1141 D[3].y = D[2].y;
1142 D[4].x = D[3].x;
1143 D[4].y = mid_y(&AF[1]) - (B[3].y-B[4].y)/4; //y_center - 1/2(width)
1144 D[5].x = D[0].x - (B[2].x-B[3].x)/2;
1145 D[5].y = D[4].y;
1146 D[6].x = D[5].x;
1147 D[6].y = D[5].y - (B[3].y-B[4].y)/2;
1148 D[7].x = D[0].x;
1149 D[7].y = D[6].y;
1150 gvrender_polygon(job, D, sides + 4, filled);
1151
1152 /*dsDNA line left half*/
1153 C[0].x = AF[1].x;
1154 C[0].y = mid_y(&AF[1]);
1155 C[1].x = D[4].x;
1156 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1157 gvrender_polyline(job, C, 2);
1158
1159 /*dsDNA line right half*/
1160 C[0].x = D[7].x;
1161 C[0].y = mid_y(&AF[1]);
1162 C[1].x = AF[0].x;
1163 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1164 gvrender_polyline(job, C, 2);
1165 free(D);
1166
1167 break;
1168 case FIVEPOVERHANG:
1169 /*
1170 * does not scale, on the left side
1171 *
1172 * D[3]──────D[2]
1173 * │ │
1174 * D[0]──────D[1]
1175 * ┌────┐ ────────────
1176 * │ │
1177 * D[0]──D[1]
1178 *
1179 *
1180 *
1181 */
1182 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1183 // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
1184 // in the y with label length
1185 D = gv_calloc(sides, sizeof(pointf));
1186 D[0].x = AF[1].x;//the very left edge
1187 D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/8;//y_center + 1/4 width
1188 D[1].x = D[0].x + 2*(B[2].x-B[3].x);
1189 D[1].y = D[0].y;
1190 D[2].x = D[1].x;
1191 D[2].y = D[1].y + (B[3].y-B[4].y)/2;
1192 D[3].x = D[0].x;
1193 D[3].y = D[2].y;
1194 gvrender_polygon(job, D, sides, filled);
1195
1196 /*second, lower shape*/
1197 free(D);
1198 D = gv_calloc(sides, sizeof(pointf));
1199 D[0].x = AF[1].x + (B[2].x-B[3].x);
1200 D[0].y = mid_y(&AF[1]) - (B[3].y-B[4].y)*5/8; //y_center - 5/4 width
1201 D[1].x = D[0].x + (B[2].x-B[3].x);
1202 D[1].y = D[0].y;
1203 D[2].x = D[1].x;
1204 D[2].y = D[1].y + (B[3].y-B[4].y)/2;
1205 D[3].x = D[0].x;
1206 D[3].y = D[2].y;
1207 gvrender_polygon(job, D, sides, filled);
1208
1209 /*dsDNA line right half*/
1210 C[0].x = D[1].x;
1211 C[0].y = mid_y(&AF[1]);
1212 C[1].x = AF[0].x;
1213 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1214 gvrender_polyline(job, C, 2);
1215 free(D);
1216
1217 break;
1218 case THREEPOVERHANG:
1219 /*
1220 * does not scale, on the right side
1221 *
1222 * D[2]──────D[1]
1223 * │ │
1224 * ─────── D[3]──────D[0]
1225 * ┌────┐ D[1]
1226 * │ │
1227 * D[3]──D[0]
1228 *
1229 *
1230 *
1231 */
1232 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1233 // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
1234 // in the y with label length
1235 D = gv_calloc(sides, sizeof(pointf));
1236 D[0].x = AF[0].x;//the very right edge
1237 D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/8;//y_center + 1/4 width
1238 D[1].x = D[0].x;
1239 D[1].y = D[0].y + (B[3].y-B[4].y)/2;
1240 D[2].x = D[1].x - 2*(B[3].y-B[4].y);
1241 D[2].y = D[1].y;
1242 D[3].x = D[2].x;
1243 D[3].y = D[0].y;
1244 gvrender_polygon(job, D, sides, filled);
1245
1246 /*second, lower shape*/
1247 free(D);
1248 D = gv_calloc(sides, sizeof(pointf));
1249 D[0].x = AF[0].x - (B[2].x-B[3].x);
1250 D[0].y = mid_y(&AF[1]) - (B[3].y-B[4].y)*5/8; //y_center - 5/4 width
1251 D[1].x = D[0].x;
1252 D[1].y = D[0].y + (B[3].y-B[4].y)/2;
1253 D[2].x = D[1].x - (B[3].y-B[4].y);
1254 D[2].y = D[1].y;
1255 D[3].x = D[2].x;
1256 D[3].y = D[0].y;
1257 gvrender_polygon(job, D, sides, filled);
1258
1259 /*dsDNA line left half*/
1260 C[0].x = AF[1].x;
1261 C[0].y = mid_y(&AF[1]);
1262 C[1].x = D[3].x;
1263 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1264 gvrender_polyline(job, C, 2);
1265 free(D);
1266
1267 break;
1268 case NOVERHANG:
1269 /*
1270 * does not scale
1271 *
1272 * D[3]──────D[2] D[3]──────D[2]
1273 * │ │ │ │
1274 * ───D[0]──────D[1] D[0]──────D[1]────
1275 * D[3]──────D[2] D[3]──────D[2]
1276 * │ │ │ │
1277 * D[0]──────D[1] D[0]──────D[1]
1278 *
1279 *
1280 *
1281 *
1282 */
1283 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1284 // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
1285 // in the y with label length
1286 /*upper left rectangle*/
1287 D = gv_calloc(sides, sizeof(pointf));
1288 D[0].x = mid_x(AF) - (B[2].x-B[3].x)*9/8; //x_center - 2*width - 1/4*width
1289 D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/8;//y_center + 1/4 width
1290 D[1].x = D[0].x + (B[2].x-B[3].x);
1291 D[1].y = D[0].y;
1292 D[2].x = D[1].x;
1293 D[2].y = D[1].y + (B[3].y-B[4].y)/2;
1294 D[3].x = D[0].x;
1295 D[3].y = D[2].y;
1296 gvrender_polygon(job, D, sides, filled);
1297
1298 /*lower, left rectangle*/
1299 free(D);
1300 D = gv_calloc(sides, sizeof(pointf));
1301 D[0].x = mid_x(AF) - (B[2].x-B[3].x)*9/8; //x_center - 2*width - 1/4*width
1302 D[0].y = mid_y(&AF[1]) - (B[3].y-B[4].y)*5/8;//y_center - width - 1/4 width
1303 D[1].x = D[0].x + (B[2].x-B[3].x);
1304 D[1].y = D[0].y;
1305 D[2].x = D[1].x;
1306 D[2].y = D[1].y + (B[3].y-B[4].y)/2;
1307 D[3].x = D[0].x;
1308 D[3].y = D[2].y;
1309 gvrender_polygon(job, D, sides, filled);
1310
1311 /*lower, right rectangle*/
1312 free(D);
1313 D = gv_calloc(sides, sizeof(pointf));
1314 D[0].x = mid_x(AF) + (B[2].x-B[3].x)/8; //x_center + 1/4*width
1315 D[0].y = mid_y(&AF[1]) - (B[3].y-B[4].y)*5/8;//y_center - width - 1/4 width
1316 D[1].x = D[0].x + (B[2].x-B[3].x);
1317 D[1].y = D[0].y;
1318 D[2].x = D[1].x;
1319 D[2].y = D[1].y + (B[3].y-B[4].y)/2;
1320 D[3].x = D[0].x;
1321 D[3].y = D[2].y;
1322 gvrender_polygon(job, D, sides, filled);
1323
1324 /*upper, right rectangle*/
1325 free(D);
1326 D = gv_calloc(sides, sizeof(pointf));
1327 D[0].x = mid_x(AF) + (B[2].x-B[3].x)/8; //x_center + 1/4*width
1328 D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/8;//y_center - width - 1/4 width
1329 D[1].x = D[0].x + (B[2].x-B[3].x);
1330 D[1].y = D[0].y;
1331 D[2].x = D[1].x;
1332 D[2].y = D[1].y + (B[3].y-B[4].y)/2;
1333 D[3].x = D[0].x;
1334 D[3].y = D[2].y;
1335 gvrender_polygon(job, D, sides, filled);
1336
1337 /*dsDNA line right half*/
1338 C[0].x = D[1].x;
1339 C[0].y = mid_y(&AF[1]);
1340 C[1].x = AF[0].x;
1341 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1342 gvrender_polyline(job, C, 2);
1343
1344 /*dsDNA line left half*/
1345 C[0].x = mid_x(AF) - (B[2].x-B[3].x)*9/8; //D[0].x of of the left rectangles
1346 C[0].y = mid_y(&AF[1]);
1347 C[1].x = AF[1].x;
1348 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1349 gvrender_polyline(job, C, 2);
1350 free(D);
1351
1352 break;
1353 case ASSEMBLY:
1354 /*
1355 * does not scale
1356 *
1357 * D[3]──────────D[2]
1358 * │ │
1359 * D[0]──────────D[1]
1360 * ──── ─────────
1361 * D[3]──────────D[2]
1362 * │ │
1363 * D[0]──────────D[1]
1364 *
1365 */
1366 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1367 // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
1368 // in the y with label length
1369 D = gv_calloc(sides, sizeof(pointf));
1370 D[0].x = mid_x(AF) - (B[2].x-B[3].x); //x_center - 2*width
1371 D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/8;//y_center + 1/4 width
1372 D[1].x = D[0].x + 2*(B[2].x-B[3].x);
1373 D[1].y = D[0].y;
1374 D[2].x = D[1].x;
1375 D[2].y = D[1].y + (B[3].y-B[4].y)/2;
1376 D[3].x = D[0].x;
1377 D[3].y = D[2].y;
1378 gvrender_polygon(job, D, sides, filled);
1379
1380 /*second, lower shape*/
1381 free(D);
1382 D = gv_calloc(sides, sizeof(pointf));
1383 D[0].x = mid_x(AF) - (B[2].x-B[3].x); //x_center - 2*width
1384 D[0].y = mid_y(&AF[1]) - (B[3].y-B[4].y)*5/8;//y_center - width - 1/4 width
1385 D[1].x = D[0].x + 2*(B[2].x-B[3].x);
1386 D[1].y = D[0].y;
1387 D[2].x = D[1].x;
1388 D[2].y = D[1].y + (B[3].y-B[4].y)/2;
1389 D[3].x = D[0].x;
1390 D[3].y = D[2].y;
1391 gvrender_polygon(job, D, sides, filled);
1392
1393 /*dsDNA line right half*/
1394 C[0].x = D[1].x;
1395 C[0].y = mid_y(&AF[1]);
1396 C[1].x = AF[0].x;
1397 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1398 gvrender_polyline(job, C, 2);
1399
1400 /*dsDNA line left half*/
1401 C[0].x = AF[1].x;
1402 C[0].y = mid_y(&AF[1]);
1403 C[1].x = D[0].x;
1404 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1405 gvrender_polyline(job, C, 2);
1406 free(D);
1407
1408 break;
1409 case SIGNATURE:
1410 /*
1411 *
1412 *
1413 * ┌──────────────┐
1414 * │ │
1415 * │x │
1416 * │_____________ │
1417 * └──────────────┘
1418 */
1419 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1420 // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
1421 // in the y with label length
1422 D = gv_calloc(sides, sizeof(pointf));
1423 D[0].x = AF[0].x;
1424 D[0].y = B[1].y - (B[3].y - B[4].y)/2;
1425 D[1].x = B[3].x;
1426 D[1].y = B[3].y - (B[3].y - B[4].y)/2;
1427 D[2].x = AF[2].x;
1428 D[2].y = AF[2].y + (B[3].y - B[4].y)/2;
1429 D[3].x = AF[0].x;
1430 D[3].y = AF[2].y + (B[3].y - B[4].y)/2;
1431 gvrender_polygon(job, D, sides, filled);
1432
1433 /* "\" of the X*/
1434 C[0].x = AF[1].x + (B[2].x-B[3].x)/4;
1435 C[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/8; //y_center + 1/4 width
1436 C[1].x = C[0].x + (B[2].x-B[3].x)/4;//C[0].x + width/2
1437 C[1].y = C[0].y - (B[3].y-B[4].y)/4;//C[0].y - width/2
1438 gvrender_polyline(job, C, 2);
1439
1440 /*"/" of the X*/
1441 C[0].x = AF[1].x + (B[2].x-B[3].x)/4;
1442 C[0].y = mid_y(&AF[1]) - (B[3].y-B[4].y)/8; //y_center - 1/4 width
1443 C[1].x = C[0].x + (B[2].x-B[3].x)/4;//C[0].x + width/2
1444 C[1].y = C[0].y + (B[3].y-B[4].y)/4;//C[0].y + width/2
1445 gvrender_polyline(job, C, 2);
1446
1447 /*bottom line*/
1448 C[0].x = AF[1].x + (B[2].x-B[3].x)/4;
1449 C[0].y = AF[2].y + (B[3].y-B[4].y)*3/4;
1450 C[1].x = AF[0].x - (B[2].x-B[3].x)/4;
1451 C[1].y = C[0].y;
1452 gvrender_polyline(job, C, 2);
1453 free(D);
1454
1455 break;
1456 case INSULATOR:
1457 /*
1458 * double square
1459 *
1460 * ┌─────┐
1461 *──┤ ┌─┐ ├───
1462 * │ └─┘ │
1463 * └─────┘
1464 *
1465 */
1466 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1467 D = gv_calloc(sides, sizeof(pointf));
1468 D[0].x = mid_x(AF) + (B[2].x-B[3].x)/2; //x_center+width
1469 D[0].y = mid_y(&AF[1]) + (B[2].x-B[3].x)/2;
1470 D[1].x = D[0].x;
1471 D[1].y = mid_y(&AF[1]) - (B[2].x-B[3].x)/2; //D[0].y- width
1472 D[2].x = mid_x(AF) - (B[2].x-B[3].x)/2; //x_center-width
1473 D[2].y = D[1].y;
1474 D[3].x = D[2].x;
1475 D[3].y = D[0].y;
1476 gvrender_polygon(job, D, sides, filled);
1477 free(D);
1478
1479 /*outer square line*/
1480 C[0].x = mid_x(AF) + (B[2].x-B[3].x)*3/4; //x_center+1.5*width
1481 C[0].y = mid_y(&AF[1]) + (B[2].x-B[3].x)*3/4; //y_center
1482 C[1].x = C[0].x;
1483 C[1].y = mid_y(&AF[1]) - (B[2].x-B[3].x)*3/4; //y_center- 1.5*width
1484 C[2].x = mid_x(AF) - (B[2].x-B[3].x)*3/4; //x_center-1.5*width
1485 C[2].y = C[1].y;
1486 C[3].x = C[2].x;
1487 C[3].y = C[0].y;
1488 C[4] = C[0];
1489 gvrender_polyline(job, C, 5);
1490
1491 /*dsDNA line right half*/
1492 C[0].x = mid_x(AF) + (B[2].x-B[3].x)*3/4;
1493 C[0].y = mid_y(&AF[1]);
1494 C[1].x = AF[0].x;
1495 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1496 gvrender_polyline(job, C, 2);
1497
1498 /*dsDNA line left half*/
1499 C[0].x = AF[1].x;
1500 C[0].y = mid_y(&AF[1]);
1501 C[1].x = mid_x(AF) - (B[2].x-B[3].x)*3/4;
1502 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1503 gvrender_polyline(job, C, 2);
1504
1505 break;
1506 case RIBOSITE:
1507 /*
1508 * X with a dashed line on the bottom
1509 *
1510 *
1511 * X
1512 * ╎
1513 * ─────┴──────
1514 */
1515 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1516
1517 D = gv_calloc(sides + 12, sizeof(pointf)); // 12-sided x
1518 D[0].x = mid_x(AF) + (B[2].x-B[3].x)/4; //x_center+width/2 , lower right corner of the x
1519 D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/2; //y_center + width
1520 D[1].x = D[0].x;
1521 D[1].y = D[0].y + (B[3].y-B[4].y)/8; //D[0].y +width/4
1522 D[2].x = D[0].x - (B[2].x-B[3].x)/8; //D[0].x- width/4 //right nook of the x
1523 D[2].y = D[1].y + (B[3].y-B[4].y)/8; //D[0].y+width/2 or D[1].y+width/4
1524 D[3].x = D[0].x;
1525 D[3].y = D[2].y + (B[3].y-B[4].y)/8; //D[2].y + width/4
1526 D[4].x = D[0].x;
1527 D[4].y = D[3].y + (B[3].y-B[4].y)/8; //top right corner of the x
1528 D[5].x = D[2].x;
1529 D[5].y = D[4].y;
1530 D[6].x = mid_x(AF);
1531 D[6].y = D[3].y; //top nook
1532 D[7].x = D[6].x - (B[2].x-B[3].x)/8; //D[5] mirrored across y
1533 D[7].y = D[5].y;
1534 D[8].x = D[7].x - (B[2].x-B[3].x)/8;//top left corner
1535 D[8].y = D[7].y;
1536 D[9].x = D[8].x;
1537 D[9].y = D[3].y;
1538 D[10].x = D[8].x + (B[2].x-B[3].x)/8;
1539 D[10].y = D[2].y;
1540 D[11].x = D[8].x;
1541 D[11].y = D[1].y;
1542 D[12].x = D[8].x;
1543 D[12].y = D[0].y;
1544 D[13].x = D[10].x;
1545 D[13].y = D[12].y;
1546 D[14].x = D[6].x; //bottom nook
1547 D[14].y = D[1].y;
1548 D[15].x = D[2].x;
1549 D[15].y = D[0].y;
1550 gvrender_polygon(job, D, sides + 12, filled);
1551
1552 //2-part dash line
1553
1554 /*line below the x, bottom dash*/
1555 C[0].x = D[14].x; //x_center
1556 C[0].y = mid_y(&AF[1]);
1557 C[1].x = C[0].x;
1558 C[1].y = C[0].y + (B[3].y-B[4].y)/8; //y_center + 1/4*width
1559 gvrender_polyline(job, C, 2);
1560
1561 /*line below the x, top dash*/
1562 C[0].x = D[14].x; //x_center
1563 C[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/4;
1564 C[1].x = C[0].x;
1565 C[1].y = C[0].y + (B[3].y-B[4].y)/8;
1566 gvrender_polyline(job, C, 2);
1567
1568 /*dsDNA line*/
1569 C[0].x = AF[1].x;
1570 C[0].y = mid_y(&AF[1]);
1571 C[1].x = AF[0].x;
1572 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1573 gvrender_polyline(job, C, 2);
1574 free(D);
1575
1576 break;
1577 case RNASTAB:
1578 /*
1579 * octagon with a dashed line on the bottom
1580 *
1581 *
1582 * O
1583 * ╎
1584 * ─────┴──────
1585 */
1586 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1587
1588 D = gv_calloc(sides + 4, sizeof(pointf)); // 12-sided x
1589 D[0].x = mid_x(AF) + (B[2].x-B[3].x)/8; //x_center+width/8 , lower right corner of the hexagon
1590 D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/2; //y_center + width
1591 D[1].x = D[0].x + (B[2].x-B[3].x)/8;
1592 D[1].y = D[0].y + (B[3].y-B[4].y)/8; //D[0].y +width/4
1593 D[2].x = D[1].x; //D[0].x- width/4
1594 D[2].y = D[1].y + (B[3].y-B[4].y)/4; //D[1].y+width/2
1595 D[3].x = D[0].x;
1596 D[3].y = D[2].y + (B[3].y-B[4].y)/8; //D[2].y + width/4
1597 D[4].x = D[3].x - (B[2].x-B[3].x)/4;
1598 D[4].y = D[3].y; //top of the hexagon
1599 D[5].x = D[4].x - (B[2].x-B[3].x)/8;
1600 D[5].y = D[2].y;
1601 D[6].x = D[5].x;
1602 D[6].y = D[1].y; //left side
1603 D[7].x = D[4].x;
1604 D[7].y = D[0].y; //bottom
1605 gvrender_polygon(job, D, sides + 4, filled);
1606
1607 //2-part dash line
1608
1609 /*line below the x, bottom dash*/
1610 C[0].x = mid_x(AF);
1611 C[0].y = mid_y(&AF[1]);
1612 C[1].x = C[0].x;
1613 C[1].y = C[0].y + (B[3].y-B[4].y)/8; //y_center + 1/4*width
1614 gvrender_polyline(job, C, 2);
1615
1616 /*line below the x, top dash*/
1617 C[0].x = mid_x(AF);
1618 C[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/4;
1619 C[1].x = C[0].x;
1620 C[1].y = C[0].y + (B[3].y-B[4].y)/8;
1621 gvrender_polyline(job, C, 2);
1622
1623
1624
1625 /*dsDNA line*/
1626 C[0].x = AF[1].x;
1627 C[0].y = mid_y(&AF[1]);
1628 C[1].x = AF[0].x;
1629 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1630 gvrender_polyline(job, C, 2);
1631 free(D);
1632
1633 break;
1634 case PROTEASESITE:
1635 /*
1636 * X with a solid line on the bottom
1637 *
1638 *
1639 * X
1640 * │
1641 * ─────┴──────
1642 */
1643 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1644 D = gv_calloc(sides + 12, sizeof(pointf)); // 12-sided x
1645 D[0].x = mid_x(AF) + (B[2].x-B[3].x)/4; //x_center+width/2 , lower right corner of the x
1646 D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/2; //y_center + width
1647 D[1].x = D[0].x;
1648 D[1].y = D[0].y + (B[3].y-B[4].y)/8; //D[0].y +width/4
1649 D[2].x = D[0].x - (B[2].x-B[3].x)/8; //D[0].x- width/4 //right nook of the x
1650 D[2].y = D[1].y + (B[3].y-B[4].y)/8; //D[0].y+width/2 or D[1].y+width/4
1651 D[3].x = D[0].x;
1652 D[3].y = D[2].y + (B[3].y-B[4].y)/8; //D[2].y + width/4
1653 D[4].x = D[0].x;
1654 D[4].y = D[3].y + (B[3].y-B[4].y)/8; //top right corner of the x
1655 D[5].x = D[2].x;
1656 D[5].y = D[4].y;
1657 D[6].x = mid_x(AF);
1658 D[6].y = D[3].y; //top nook
1659 D[7].x = D[6].x - (B[2].x-B[3].x)/8; //D[5] mirrored across y
1660 D[7].y = D[5].y;
1661 D[8].x = D[7].x - (B[2].x-B[3].x)/8;//top left corner
1662 D[8].y = D[7].y;
1663 D[9].x = D[8].x;
1664 D[9].y = D[3].y;
1665 D[10].x = D[8].x + (B[2].x-B[3].x)/8;
1666 D[10].y = D[2].y;
1667 D[11].x = D[8].x;
1668 D[11].y = D[1].y;
1669 D[12].x = D[8].x;
1670 D[12].y = D[0].y;
1671 D[13].x = D[10].x;
1672 D[13].y = D[12].y;
1673 D[14].x = D[6].x; //bottom nook
1674 D[14].y = D[1].y;
1675 D[15].x = D[2].x;
1676 D[15].y = D[0].y;
1677 gvrender_polygon(job, D, sides + 12, filled);
1678
1679
1680 /*line below the x*/
1681 C[0] = D[14];
1682 C[1].x = C[0].x;
1683 C[1].y = mid_y(&AF[1]);
1684 gvrender_polyline(job, C, 2);
1685
1686 /*dsDNA line*/
1687 C[0].x = AF[1].x;
1688 C[0].y = mid_y(&AF[1]);
1689 C[1].x = AF[0].x;
1690 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1691 gvrender_polyline(job, C, 2);
1692 free(D);
1693
1694 break;
1695 case PROTEINSTAB:
1696 /*
1697 * octagon with a solid line on the bottom
1698 *
1699 *
1700 * O
1701 * │
1702 * ─────┴──────
1703 */
1704 //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
1705
1706 D = gv_calloc(sides + 4, sizeof(pointf)); // 12-sided x
1707 D[0].x = mid_x(AF) + (B[2].x-B[3].x)/8; //x_center+width/8 , lower right corner of the hexagon
1708 D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/2; //y_center + width
1709 D[1].x = D[0].x + (B[2].x-B[3].x)/8;
1710 D[1].y = D[0].y + (B[3].y-B[4].y)/8; //D[0].y +width/4
1711 D[2].x = D[1].x; //D[0].x- width/4
1712 D[2].y = D[1].y + (B[3].y-B[4].y)/4; //D[1].y+width/2
1713 D[3].x = D[0].x;
1714 D[3].y = D[2].y + (B[3].y-B[4].y)/8; //D[2].y + width/4
1715 D[4].x = D[3].x - (B[2].x-B[3].x)/4;
1716 D[4].y = D[3].y; //top of the hexagon
1717 D[5].x = D[4].x - (B[2].x-B[3].x)/8;
1718 D[5].y = D[2].y;
1719 D[6].x = D[5].x;
1720 D[6].y = D[1].y; //left side
1721 D[7].x = D[4].x;
1722 D[7].y = D[0].y; //bottom
1723 gvrender_polygon(job, D, sides + 4, filled);
1724
1725 /*line below the x*/
1726 C[0].x = mid_x(AF);
1727 C[0].y = D[0].y;
1728 C[1].x = C[0].x;
1729 C[1].y = mid_y(&AF[1]);
1730 gvrender_polyline(job, C, 2);
1731
1732 /*dsDNA line*/
1733 C[0].x = AF[1].x;
1734 C[0].y = mid_y(&AF[1]);
1735 C[1].x = AF[0].x;
1736 C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
1737 gvrender_polyline(job, C, 2);
1738 free(D);
1739
1740 break;
1741
1742 case RPROMOTER:
1743 /*
1744 * Adjust the perimeter for the protrusions.
1745 *
1746 *
1747 * D[1] = AF[1] │╲
1748 * ×────────────────× ╲
1749 * │ D[0] ╲
1750 * │ ╲
1751 * │ ╱
1752 * │ ╱
1753 * │ ┌───────┐ ╱
1754 * │ │ │╱
1755 * └────────┘
1756 */
1757 /* Add the tab edges. */
1758 D = gv_calloc(sides + 5, sizeof(pointf)); // 5 new points
1759 D[0].x = B[1].x - (B[2].x - B[3].x)/2;
1760 D[0].y = B[1].y - (B[3].y - B[4].y)/2;
1761 D[1].x = B[3].x;
1762 D[1].y = B[3].y - (B[3].y - B[4].y)/2;
1763 D[2].x = AF[2].x;
1764 D[2].y = AF[2].y;
1765 D[3].x = B[2].x + (B[2].x - B[3].x)/2;
1766 D[3].y = AF[2].y;
1767 D[4].x = B[2].x + (B[2].x - B[3].x)/2;
1768 D[4].y = AF[2].y + (B[3].y - B[4].y)/2;
1769 D[5].x = B[1].x - (B[2].x - B[3].x)/2;
1770 D[5].y = AF[2].y + (B[3].y - B[4].y)/2;
1771 D[6].x = B[1].x - (B[2].x - B[3].x)/2;
1772 D[6].y = AF[3].y;
1773 D[7].y = AF[0].y - (AF[0].y - AF[3].y)/2; /*triangle point */
1774 D[7].x = AF[0].x; /*triangle point */
1775 D[8].y = AF[0].y;
1776 D[8].x = B[1].x - (B[2].x - B[3].x)/2;
1777
1778 gvrender_polygon(job, D, sides + 5, filled);
1779 free(D);
1780 break;
1781
1782 case RARROW:
1783 /*
1784 * Adjust the perimeter for the protrusions.
1785 *
1786 *
1787 * D[1] = AF[1] │╲
1788 * ×────────────────× ╲
1789 * │ D[0] ╲
1790 * │ ╲
1791 * │ ╱
1792 * │ ╱
1793 * └────────────────┐ ╱
1794 * │╱
1795 *
1796 */
1797 /* Add the tab edges. */
1798 D = gv_calloc(sides + 3, sizeof(pointf)); // 3 new points
1799 D[0].x = B[1].x - (B[2].x - B[3].x)/2;
1800 D[0].y = B[1].y - (B[3].y - B[4].y)/2;
1801 D[1].x = B[3].x;
1802 D[1].y = B[3].y - (B[3].y - B[4].y)/2;
1803 D[2].x = AF[2].x;
1804 D[2].y = AF[2].y + (B[3].y - B[4].y)/2;
1805 D[3].x = B[1].x - (B[2].x - B[3].x)/2;
1806 D[3].y = AF[2].y + (B[3].y - B[4].y)/2;
1807 D[4].x = B[1].x - (B[2].x - B[3].x)/2;
1808 D[4].y = AF[3].y;
1809 D[5].y = AF[0].y - (AF[0].y - AF[3].y)/2;/*triangle point*/
1810 D[5].x = AF[0].x; /*triangle point */
1811 D[6].y = AF[0].y;
1812 D[6].x = B[1].x - (B[2].x - B[3].x)/2;
1813
1814 gvrender_polygon(job, D, sides + 3, filled);
1815 free(D);
1816 break;
1817
1818 case LARROW:
1819 /*
1820 * Adjust the perimeter for the protrusions.
1821 *
1822 *
1823 * ╱│
1824 * ╱ └────────────────┐
1825 * ╱ │
1826 * ╲ │
1827 * ╲ ┌────────────────┘
1828 * ╲│
1829 *
1830 */
1831 /* Add the tab edges. */
1832 D = gv_calloc(sides + 3, sizeof(pointf)); // 3 new points
1833 D[0].x = AF[0].x;
1834 D[0].y = AF[0].y - (B[3].y-B[4].y)/2;
1835 D[1].x = B[2].x + (B[2].x - B[3].x)/2;
1836 D[1].y = AF[0].y - (B[3].y-B[4].y)/2;/*D[0].y*/
1837 D[2].x = B[2].x + (B[2].x - B[3].x)/2;/*D[1].x*/
1838 D[2].y = B[2].y;
1839 D[3].x = AF[1].x; /*triangle point*/
1840 D[3].y = AF[1].y - (AF[1].y - AF[2].y)/2; /*triangle point*/
1841 D[4].x = B[2].x + (B[2].x - B[3].x)/2;/*D[1].x*/
1842 D[4].y = AF[2].y;
1843 D[5].y = AF[2].y + (B[3].y-B[4].y)/2;
1844 D[5].x = B[2].x + (B[2].x - B[3].x)/2;/*D[1].x*/
1845 D[6].y = AF[3].y + (B[3].y - B[4].y)/2;
1846 D[6].x = AF[0].x;/*D[0]*/
1847
1848 gvrender_polygon(job, D, sides + 3, filled);
1849 free(D);
1850 break;
1851
1852 case LPROMOTER:
1853 /*
1854 * Adjust the perimeter for the protrusions.
1855 *
1856 *
1857 * ╱│
1858 * ╱ └────────────────×
1859 * ╱ D[0]
1860 * ╱ │
1861 * ╲ │
1862 * ╲ │
1863 * ╲ ┌────────┐ │
1864 * ╲│ │ │
1865 * └───────┘
1866 */
1867 /* Add the tab edges. */
1868 D = gv_calloc(sides + 5, sizeof(pointf)); // 3 new points
1869 D[0].x = AF[0].x;
1870 D[0].y = AF[0].y - (B[3].y-B[4].y)/2;
1871 D[1].x = B[2].x + (B[2].x - B[3].x)/2;
1872 D[1].y = AF[0].y - (B[3].y-B[4].y)/2;/*D[0].y*/
1873 D[2].x = B[2].x + (B[2].x - B[3].x)/2;/*D[1].x*/
1874 D[2].y = B[2].y;
1875 D[3].x = AF[1].x; /*triangle point*/
1876 D[3].y = AF[1].y - (AF[1].y - AF[2].y)/2; /*triangle point*/
1877 D[4].x = B[2].x + (B[2].x - B[3].x)/2;/*D[1].x*/
1878 D[4].y = AF[2].y;
1879 D[5].y = AF[2].y + (B[3].y-B[4].y)/2;
1880 D[5].x = B[2].x + (B[2].x - B[3].x)/2;/*D[1].x*/
1881 D[6].y = AF[3].y + (B[3].y - B[4].y)/2;
1882 D[6].x = B[1].x - (B[2].x - B[3].x)/2;
1883 D[7].x = B[1].x - (B[2].x - B[3].x)/2;/*D[6].x*/
1884 D[7].y = AF[3].y;
1885 D[8].x = AF[3].x;
1886 D[8].y = AF[3].y;
1887
1888 gvrender_polygon(job, D, sides + 5, filled);
1889 free(D);
1890 break;
1891 }
1892 free(B);
1893}
1894
1895/*=============================poly start=========================*/
1896
1897/* userSize;
1898 * Return maximum size, in points, of width and height supplied
1899 * by user, if any. Return 0 otherwise.
1900 */
1901static double userSize(node_t * n)
1902{
1903 double w, h;
1904 w = late_double(n, N_width, 0.0, MIN_NODEWIDTH);
1905 h = late_double(n, N_height, 0.0, MIN_NODEHEIGHT);
1906 return INCH2PS(fmax(w, h));
1907}
1908
1910{
1911 shape_desc *sh = ND_shape(n);
1912 void (*ifn) (node_t *);
1913
1914 if (!sh)
1915 return SH_UNSET;
1916 ifn = ND_shape(n)->fns->initfn;
1917 if (ifn == poly_init)
1918 return SH_POLY;
1919 else if (ifn == record_init)
1920 return SH_RECORD;
1921 else if (ifn == point_init)
1922 return SH_POINT;
1923 else if (ifn == epsf_init)
1924 return SH_EPSF;
1925 else
1926 return SH_UNSET;
1927}
1928
1930{
1931 return ND_shape(n) && ND_shape(n)->fns->initfn == poly_init;
1932}
1933
1934static void poly_init(node_t * n)
1935{
1936 pointf dimen, min_bb;
1937 pointf outline_bb;
1938 point imagesize;
1939 pointf *vertices;
1940 char *p, *sfile, *fxd;
1941 double temp, alpha, beta, gamma;
1942 double orientation, distortion, skew;
1943 double scalex, scaley;
1944 double width, height, marginx, marginy, spacex;
1945 polygon_t *poly = gv_alloc(sizeof(polygon_t));
1946 bool isPlain = IS_PLAIN(n);
1947
1948 bool regular = !!ND_shape(n)->polygon->regular;
1949 size_t peripheries = ND_shape(n)->polygon->peripheries;
1950 size_t sides = ND_shape(n)->polygon->sides;
1951 orientation = ND_shape(n)->polygon->orientation;
1952 skew = ND_shape(n)->polygon->skew;
1953 distortion = ND_shape(n)->polygon->distortion;
1954 regular |= mapbool(agget(n, "regular"));
1955
1956 /* all calculations in floating point POINTS */
1957
1958 /* make x and y dimensions equal if node is regular
1959 * If the user has specified either width or height, use the max.
1960 * Else use minimum default value.
1961 * If node is not regular, use the current width and height.
1962 */
1963 if (isPlain) {
1964 width = height = 0;
1965 }
1966 else if (regular) {
1967 double sz = userSize(n);
1968 if (sz > 0.0)
1969 width = height = sz;
1970 else {
1971 width = ND_width(n);
1972 height = ND_height(n);
1973 width = height = INCH2PS(fmin(width, height));
1974 }
1975 } else {
1976 width = INCH2PS(ND_width(n));
1977 height = INCH2PS(ND_height(n));
1978 }
1979
1980 peripheries = (size_t)late_int(n, N_peripheries, (int)peripheries, 0);
1981 orientation += late_double(n, N_orientation, 0.0, -360.0);
1982 if (sides == 0) { /* not for builtins */
1983 skew = late_double(n, N_skew, 0.0, -100.0);
1984 sides = (size_t)late_int(n, N_sides, 4, 0);
1985 distortion = late_double(n, N_distortion, 0.0, -100.0);
1986 }
1987
1988 /* get label dimensions */
1989 dimen = ND_label(n)->dimen;
1990
1991 /* minimal whitespace around label */
1992 if (dimen.x > 0 || dimen.y > 0) {
1993 /* padding */
1994 if (!isPlain) {
1995 if ((p = agget(n, "margin"))) {
1996 marginx = marginy = 0;
1997 const int i = sscanf(p, "%lf,%lf", &marginx, &marginy);
1998 marginx = fmax(marginx, 0);
1999 marginy = fmax(marginy, 0);
2000 if (i > 0) {
2001 dimen.x += 2 * INCH2PS(marginx);
2002 if (i > 1)
2003 dimen.y += 2 * INCH2PS(marginy);
2004 else
2005 dimen.y += 2 * INCH2PS(marginx);
2006 } else
2007 PAD(dimen);
2008 } else
2009 PAD(dimen);
2010 }
2011 }
2012 spacex = dimen.x - ND_label(n)->dimen.x;
2013
2014 /* quantization */
2015 if ((temp = GD_drawing(agraphof(n))->quantum) > 0.0) {
2016 temp = INCH2PS(temp);
2017 dimen.x = quant(dimen.x, temp);
2018 dimen.y = quant(dimen.y, temp);
2019 }
2020
2021 imagesize.x = imagesize.y = 0;
2022 if (ND_shape(n)->usershape) {
2023 /* custom requires a shapefile
2024 * not custom is an adaptable user shape such as a postscript
2025 * function.
2026 */
2027 if (streq(ND_shape(n)->name, "custom")) {
2028 sfile = agget(n, "shapefile");
2029 imagesize = gvusershape_size(agraphof(n), sfile);
2030 if (imagesize.x == -1 && imagesize.y == -1) {
2031 agwarningf(
2032 "No or improper shapefile=\"%s\" for node \"%s\"\n",
2033 sfile ? sfile : "<nil>", agnameof(n));
2034 imagesize.x = imagesize.y = 0;
2035 } else {
2036 GD_has_images(agraphof(n)) = true;
2037 imagesize.x += 2; /* some fixed padding */
2038 imagesize.y += 2;
2039 }
2040 }
2041 } else if ((sfile = agget(n, "image")) && *sfile != '\0') {
2042 imagesize = gvusershape_size(agraphof(n), sfile);
2043 if (imagesize.x == -1 && imagesize.y == -1) {
2044 agwarningf(
2045 "No or improper image=\"%s\" for node \"%s\"\n",
2046 sfile, agnameof(n));
2047 imagesize.x = imagesize.y = 0;
2048 } else {
2049 GD_has_images(agraphof(n)) = true;
2050 imagesize.x += 2; /* some fixed padding */
2051 imagesize.y += 2;
2052 }
2053 }
2054
2055 /* initialize node bb to labelsize */
2056 pointf bb = {.x = fmax(dimen.x, imagesize.x),
2057 .y = fmax(dimen.y, imagesize.y)};
2058
2059 /* I don't know how to distort or skew ellipses in postscript */
2060 /* Convert request to a polygon with a large number of sides */
2061 if (sides <= 2 &&
2062 (!is_exactly_pos_zero(distortion) || !is_exactly_pos_zero(skew))) {
2063 sides = 120;
2064 }
2065
2066 /* extra sizing depends on if label is centered vertically */
2067 p = agget(n, "labelloc");
2068 if (p && (p[0] == 't' || p[0] == 'b'))
2069 ND_label(n)->valign = p[0];
2070 else
2071 ND_label(n)->valign = 'c';
2072
2073 const bool isBox = sides == 4 && fabs(fmod(orientation, 90)) < 0.5
2074 && is_exactly_pos_zero(distortion) && is_exactly_pos_zero(skew);
2075 if (isBox) {
2076 /* for regular boxes the fit should be exact */
2077 } else if (ND_shape(n)->polygon->vertices) {
2078 poly_desc_t* pd = (poly_desc_t*)ND_shape(n)->polygon->vertices;
2079 bb = pd->size_gen(bb);
2080 } else {
2081 /* for all other shapes, compute a smallest ellipse
2082 * containing bb centered on the origin, and then pad for that.
2083 * We assume the ellipse is defined by a scaling up of bb.
2084 */
2085 temp = bb.y * SQRT2;
2086 if (height > temp && ND_label(n)->valign == 'c') {
2087 /* if there is height to spare
2088 * and the label is centered vertically
2089 * then just pad x in proportion to the spare height */
2090 bb.x *= sqrt(1. / (1. - SQR(bb.y / height)));
2091 } else {
2092 bb.x *= SQRT2;
2093 bb.y = temp;
2094 }
2095#if 1
2096 if (sides > 2) {
2097 temp = cos(M_PI / (double)sides);
2098 bb.x /= temp;
2099 bb.y /= temp;
2100 /* FIXME - for odd-sided polygons, e.g. triangles, there
2101 would be a better fit with some vertical adjustment of the shape */
2102 }
2103#endif
2104 }
2105
2106 /* at this point, bb is the minimum size of node that can hold the label */
2107 min_bb = bb;
2108
2109 /* increase node size to width/height if needed */
2110 fxd = late_string(n, N_fixed, "false");
2111 if (*fxd == 's' && streq(fxd,"shape")) {
2112 bb = (pointf){.x = width, .y = height};
2113 poly->option.fixedshape = true;
2114 } else if (mapbool(fxd)) {
2115 /* check only label, as images we can scale to fit */
2116 if (width < ND_label(n)->dimen.x || height < ND_label(n)->dimen.y)
2117 agwarningf(
2118 "node '%s', graph '%s' size too small for label\n",
2119 agnameof(n), agnameof(agraphof(n)));
2120 bb = (pointf){.x = width, .y = height};
2121 } else {
2122 bb.x = width = fmax(width, bb.x);
2123 bb.y = height = fmax(height, bb.y);
2124 }
2125
2126 /* If regular, make dimensions the same.
2127 * Need this to guarantee final node size is regular.
2128 */
2129 if (regular) {
2130 width = height = bb.x = bb.y = fmax(bb.x, bb.y);
2131 }
2132
2133 /* Compute space available for label. Provides the justification borders */
2134 if (!mapbool(late_string(n, N_nojustify, "false"))) {
2135 if (isBox) {
2136 ND_label(n)->space.x = fmax(dimen.x, bb.x) - spacex;
2137 }
2138 else if (dimen.y < bb.y) {
2139 temp = bb.x * sqrt(1.0 - SQR(dimen.y) / SQR(bb.y));
2140 ND_label(n)->space.x = fmax(dimen.x, temp) - spacex;
2141 }
2142 else
2143 ND_label(n)->space.x = dimen.x - spacex;
2144 } else {
2145 ND_label(n)->space.x = dimen.x - spacex;
2146 }
2147
2148 if (!poly->option.fixedshape) {
2149 temp = bb.y - min_bb.y;
2150 if (dimen.y < imagesize.y)
2151 temp += imagesize.y - dimen.y;
2152 ND_label(n)->space.y = dimen.y + temp;
2153 }
2154
2157
2158 size_t outp = peripheries;
2159 if (peripheries < 1)
2160 outp = 1;
2161
2162 if (peripheries >= 1 && penwidth > 0) {
2163 // allocate extra vertices representing the outline, i.e., the outermost
2164 // periphery with penwidth taken into account
2165 ++outp;
2166 }
2167
2168 if (sides < 3) { /* ellipses */
2169 sides = 2;
2170 vertices = gv_calloc(outp * sides, sizeof(pointf));
2171 pointf P = {.x = bb.x / 2., .y = bb.y / 2.};
2172 vertices[0] = (pointf){.x = -P.x, .y = -P.y};
2173 vertices[1] = P;
2174 if (peripheries > 1) {
2175 for (size_t j = 1, i = 2; j < peripheries; j++) {
2176 P.x += GAP;
2177 P.y += GAP;
2178 vertices[i] = (pointf){.x = -P.x, .y = -P.y};
2179 i++;
2180 vertices[i] = P;
2181 i++;
2182 }
2183 bb.x = 2. * P.x;
2184 bb.y = 2. * P.y;
2185 }
2186 outline_bb = bb;
2187 if (outp > peripheries) {
2188 // add an outline at half the penwidth outside the outermost periphery
2189 P.x += penwidth / 2;
2190 P.y += penwidth / 2;
2191 size_t i = sides * peripheries;
2192 vertices[i] = (pointf){.x = -P.x, .y = -P.y};
2193 i++;
2194 vertices[i] = P;
2195 i++;
2196 outline_bb.x = 2. * P.x;
2197 outline_bb.y = 2. * P.y;
2198 }
2199 } else {
2200
2201/*
2202 * FIXME - this code is wrong - it doesn't work for concave boundaries.
2203 * (e.g. "folder" or "promoter")
2204 * I don't think it even needs sectorangle, or knowledge of skewed shapes.
2205 * (Concepts that only work for convex regular (modulo skew/distort) polygons.)
2206 *
2207 * I think it only needs to know inside v. outside (by always drawing
2208 * boundaries clockwise, say), and the two adjacent segments.
2209 *
2210 * It needs to find the point where the two lines, parallel to
2211 * the current segments, and outside by GAP distance, intersect.
2212 */
2213
2214 double sinx = 0, cosx = 0, xmax, ymax;
2215 vertices = gv_calloc(outp * sides, sizeof(pointf));
2216 if (ND_shape(n)->polygon->vertices) {
2217 poly_desc_t* pd = (poly_desc_t*)ND_shape(n)->polygon->vertices;
2218 pd->vertex_gen (vertices, &bb);
2219 xmax = bb.x/2;
2220 ymax = bb.y/2;
2221 } else {
2222 double angle, sectorangle, sidelength, skewdist, gdistortion, gskew;
2223 sectorangle = 2. * M_PI / (double)sides;
2224 sidelength = sin(sectorangle / 2.);
2225 skewdist = hypot(fabs(distortion) + fabs(skew), 1.);
2226 gdistortion = distortion * SQRT2 / cos(sectorangle / 2.);
2227 gskew = skew / 2.;
2228 angle = (sectorangle - M_PI) / 2.;
2229 sinx = sin(angle);
2230 cosx = cos(angle);
2231 pointf R = {.x = .5 * cosx, .y = .5 * sinx};
2232 xmax = ymax = 0.;
2233 angle += (M_PI - sectorangle) / 2.;
2234 for (size_t i = 0; i < sides; i++) {
2235
2236 /*next regular vertex */
2237 angle += sectorangle;
2238 sinx = sin(angle);
2239 cosx = cos(angle);
2240 R.x += sidelength * cosx;
2241 R.y += sidelength * sinx;
2242
2243 /*distort and skew */
2244 pointf P = {
2245 .x = R.x * (skewdist + R.y * gdistortion) + R.y * gskew,
2246 .y = R.y};
2247
2248 /*orient P.x,P.y */
2249 alpha = RADIANS(orientation) + atan2(P.y, P.x);
2250 sinx = sin(alpha);
2251 cosx = cos(alpha);
2252 P.x = P.y = hypot(P.x, P.y);
2253 P.x *= cosx;
2254 P.y *= sinx;
2255
2256 /*scale for label */
2257 P.x *= bb.x;
2258 P.y *= bb.y;
2259
2260 /*find max for bounding box */
2261 xmax = fmax(fabs(P.x), xmax);
2262 ymax = fmax(fabs(P.y), ymax);
2263
2264 /* store result in array of points */
2265 vertices[i] = P;
2266 if (isBox) { /* enforce exact symmetry of box */
2267 vertices[1] = (pointf){.x = -P.x, .y = P.y};
2268 vertices[2] = (pointf){.x = -P.x, .y = -P.y};
2269 vertices[3] = (pointf){.x = P.x, .y = -P.y};
2270 break;
2271 }
2272 }
2273 }
2274
2275 /* apply minimum dimensions */
2276 xmax *= 2.;
2277 ymax *= 2.;
2278 bb = (pointf){.x = fmax(width, xmax), .y = fmax(height, ymax)};
2279 outline_bb = bb;
2280
2281 scalex = bb.x / xmax;
2282 scaley = bb.y / ymax;
2283
2284 size_t i;
2285 for (i = 0; i < sides; i++) {
2286 pointf P = vertices[i];
2287 P.x *= scalex;
2288 P.y *= scaley;
2289 vertices[i] = P;
2290 }
2291
2292 if (outp > 1) {
2293 pointf R = vertices[0];
2294 pointf Q;
2295 for (size_t j = 1; j < sides; j++) {
2296 Q = vertices[(i - j) % sides];
2297 if (!is_exactly_equal(Q.x, R.x) || !is_exactly_equal(Q.y, R.y)) {
2298 break;
2299 }
2300 }
2301 assert(!is_exactly_equal(R.x, Q.x) || !is_exactly_equal(R.y, Q.y));
2302 beta = atan2(R.y - Q.y, R.x - Q.x);
2303 pointf Qprev = Q;
2304 for (i = 0; i < sides; i++) {
2305
2306 /*for each vertex find the bisector */
2307 Q = vertices[i];
2308 if (is_exactly_equal(Q.x, Qprev.x) && is_exactly_equal(Q.y, Qprev.y)) {
2309 // The vertex points for the side ending at Q are equal,
2310 // i.e. this side is actually a point and its angle is
2311 // undefined. Therefore we keep the same offset for the end
2312 // point as already calculated for the start point. This may
2313 // occur for shapes which are represented as polygons during
2314 // layout, but are drawn using Bézier curves during
2315 // rendering, e.g. for the `cylinder` shape.
2316 } else {
2317 for (size_t j = 1; j < sides; j++) {
2318 R = vertices[(i + j) % sides];
2319 if (!is_exactly_equal(R.x, Q.x) || !is_exactly_equal(R.y, Q.y)) {
2320 break;
2321 }
2322 }
2323 assert(!is_exactly_equal(R.x, Q.x) || !is_exactly_equal(R.y, Q.y));
2324 alpha = beta;
2325 beta = atan2(R.y - Q.y, R.x - Q.x);
2326 gamma = (alpha + M_PI - beta) / 2.;
2327
2328 /*find distance along bisector to */
2329 /*intersection of next periphery */
2330 temp = GAP / sin(gamma);
2331
2332 /*convert this distance to x and y */
2333 sinx = sin(alpha - gamma) * temp;
2334 cosx = cos(alpha - gamma) * temp;
2335 }
2336 assert(cosx != 0 || sinx != 0);
2337 Qprev = Q;
2338
2339 /*save the vertices of all the */
2340 /*peripheries at this base vertex */
2341 for (size_t j = 1; j < peripheries; j++) {
2342 Q.x += cosx;
2343 Q.y += sinx;
2344 vertices[i + j * sides] = Q;
2345 }
2346 if (outp > peripheries) {
2347 // add an outline at half the penwidth outside the outermost periphery
2348 Q.x += cosx * penwidth / 2 / GAP;
2349 Q.y += sinx * penwidth / 2 / GAP;
2350 vertices[i + peripheries * sides] = Q;
2351 }
2352 }
2353 for (i = 0; i < sides; i++) {
2354 pointf P = vertices[i + (peripheries - 1) * sides];
2355 bb = (pointf){.x = fmax(2.0 * fabs(P.x), bb.x),
2356 .y = fmax(2.0 * fabs(P.y), bb.y)};
2357 Q = vertices[i + (outp - 1) * sides];
2358 outline_bb = (pointf){.x = fmax(2.0 * fabs(Q.x), outline_bb.x),
2359 .y = fmax(2.0 * fabs(Q.y), outline_bb.y)};
2360 }
2361 }
2362 }
2363 poly->regular = regular;
2364 poly->peripheries = peripheries;
2365 poly->sides = sides;
2366 poly->orientation = orientation;
2367 poly->skew = skew;
2368 poly->distortion = distortion;
2369 poly->vertices = vertices;
2370
2371 if (poly->option.fixedshape) {
2372 /* set width and height to reflect label and shape */
2373 ND_width(n) = PS2INCH(fmax(dimen.x, bb.x));
2374 ND_height(n) = PS2INCH(fmax(dimen.y, bb.y));
2375 ND_outline_width(n) = PS2INCH(fmax(dimen.x, outline_bb.x));
2376 ND_outline_height(n) = PS2INCH(fmax(dimen.y, outline_bb.y));
2377 } else {
2378 ND_width(n) = PS2INCH(bb.x);
2379 ND_height(n) = PS2INCH(bb.y);
2380 ND_outline_width(n) = PS2INCH(outline_bb.x);
2381 ND_outline_height(n) = PS2INCH(outline_bb.y);
2382 }
2383 ND_shape_info(n) = poly;
2384}
2385
2386static void poly_free(node_t * n)
2387{
2388 polygon_t *p = ND_shape_info(n);
2389
2390 if (p) {
2391 free(p->vertices);
2392 free(p);
2393 }
2394}
2395
2396/* poly_inside:
2397 * Return true if point p is inside polygonal shape of node inside_context->s.n.
2398 * Calculations are done using unrotated node shape. Thus, if p is in a rotated
2399 * coordinate system, it is reset as P in the unrotated coordinate system. Similarly,
2400 * the ND_rw, ND_lw and ND_ht values are rotated if the graph is flipped.
2401 */
2402static bool poly_inside(inside_t * inside_context, pointf p)
2403{
2404 size_t sides;
2405 const pointf O = {0};
2406 pointf *vertex = NULL;
2407
2408 int s;
2409 pointf P, Q, R;
2410 boxf *bp;
2411 node_t *n;
2412
2413 if (!inside_context) {
2414 return false;
2415 }
2416
2417 bp = inside_context->s.bp;
2418 n = inside_context->s.n;
2419 P = ccwrotatepf(p, 90 * GD_rankdir(agraphof(n)));
2420
2421 /* Quick test if port rectangle is target */
2422 if (bp) {
2423 boxf bbox = *bp;
2424 return INSIDE(P, bbox);
2425 }
2426
2427 if (n != inside_context->s.lastn) {
2428 double n_width, n_height;
2429 double n_outline_width;
2430 double n_outline_height;
2431 inside_context->s.last_poly = ND_shape_info(n);
2432 vertex = inside_context->s.last_poly->vertices;
2433 sides = inside_context->s.last_poly->sides;
2434
2435 double xsize, ysize;
2436 if (inside_context->s.last_poly->option.fixedshape) {
2437 boxf bb = polyBB(inside_context->s.last_poly);
2438 n_width = bb.UR.x - bb.LL.x;
2439 n_height = bb.UR.y - bb.LL.y;
2440 n_outline_width = n_width;
2441 n_outline_height = n_height;
2442 /* get point and node size adjusted for rankdir=LR */
2443 if (GD_flip(agraphof(n))) {
2444 ysize = n_width;
2445 xsize = n_height;
2446 } else {
2447 xsize = n_width;
2448 ysize = n_height;
2449 }
2450 } else {
2451 /* get point and node size adjusted for rankdir=LR */
2452 if (GD_flip(agraphof(n))) {
2453 ysize = ND_lw(n) + ND_rw(n);
2454 xsize = ND_ht(n);
2455 } else {
2456 xsize = ND_lw(n) + ND_rw(n);
2457 ysize = ND_ht(n);
2458 }
2459 n_width = INCH2PS(ND_width(n));
2460 n_height = INCH2PS(ND_height(n));
2461 n_outline_width = INCH2PS(ND_outline_width(n));
2462 n_outline_height = INCH2PS(ND_outline_height(n));
2463 }
2464
2465 /* scale */
2466 inside_context->s.scalex = n_width;
2467 (void)fdiv_overflow(inside_context->s.scalex, xsize, &inside_context->s.scalex);
2468 inside_context->s.scaley = n_height;
2469 (void)fdiv_overflow(inside_context->s.scaley, ysize, &inside_context->s.scaley);
2470 inside_context->s.box_URx = n_outline_width / 2;
2471 inside_context->s.box_URy = n_outline_height / 2;
2472
2475 if (inside_context->s.last_poly->peripheries >= 1 && penwidth > 0) {
2476 /* index to outline, i.e., the outer-periphery with penwidth taken into account */
2477 inside_context->s.outp =
2478 (inside_context->s.last_poly->peripheries + 1 - 1) * sides;
2479 } else if (inside_context->s.last_poly->peripheries < 1) {
2480 inside_context->s.outp = 0;
2481 } else {
2482 /* index to outer-periphery */
2483 inside_context->s.outp =
2484 (inside_context->s.last_poly->peripheries - 1) * sides;
2485 }
2486 inside_context->s.lastn = n;
2487 } else {
2488 vertex = inside_context->s.last_poly->vertices;
2489 sides = inside_context->s.last_poly->sides;
2490 }
2491
2492 /* scale */
2493 P.x *= inside_context->s.scalex;
2494 P.y *= inside_context->s.scaley;
2495
2496 /* inside bounding box? */
2497 if (fabs(P.x) > inside_context->s.box_URx ||
2498 fabs(P.y) > inside_context->s.box_URy)
2499 return false;
2500
2501 /* ellipses */
2502 if (sides <= 2)
2503 return hypot(P.x / inside_context->s.box_URx,
2504 P.y / inside_context->s.box_URy) < 1;
2505
2506 /* use fast test in case we are converging on a segment */
2507 size_t i = inside_context->s.last % sides; // in case last left over from larger polygon
2508 size_t i1 = (i + 1) % sides;
2509 Q = vertex[i + inside_context->s.outp];
2510 R = vertex[i1 + inside_context->s.outp];
2511 if (!same_side(P, O, Q, R)) /* false if outside the segment's face */
2512 return false;
2513 /* else inside the segment face... */
2514 if ((s = same_side(P, Q, R, O)) && same_side(P, R, O, Q)) /* true if between the segment's sides */
2515 return true;
2516 /* else maybe in another segment */
2517 for (size_t j = 1; j < sides; j++) { // iterate over remaining segments
2518 if (s) { /* clockwise */
2519 i = i1;
2520 i1 = (i + 1) % sides;
2521 } else { /* counter clockwise */
2522 i1 = i;
2523 i = (i + sides - 1) % sides;
2524 }
2525 if (!same_side(P, O, vertex[i + inside_context->s.outp],
2526 vertex[i1 + inside_context->s.outp])) { // false if outside any other segment’s face
2527 inside_context->s.last = i;
2528 return false;
2529 }
2530 }
2531 /* inside all segments' faces */
2532 inside_context->s.last = i; // in case next edge is to same side
2533 return true;
2534}
2535
2536static int poly_path(node_t * n, port * p, int side, boxf rv[], int *kptr)
2537{
2538 (void)n;
2539 (void)p;
2540 (void)side;
2541 (void)rv;
2542 (void)kptr;
2543
2544 return 0;
2545}
2546
2547static unsigned char invflip_side(unsigned char side, int rankdir) {
2548 switch (rankdir) {
2549 case RANKDIR_TB:
2550 break;
2551 case RANKDIR_BT:
2552 switch (side) {
2553 case TOP:
2554 side = BOTTOM;
2555 break;
2556 case BOTTOM:
2557 side = TOP;
2558 break;
2559 default:
2560 break;
2561 }
2562 break;
2563 case RANKDIR_LR:
2564 switch (side) {
2565 case TOP:
2566 side = RIGHT;
2567 break;
2568 case BOTTOM:
2569 side = LEFT;
2570 break;
2571 case LEFT:
2572 side = TOP;
2573 break;
2574 case RIGHT:
2575 side = BOTTOM;
2576 break;
2577 default:
2578 break;
2579 }
2580 break;
2581 case RANKDIR_RL:
2582 switch (side) {
2583 case TOP:
2584 side = RIGHT;
2585 break;
2586 case BOTTOM:
2587 side = LEFT;
2588 break;
2589 case LEFT:
2590 side = BOTTOM;
2591 break;
2592 case RIGHT:
2593 side = TOP;
2594 break;
2595 default:
2596 break;
2597 }
2598 break;
2599 default:
2600 UNREACHABLE();
2601 }
2602 return side;
2603}
2604
2607static double invflip_angle(int angle, int rankdir) {
2608 switch (rankdir) {
2609 case RANKDIR_TB:
2610 break;
2611 case RANKDIR_BT:
2612 angle *= -1;
2613 break;
2614 case RANKDIR_LR:
2615 angle -= 2;
2616 break;
2617 case RANKDIR_RL:
2618 if (angle == 4)
2619 angle = -2;
2620 else if (angle == 3)
2621 angle = -1;
2622 else if (angle == 2)
2623 angle = 0;
2624 else if (angle == 0)
2625 angle = 2;
2626 else if (angle == -1)
2627 angle = 3;
2628 else if (angle == -2)
2629 angle = 4;
2630 break;
2631 default:
2632 UNREACHABLE();
2633 }
2634 return angle * 0.25 * M_PI;
2635}
2636
2637/* compassPoint:
2638 * Compute compass points for non-trivial shapes.
2639 * It finds where the ray ((0,0),(x,y)) hits the boundary and
2640 * returns it.
2641 * Assumes ictxt and ictxt->n are non-NULL.
2642 *
2643 * bezier_clip uses the shape's _inside function, which assumes the input
2644 * point is in the rotated coordinate system (as determined by rankdir), so
2645 * it rotates the point counterclockwise based on rankdir to get the node's
2646 * coordinate system.
2647 * To handle this, if rankdir is set, we rotate (x,y) clockwise, and then
2648 * rotate the answer counterclockwise.
2649 */
2650static pointf compassPoint(inside_t * ictxt, double y, double x)
2651{
2652 pointf curve[4]; /* Bézier control points for a straight line */
2653 node_t *n = ictxt->s.n;
2654 graph_t* g = agraphof(n);
2655 int rd = GD_rankdir(g);
2656 pointf p;
2657
2658 p.x = x;
2659 p.y = y;
2660 if (rd)
2661 p = cwrotatepf(p, 90 * rd);
2662
2663 curve[0].x = curve[0].y = 0;
2664 curve[1] = curve[0];
2665 curve[3] = curve[2] = p;
2666
2667 bezier_clip(ictxt, ND_shape(n)->fns->insidefn, curve, true);
2668
2669 if (rd)
2670 curve[0] = ccwrotatepf(curve[0], 90 * rd);
2671 return curve[0];
2672}
2673
2674/* compassPort:
2675 * Attach a compass point to a port pp, and fill in remaining fields.
2676 * n is the corresponding node; bp is the bounding box of the port.
2677 * compass is the compass point
2678 * Return 1 if unrecognized compass point, in which case we
2679 * use the center.
2680 *
2681 * This function also finishes initializing the port structure,
2682 * even if no compass point is involved.
2683 * The sides value gives the set of sides shared by the port. This
2684 * is used with a compass point to indicate if the port is exposed, to
2685 * set the port's side value.
2686 *
2687 * If ictxt is NULL, we are working with a simple rectangular shape (node or
2688 * port of record of HTML label), so compass points are trivial. If ictxt is
2689 * not NULL, it provides shape information so that the compass point can be
2690 * calculated based on the shape.
2691 *
2692 * The code assumes the node has its unrotated shape to find the points,
2693 * angles, etc. At the end, the parameters are adjusted to take into account
2694 * the rankdir attribute. In particular, the first if-else statement flips
2695 * the already adjusted ND_ht, ND_lw and ND_rw back to non-flipped values.
2696 *
2697 */
2698static int compassPort(node_t *n, boxf *bp, port *pp, const char *compass,
2699 unsigned char sides, inside_t * ictxt) {
2700 boxf b;
2701 pointf p, ctr;
2702 int rv = 0;
2703 int theta = 0; // in units of ¼×π
2704 bool constrain = false;
2705 bool dyna = false;
2706 unsigned char side = 0;
2707 bool clip = true;
2708 bool defined;
2709 double maxv; /* sufficiently large value outside of range of node */
2710
2711 if (bp) {
2712 b = *bp;
2713 p = (pointf){(b.LL.x + b.UR.x) / 2, (b.LL.y + b.UR.y) / 2};
2714 defined = true;
2715 } else {
2716 p.x = p.y = 0.;
2717 if (GD_flip(agraphof(n))) {
2718 b.UR.x = ND_ht(n) / 2.;
2719 b.LL.x = -b.UR.x;
2720 b.UR.y = ND_lw(n);
2721 b.LL.y = -b.UR.y;
2722 } else {
2723 b.UR.y = ND_ht(n) / 2.;
2724 b.LL.y = -b.UR.y;
2725 b.UR.x = ND_lw(n);
2726 b.LL.x = -b.UR.x;
2727 }
2728 defined = false;
2729 }
2730 maxv = fmax(b.UR.x, b.UR.y);
2731 maxv *= 4.0;
2732 ctr = p;
2733 if (compass && *compass) {
2734 switch (*compass++) {
2735 case 'e':
2736 if (*compass)
2737 rv = 1;
2738 else {
2739 if (ictxt)
2740 p = compassPoint(ictxt, ctr.y, maxv);
2741 else
2742 p.x = b.UR.x;
2743 theta = 0;
2744 constrain = true;
2745 defined = true;
2746 clip = false;
2747 side = sides & RIGHT;
2748 }
2749 break;
2750 case 's':
2751 p.y = b.LL.y;
2752 constrain = true;
2753 clip = false;
2754 switch (*compass) {
2755 case '\0':
2756 theta = -2;
2757 defined = true;
2758 if (ictxt)
2759 p = compassPoint(ictxt, -maxv, ctr.x);
2760 else
2761 p.x = ctr.x;
2762 side = sides & BOTTOM;
2763 break;
2764 case 'e':
2765 theta = -1;
2766 defined = true;
2767 if (ictxt)
2768 p = compassPoint(ictxt, -maxv, maxv);
2769 else
2770 p.x = b.UR.x;
2771 side = sides & (BOTTOM | RIGHT);
2772 break;
2773 case 'w':
2774 theta = -3;
2775 defined = true;
2776 if (ictxt)
2777 p = compassPoint(ictxt, -maxv, -maxv);
2778 else
2779 p.x = b.LL.x;
2780 side = sides & (BOTTOM | LEFT);
2781 break;
2782 default:
2783 p.y = ctr.y;
2784 constrain = false;
2785 clip = true;
2786 rv = 1;
2787 break;
2788 }
2789 break;
2790 case 'w':
2791 if (*compass)
2792 rv = 1;
2793 else {
2794 if (ictxt)
2795 p = compassPoint(ictxt, ctr.y, -maxv);
2796 else
2797 p.x = b.LL.x;
2798 theta = 4;
2799 constrain = true;
2800 defined = true;
2801 clip = false;
2802 side = sides & LEFT;
2803 }
2804 break;
2805 case 'n':
2806 p.y = b.UR.y;
2807 constrain = true;
2808 clip = false;
2809 switch (*compass) {
2810 case '\0':
2811 defined = true;
2812 theta = 2;
2813 if (ictxt)
2814 p = compassPoint(ictxt, maxv, ctr.x);
2815 else
2816 p.x = ctr.x;
2817 side = sides & TOP;
2818 break;
2819 case 'e':
2820 defined = true;
2821 theta = 1;
2822 if (ictxt)
2823 p = compassPoint(ictxt, maxv, maxv);
2824 else
2825 p.x = b.UR.x;
2826 side = sides & (TOP | RIGHT);
2827 break;
2828 case 'w':
2829 defined = true;
2830 theta = 3;
2831 if (ictxt)
2832 p = compassPoint(ictxt, maxv, -maxv);
2833 else
2834 p.x = b.LL.x;
2835 side = sides & (TOP | LEFT);
2836 break;
2837 default:
2838 p.y = ctr.y;
2839 constrain = false;
2840 clip = true;
2841 rv = 1;
2842 break;
2843 }
2844 break;
2845 case '_':
2846 dyna = true;
2847 side = sides;
2848 break;
2849 case 'c':
2850 break;
2851 default:
2852 rv = 1;
2853 break;
2854 }
2855 }
2856 p = cwrotatepf(p, 90 * GD_rankdir(agraphof(n)));
2857 if (dyna)
2858 pp->side = side;
2859 else
2860 pp->side = invflip_side(side, GD_rankdir(agraphof(n)));
2861 pp->bp = bp;
2862 pp->p = p;
2863 pp->theta = invflip_angle(theta, GD_rankdir(agraphof(n)));
2864 if (p.x == 0 && p.y == 0)
2865 pp->order = MC_SCALE / 2;
2866 else {
2867 /* compute angle with 0 at north pole, increasing CCW */
2868 double angle = atan2(p.y, p.x) + 1.5 * M_PI;
2869 if (angle >= 2 * M_PI)
2870 angle -= 2 * M_PI;
2871 pp->order = (int) (MC_SCALE * angle / (2 * M_PI));
2872 }
2873 pp->constrained = constrain;
2874 pp->defined = defined;
2875 pp->clip = clip;
2876 pp->dyna = dyna;
2877 return rv;
2878}
2879
2880static port poly_port(node_t * n, char *portname, char *compass)
2881{
2882 port rv;
2883 boxf *bp;
2884 unsigned char sides; // bitmap of which sides the port lies along
2885
2886 if (portname[0] == '\0')
2887 return Center;
2888
2889 if (compass == NULL)
2890 compass = "_";
2891 sides = BOTTOM | RIGHT | TOP | LEFT;
2892 if (ND_label(n)->html && (bp = html_port(n, portname, &sides))) {
2893 if (compassPort(n, bp, &rv, compass, sides, NULL)) {
2894 agwarningf(
2895 "node %s, port %s, unrecognized compass point '%s' - ignored\n",
2896 agnameof(n), portname, compass);
2897 }
2898 } else {
2899 inside_t *ictxtp;
2900 inside_t ictxt = {0};
2901
2902 if (IS_BOX(n))
2903 ictxtp = NULL;
2904 else {
2905 ictxt.s.n = n;
2906 ictxt.s.bp = NULL;
2907 ictxtp = &ictxt;
2908 }
2909 if (compassPort(n, NULL, &rv, portname, sides, ictxtp))
2910 unrecognized(n, portname);
2911 }
2912
2913 rv.name = NULL;
2914 return rv;
2915}
2916
2917static bool multicolor(const char *f) {
2918 return strchr(f, ':') != NULL;
2919}
2920
2921/* generic polygon gencode routine */
2922static void poly_gencode(GVJ_t * job, node_t * n)
2923{
2924 obj_state_t *obj = job->obj;
2925 polygon_t *poly;
2926 double xsize, ysize;
2927 pointf P, *vertices;
2928 int filled;
2929 bool usershape_p;
2930 bool pfilled; /* true if fill not handled by user shape */
2931 char *color, *name;
2932 int doMap = (obj->url || obj->explicit_tooltip);
2933 char* fillcolor=NULL;
2934 char* pencolor=NULL;
2935
2936 if (doMap && !(job->flags & EMIT_CLUSTERS_LAST))
2938 obj->url, obj->tooltip, obj->target,
2939 obj->id);
2940
2941 poly = ND_shape_info(n);
2942 vertices = poly->vertices;
2943 const size_t sides = poly->sides;
2944 size_t peripheries = poly->peripheries;
2945 pointf *AF = gv_calloc(sides + 5, sizeof(pointf));
2946
2947 /* nominal label position in the center of the node */
2948 ND_label(n)->pos = ND_coord(n);
2949
2950 xsize = (ND_lw(n) + ND_rw(n)) / INCH2PS(ND_width(n));
2951 ysize = ND_ht(n) / INCH2PS(ND_height(n));
2952
2953 const graphviz_polygon_style_t style = stylenode(job, n);
2954
2955 char *clrs[2] = {0};
2956 if (ND_gui_state(n) & GUI_STATE_ACTIVE) {
2957 pencolor = DEFAULT_ACTIVEPENCOLOR;
2958 gvrender_set_pencolor(job, pencolor);
2961 filled = FILL;
2962 } else if (ND_gui_state(n) & GUI_STATE_SELECTED) {
2963 pencolor = DEFAULT_SELECTEDPENCOLOR;
2964 gvrender_set_pencolor(job, pencolor);
2967 filled = FILL;
2968 } else if (ND_gui_state(n) & GUI_STATE_DELETED) {
2969 pencolor = DEFAULT_DELETEDPENCOLOR;
2970 gvrender_set_pencolor(job, pencolor);
2973 filled = FILL;
2974 } else if (ND_gui_state(n) & GUI_STATE_VISITED) {
2975 pencolor = DEFAULT_VISITEDPENCOLOR;
2976 gvrender_set_pencolor(job, pencolor);
2979 filled = FILL;
2980 } else {
2981 if (style.filled) {
2982 double frac;
2983 fillcolor = findFill (n);
2984 if (findStopColor (fillcolor, clrs, &frac)) {
2985 gvrender_set_fillcolor(job, clrs[0]);
2986 if (clrs[1])
2987 gvrender_set_gradient_vals(job,clrs[1],late_int(n,N_gradientangle,0,0), frac);
2988 else
2990 if (style.radial)
2991 filled = RGRADIENT;
2992 else
2993 filled = GRADIENT;
2994 }
2995 else {
2996 gvrender_set_fillcolor(job, fillcolor);
2997 filled = FILL;
2998 }
2999 }
3000 else if (style.striped || style.wedged) {
3001 fillcolor = findFill (n);
3002 filled = 1;
3003 }
3004 else {
3005 filled = 0;
3006 }
3007 pencolor = penColor(job, n); /* emit pen color */
3008 }
3009
3010 pfilled = !ND_shape(n)->usershape || streq(ND_shape(n)->name, "custom");
3011
3012 /* if no boundary but filled, set boundary color to transparent */
3013 if (peripheries == 0 && filled != 0 && pfilled) {
3014 peripheries = 1;
3015 gvrender_set_pencolor(job, "transparent");
3016 }
3017
3018 /* draw peripheries first */
3019 size_t j;
3020 for (j = 0; j < peripheries; j++) {
3021 for (size_t i = 0; i < sides; i++) {
3022 P = vertices[i + j * sides];
3023 AF[i].x = P.x * xsize + ND_coord(n).x;
3024 AF[i].y = P.y * ysize + ND_coord(n).y;
3025 }
3026 if (sides <= 2) {
3027 if (style.wedged && j == 0 && multicolor(fillcolor)) {
3028 int rv = wedgedEllipse (job, AF, fillcolor);
3029 if (rv > 1)
3030 agerr (AGPREV, "in node %s\n", agnameof(n));
3031 filled = 0;
3032 }
3033 gvrender_ellipse(job, AF, filled);
3034 if (style.diagonals) {
3035 Mcircle_hack(job, n);
3036 }
3037 } else if (style.striped) {
3038 if (j == 0) {
3039 int rv = stripedBox (job, AF, fillcolor, 1);
3040 if (rv > 1)
3041 agerr (AGPREV, "in node %s\n", agnameof(n));
3042 }
3043 gvrender_polygon(job, AF, sides, 0);
3044 } else if (style.underline) {
3045 gvrender_set_pencolor(job, "transparent");
3046 gvrender_polygon(job, AF, sides, filled);
3047 gvrender_set_pencolor(job, pencolor);
3048 gvrender_polyline(job, AF+2, 2);
3049 } else if (SPECIAL_CORNERS(style)) {
3050 round_corners(job, AF, sides, style, filled);
3051 } else {
3052 gvrender_polygon(job, AF, sides, filled);
3053 }
3054 /* fill innermost periphery only */
3055 filled = 0;
3056 }
3057
3058 usershape_p = false;
3059 if (ND_shape(n)->usershape) {
3060 name = ND_shape(n)->name;
3061 if (streq(name, "custom")) {
3062 if ((name = agget(n, "shapefile")) && name[0])
3063 usershape_p = true;
3064 } else
3065 usershape_p = true;
3066 } else if ((name = agget(n, "image")) && name[0]) {
3067 usershape_p = true;
3068 }
3069 if (usershape_p) {
3070 /* get coords of innermost periphery */
3071 for (size_t i = 0; i < sides; i++) {
3072 P = vertices[i];
3073 AF[i].x = P.x * xsize + ND_coord(n).x;
3074 AF[i].y = P.y * ysize + ND_coord(n).y;
3075 }
3076 gvrender_usershape(job, name, AF, sides, filled != 0,
3077 late_string(n, N_imagescale, "false"),
3078 late_string(n, N_imagepos, "mc"));
3079 filled = 0; /* with user shapes, we have done the fill if needed */
3080 }
3081
3082 free(AF);
3083 free (clrs[0]);
3084 free (clrs[1]);
3085
3087 if (doMap) {
3088 if (job->flags & EMIT_CLUSTERS_LAST)
3090 obj->url, obj->tooltip, obj->target,
3091 obj->id);
3093 }
3094}
3095
3096/*=======================end poly======================================*/
3097
3098/*===============================point start========================*/
3099
3100/* point_init:
3101 * shorthand for shape=circle, style=filled, width=0.05, label=""
3102 */
3103static void point_init(node_t * n)
3104{
3105 polygon_t *poly = gv_alloc(sizeof(polygon_t));
3106 size_t sides, outp, peripheries = ND_shape(n)->polygon->peripheries;
3107 double sz;
3108 pointf P, *vertices;
3109 size_t i, j;
3110 double w, h;
3111
3112 /* set width and height, and make them equal
3113 * if user has set weight or height, use it.
3114 * if both are set, use smallest.
3115 * if neither, use default
3116 */
3117 w = late_double(n, N_width, DBL_MAX, MIN_NODEWIDTH);
3118 h = late_double(n, N_height, DBL_MAX, MIN_NODEHEIGHT);
3119 w = fmin(w, h);
3120 if (is_exactly_equal(w, DBL_MAX) &&
3121 is_exactly_equal(h, DBL_MAX)) // neither defined
3122 ND_width(n) = ND_height(n) = DEF_POINT;
3123 else {
3124 w = fmin(w, h);
3125 /* If w == 0, use it; otherwise, make w no less than MIN_POINT due
3126 * to the restrictions mentioned above.
3127 */
3128 if (w > 0.0)
3129 w = fmax(w, MIN_POINT);
3130 ND_width(n) = ND_height(n) = w;
3131 }
3132
3133 sz = ND_width(n) * POINTS_PER_INCH;
3134 peripheries = (size_t)late_int(n, N_peripheries, (int)peripheries, 0);
3135 if (peripheries < 1)
3136 outp = 1;
3137 else
3138 outp = peripheries;
3139 sides = 2;
3142 if (peripheries >= 1 && penwidth > 0) {
3143 // allocate extra vertices representing the outline, i.e., the outermost
3144 // periphery with penwidth taken into account
3145 ++outp;
3146 }
3147 vertices = gv_calloc(outp * sides, sizeof(pointf));
3148 P.y = P.x = sz / 2.;
3149 vertices[0].x = -P.x;
3150 vertices[0].y = -P.y;
3151 vertices[1] = P;
3152 if (peripheries > 1) {
3153 for (j = 1, i = 2; j < peripheries; j++) {
3154 P.x += GAP;
3155 P.y += GAP;
3156 vertices[i].x = -P.x;
3157 vertices[i].y = -P.y;
3158 i++;
3159 vertices[i].x = P.x;
3160 vertices[i].y = P.y;
3161 i++;
3162 }
3163 sz = 2. * P.x;
3164 } else {
3165 i = sides;
3166 }
3167
3168 if (peripheries >= 1 && penwidth > 0 && outp > peripheries) {
3169 // add an outline at half the penwidth outside the outermost periphery
3170 P.x += penwidth / 2;
3171 P.y += penwidth / 2;
3172 vertices[i].x = -P.x;
3173 vertices[i].y = -P.y;
3174 i++;
3175 vertices[i].x = P.x;
3176 vertices[i].y = P.y;
3177 i++;
3178 }
3179 const double sz_outline = 2. * P.x;
3180
3181 poly->regular = true;
3182 poly->peripheries = peripheries;
3183 poly->sides = 2;
3184 poly->orientation = 0;
3185 poly->skew = 0;
3186 poly->distortion = 0;
3187 poly->vertices = vertices;
3188
3189 ND_height(n) = ND_width(n) = PS2INCH(sz);
3190 ND_outline_height(n) = ND_outline_width(n) = PS2INCH(sz_outline);
3191 ND_shape_info(n) = poly;
3192}
3193
3194static bool point_inside(inside_t * inside_context, pointf p)
3195{
3196 pointf P;
3197 node_t *n;
3198
3199 if (!inside_context) {
3200 return false;
3201 }
3202
3203 n = inside_context->s.n;
3204 P = ccwrotatepf(p, 90 * GD_rankdir(agraphof(n)));
3205
3206 if (n != inside_context->s.lastn) {
3207 size_t outp;
3209 const size_t sides = 2;
3212
3213 if (poly->peripheries >= 1 && penwidth > 0) {
3214 /* index to outline, i.e., the outer-periphery with penwidth taken into account */
3215 outp = sides * (poly->peripheries + 1 - 1);
3216 } else if (poly->peripheries < 1) {
3217 outp = 0;
3218 } else {
3219 /* index to outer-periphery */
3220 outp = sides * (poly->peripheries - 1);
3221 }
3222
3223 inside_context->s.radius = poly->vertices[outp + 1].x;
3224 inside_context->s.lastn = n;
3225 }
3226
3227 /* inside bounding box? */
3228 if (fabs(P.x) > inside_context->s.radius ||
3229 fabs(P.y) > inside_context->s.radius)
3230 return false;
3231
3232 return hypot(P.x, P.y) <= inside_context->s.radius;
3233}
3234
3235static void point_gencode(GVJ_t * job, node_t * n)
3236{
3237 obj_state_t *obj = job->obj;
3238 polygon_t *poly;
3239 pointf P, *vertices;
3240 bool filled;
3241 char *color;
3242 int doMap = obj->url || obj->explicit_tooltip;
3243
3244 if (doMap && !(job->flags & EMIT_CLUSTERS_LAST))
3246 obj->url, obj->tooltip, obj->target,
3247 obj->id);
3248
3249 poly = ND_shape_info(n);
3250 vertices = poly->vertices;
3251 const size_t sides = poly->sides;
3252 size_t peripheries = poly->peripheries;
3253
3254 graphviz_polygon_style_t style = {0};
3255 checkStyle(n, &style);
3256 if (style.invisible)
3258 else
3260 if (N_penwidth)
3261 gvrender_set_penwidth(job, late_double(n, N_penwidth, 1.0, 0.0));
3262
3263 if (ND_gui_state(n) & GUI_STATE_ACTIVE) {
3268 } else if (ND_gui_state(n) & GUI_STATE_SELECTED) {
3273 } else if (ND_gui_state(n) & GUI_STATE_DELETED) {
3278 } else if (ND_gui_state(n) & GUI_STATE_VISITED) {
3283 } else {
3284 color = findFillDflt(n, "black");
3285 gvrender_set_fillcolor(job, color); /* emit fill color */
3286 penColor(job, n); /* emit pen color */
3287 }
3288 filled = true;
3289
3290 /* if no boundary but filled, set boundary color to fill color */
3291 if (peripheries == 0) {
3292 peripheries = 1;
3293 if (color[0])
3295 }
3296
3297 for (size_t j = 0; j < peripheries; j++) {
3298 enum {A_size = 2};
3299 pointf AF[A_size] = {{0}};
3300 for (size_t i = 0; i < sides; i++) {
3301 P = vertices[i + j * sides];
3302 if (i < A_size) {
3303 AF[i].x = P.x + ND_coord(n).x;
3304 AF[i].y = P.y + ND_coord(n).y;
3305 }
3306 }
3307 gvrender_ellipse(job, AF, filled);
3308 /* fill innermost periphery only */
3309 filled = false;
3310 }
3311
3312 if (doMap) {
3313 if (job->flags & EMIT_CLUSTERS_LAST)
3315 obj->url, obj->tooltip, obj->target,
3316 obj->id);
3318 }
3319}
3320
3321/* the "record" shape is a rudimentary table formatter */
3322
3323#define HASTEXT 1
3324#define HASPORT 2
3325#define HASTABLE 4
3326#define INTEXT 8
3327#define INPORT 16
3328
3329static bool ISCTRL(int c) {
3330 return c == '{' || c == '}' || c == '|' || c == '<' || c == '>';
3331}
3332
3333static char *reclblp;
3334
3335static void free_field(field_t * f)
3336{
3337 int i;
3338
3339 for (i = 0; i < f->n_flds; i++) {
3340 free_field(f->fld[i]);
3341 }
3342
3343 free(f->id);
3344 free_label(f->lp);
3345 free(f->fld);
3346 free(f);
3347}
3348
3349/* parse_error:
3350 * Clean up memory allocated in parse_reclbl, then return NULL
3351 */
3352static field_t *parse_error(field_t *rv, char *portname) {
3353 free_field(rv);
3354 free(portname);
3355 return NULL;
3356}
3357
3358static field_t *parse_reclbl(node_t *n, bool LR, bool flag, char *text) {
3359 field_t *fp, *rv = gv_alloc(sizeof(field_t));
3360 char *tsp, *psp=NULL, *hstsp, *hspsp=NULL, *sp;
3361 char *tmpport = NULL;
3362 int cnt, mode, fi;
3363 textlabel_t *lbl = ND_label(n);
3364 unsigned char uc;
3365
3366 fp = NULL;
3367 size_t maxf;
3368 for (maxf = 1, cnt = 0, sp = reclblp; *sp; sp++) {
3369 if (*sp == '\\') {
3370 sp++;
3371 if (*sp && (*sp == '{' || *sp == '}' || *sp == '|' || *sp == '\\'))
3372 continue;
3373 }
3374 if (*sp == '{')
3375 cnt++;
3376 else if (*sp == '}')
3377 cnt--;
3378 else if (*sp == '|' && cnt == 0)
3379 maxf++;
3380 if (cnt < 0)
3381 break;
3382 }
3383 rv->fld = gv_calloc(maxf, sizeof(field_t*));
3384 rv->LR = LR;
3385 mode = 0;
3386 fi = 0;
3387 hstsp = tsp = text;
3388 bool wflag = true;
3389 bool ishardspace = false;
3390 while (wflag) {
3391 if ((uc = *(unsigned char*)reclblp) && uc < ' ') { /* Ignore non-0 control characters */
3392 reclblp++;
3393 continue;
3394 }
3395 switch (*reclblp) {
3396 case '<':
3397 if (mode & (HASTABLE | HASPORT))
3398 return parse_error(rv, tmpport);
3399 if (lbl->html)
3400 goto dotext;
3401 mode |= (HASPORT | INPORT);
3402 reclblp++;
3403 hspsp = psp = text;
3404 break;
3405 case '>':
3406 if (lbl->html)
3407 goto dotext;
3408 if (!(mode & INPORT))
3409 return parse_error(rv, tmpport);
3410 if (psp > text + 1 && psp - 1 != hspsp && *(psp - 1) == ' ')
3411 psp--;
3412 *psp = '\000';
3413 tmpport = gv_strdup(text);
3414 mode &= ~INPORT;
3415 reclblp++;
3416 break;
3417 case '{':
3418 reclblp++;
3419 if (mode != 0 || !*reclblp)
3420 return parse_error(rv, tmpport);
3421 mode = HASTABLE;
3422 if (!(rv->fld[fi++] = parse_reclbl(n, !LR, false, text)))
3423 return parse_error(rv, tmpport);
3424 break;
3425 case '}':
3426 case '|':
3427 case '\000':
3428 if ((!*reclblp && !flag) || (mode & INPORT))
3429 return parse_error(rv, tmpport);
3430 if (!(mode & HASTABLE))
3431 fp = rv->fld[fi++] = gv_alloc(sizeof(field_t));
3432 if (tmpport) {
3433 fp->id = tmpport;
3434 tmpport = NULL;
3435 }
3436 if (!(mode & (HASTEXT | HASTABLE))) {
3437 mode |= HASTEXT;
3438 *tsp++ = ' ';
3439 }
3440 if (mode & HASTEXT) {
3441 if (tsp > text + 1 && tsp - 1 != hstsp && *(tsp - 1) == ' ')
3442 tsp--;
3443 *tsp = '\000';
3444 fp->lp = make_label(n, text, lbl->html, false,
3445 lbl->fontsize, lbl->fontname, lbl->fontcolor);
3446 fp->LR = true;
3447 hstsp = tsp = text;
3448 }
3449 if (*reclblp) {
3450 if (*reclblp == '}') {
3451 reclblp++;
3452 rv->n_flds = fi;
3453 return rv;
3454 }
3455 mode = 0;
3456 reclblp++;
3457 } else
3458 wflag = false;
3459 break;
3460 case '\\':
3461 if (*(reclblp + 1)) {
3462 if (ISCTRL(*(reclblp + 1))) {
3463 // nothing
3464 } else if (*(reclblp + 1) == ' ' && !lbl->html)
3465 ishardspace = true;
3466 else {
3467 *tsp++ = '\\';
3468 mode |= INTEXT | HASTEXT;
3469 }
3470 reclblp++;
3471 }
3472 /* fall through */
3473 default:
3474 dotext:
3475 if ((mode & HASTABLE) && *reclblp != ' ')
3476 return parse_error(rv, tmpport);
3477 if (!(mode & (INTEXT | INPORT)) && *reclblp != ' ')
3478 mode |= INTEXT | HASTEXT;
3479 if (mode & INTEXT) {
3480 if (!(*reclblp == ' ' && !ishardspace && *(tsp - 1) == ' '
3481 && !lbl->html))
3482 *tsp++ = *reclblp;
3483 if (ishardspace)
3484 hstsp = tsp - 1;
3485 } else if (mode & INPORT) {
3486 if (!(*reclblp == ' ' && !ishardspace &&
3487 (psp == text || *(psp - 1) == ' ')))
3488 *psp++ = *reclblp;
3489 if (ishardspace)
3490 hspsp = psp - 1;
3491 }
3492 reclblp++;
3493 while ((*reclblp & 0xc0) == 0x80)
3494 *tsp++ = *reclblp++;
3495 break;
3496 }
3497 }
3498 rv->n_flds = fi;
3499 return rv;
3500}
3501
3503{
3504 int i;
3505 char *p;
3506 double marginx, marginy;
3507 pointf d, d0;
3508 pointf dimen;
3509
3510 if (f->lp) {
3511 dimen = f->lp->dimen;
3512
3513 /* minimal whitespace around label */
3514 if (dimen.x > 0.0 || dimen.y > 0.0) {
3515 /* padding */
3516 if ((p = agget(n, "margin"))) {
3517 i = sscanf(p, "%lf,%lf", &marginx, &marginy);
3518 if (i > 0) {
3519 dimen.x += 2 * INCH2PS(marginx);
3520 if (i > 1)
3521 dimen.y += 2 * INCH2PS(marginy);
3522 else
3523 dimen.y += 2 * INCH2PS(marginx);
3524 } else
3525 PAD(dimen);
3526 } else
3527 PAD(dimen);
3528 }
3529 d = dimen;
3530 } else {
3531 d.x = d.y = 0;
3532 for (i = 0; i < f->n_flds; i++) {
3533 d0 = size_reclbl(n, f->fld[i]);
3534 if (f->LR) {
3535 d.x += d0.x;
3536 d.y = fmax(d.y, d0.y);
3537 } else {
3538 d.y += d0.y;
3539 d.x = fmax(d.x, d0.x);
3540 }
3541 }
3542 }
3543 f->size = d;
3544 return d;
3545}
3546
3547static void resize_reclbl(field_t *f, pointf sz, bool nojustify_p) {
3548 int i, amt;
3549 double inc;
3550 pointf d;
3551 pointf newsz;
3552 field_t *sf;
3553
3554 /* adjust field */
3555 d.x = sz.x - f->size.x;
3556 d.y = sz.y - f->size.y;
3557 f->size = sz;
3558
3559 /* adjust text area */
3560 if (f->lp && !nojustify_p) {
3561 f->lp->space.x += d.x;
3562 f->lp->space.y += d.y;
3563 }
3564
3565 /* adjust children */
3566 if (f->n_flds) {
3567
3568 if (f->LR)
3569 inc = d.x / f->n_flds;
3570 else
3571 inc = d.y / f->n_flds;
3572 for (i = 0; i < f->n_flds; i++) {
3573 sf = f->fld[i];
3574 amt = (int)((i + 1) * inc) - (int)(i * inc);
3575 if (f->LR)
3576 newsz = (pointf){sf->size.x + amt, sz.y};
3577 else
3578 newsz = (pointf){sz.x, sf->size.y + amt};
3579 resize_reclbl(sf, newsz, nojustify_p);
3580 }
3581 }
3582}
3583
3584/* pos_reclbl:
3585 * Assign position info for each field. Also, set
3586 * the sides attribute, which indicates which sides of the
3587 * record are accessible to the field.
3588 */
3589static void pos_reclbl(field_t *f, pointf ul, unsigned char sides) {
3590 int i, last;
3591 unsigned char mask;
3592
3593 f->sides = sides;
3594 f->b.LL = (pointf){ul.x, ul.y - f->size.y};
3595 f->b.UR = (pointf){ul.x + f->size.x, ul.y};
3596 last = f->n_flds - 1;
3597 for (i = 0; i <= last; i++) {
3598 if (sides) {
3599 if (f->LR) {
3600 if (i == 0) {
3601 if (i == last)
3602 mask = TOP | BOTTOM | RIGHT | LEFT;
3603 else
3604 mask = TOP | BOTTOM | LEFT;
3605 } else if (i == last)
3606 mask = TOP | BOTTOM | RIGHT;
3607 else
3608 mask = TOP | BOTTOM;
3609 } else {
3610 if (i == 0) {
3611 if (i == last)
3612 mask = TOP | BOTTOM | RIGHT | LEFT;
3613 else
3614 mask = TOP | RIGHT | LEFT;
3615 } else if (i == last)
3616 mask = LEFT | BOTTOM | RIGHT;
3617 else
3618 mask = LEFT | RIGHT;
3619 }
3620 } else
3621 mask = 0;
3622 pos_reclbl(f->fld[i], ul, (unsigned char)(sides & mask));
3623 if (f->LR)
3624 ul.x = ul.x + f->fld[i]->size.x;
3625 else
3626 ul.y = ul.y - f->fld[i]->size.y;
3627 }
3628}
3629
3630#if defined(DEBUG) && DEBUG > 1
3631static void indent(int l)
3632{
3633 int i;
3634 for (i = 0; i < l; i++)
3635 fputs(" ", stderr);
3636}
3637
3638static void prbox(boxf b)
3639{
3640 fprintf(stderr, "((%.5g,%.5g),(%.5g,%.5g))\n", b.LL.x, b.LL.y, b.UR.x,
3641 b.UR.y);
3642}
3643
3644static void dumpL(field_t * info, int level)
3645{
3646 int i;
3647
3648 indent(level);
3649 if (info->n_flds == 0) {
3650 fprintf(stderr, "Label \"%s\" ", info->lp->text);
3651 prbox(info->b);
3652 } else {
3653 fprintf(stderr, "Tbl ");
3654 prbox(info->b);
3655 for (i = 0; i < info->n_flds; i++) {
3656 dumpL(info->fld[i], level + 1);
3657 }
3658 }
3659}
3660#endif
3661
3662/* syntax of labels: foo|bar|baz or foo|(recursive|label)|baz */
3663static void record_init(node_t * n)
3664{
3665 field_t *info;
3666 pointf sz;
3667 int flip;
3668 size_t len;
3669 unsigned char sides = BOTTOM | RIGHT | TOP | LEFT;
3670
3671 /* Always use rankdir to determine how records are laid out */
3672 flip = !GD_realflip(agraphof(n));
3673 reclblp = ND_label(n)->text;
3674 len = strlen(reclblp);
3675 /* For some forgotten reason, an empty label is parsed into a space, so
3676 * we need at least two bytes in textbuf, as well as accounting for the
3677 * error path involving "\\N" below.
3678 */
3679 len = MAX(MAX(len, 1), strlen("\\N"));
3680 char *textbuf = gv_calloc(len + 1, sizeof(char)); // temp buffer for storing labels
3681 if (!(info = parse_reclbl(n, flip, true, textbuf))) {
3682 agerrorf("bad label format %s\n", ND_label(n)->text);
3683 reclblp = "\\N";
3684 info = parse_reclbl(n, flip, true, textbuf);
3685 }
3686 free(textbuf);
3687 size_reclbl(n, info);
3688 sz.x = INCH2PS(ND_width(n));
3689 sz.y = INCH2PS(ND_height(n));
3690 if (mapbool(late_string(n, N_fixed, "false"))) {
3691 if (sz.x < info->size.x || sz.y < info->size.y) {
3692/* should check that the record really won't fit, e.g., there may be no text.
3693 agwarningf("node '%s' size may be too small\n", agnameof(n));
3694*/
3695 }
3696 } else {
3697 sz.x = fmax(info->size.x, sz.x);
3698 sz.y = fmax(info->size.y, sz.y);
3699 }
3700 resize_reclbl(info, sz, mapbool(late_string(n, N_nojustify, "false")));
3701 pointf ul = {-sz.x / 2., sz.y / 2.}; /* FIXME - is this still true: suspected to introduce rounding error - see Kluge below */
3702 pos_reclbl(info, ul, sides);
3703 ND_width(n) = PS2INCH(info->size.x);
3704 ND_height(n) = PS2INCH(info->size.y + 1); /* Kluge!! +1 to fix rounding diff between layout and rendering
3705 otherwise we can get -1 coords in output */
3706 ND_shape_info(n) = info;
3707}
3708
3709static void record_free(node_t * n)
3710{
3711 field_t *p = ND_shape_info(n);
3712
3713 free_field(p);
3714}
3715
3716static field_t *map_rec_port(field_t * f, char *str)
3717{
3718 field_t *rv;
3719 int sub;
3720
3721 if (f->id && streq(f->id, str))
3722 rv = f;
3723 else {
3724 rv = NULL;
3725 for (sub = 0; sub < f->n_flds; sub++)
3726 if ((rv = map_rec_port(f->fld[sub], str)))
3727 break;
3728 }
3729 return rv;
3730}
3731
3732static port record_port(node_t * n, char *portname, char *compass)
3733{
3734 field_t *f;
3735 field_t *subf;
3736 port rv;
3737 unsigned char sides; // bitmap of which sides the port lies along
3738
3739 if (portname[0] == '\0')
3740 return Center;
3741 sides = BOTTOM | RIGHT | TOP | LEFT;
3742 if (compass == NULL)
3743 compass = "_";
3744 f = ND_shape_info(n);
3745 if ((subf = map_rec_port(f, portname))) {
3746 if (compassPort(n, &subf->b, &rv, compass, subf->sides, NULL)) {
3747 agwarningf(
3748 "node %s, port %s, unrecognized compass point '%s' - ignored\n",
3749 agnameof(n), portname, compass);
3750 }
3751 } else if (compassPort(n, &f->b, &rv, portname, sides, NULL)) {
3752 unrecognized(n, portname);
3753 }
3754
3755 return rv;
3756}
3757
3758/* record_inside:
3759 * Note that this does not handle Mrecords correctly. It assumes
3760 * everything is a rectangle.
3761 */
3762static bool record_inside(inside_t * inside_context, pointf p)
3763{
3764
3765 field_t *fld0;
3766 boxf *bp = inside_context->s.bp;
3767 node_t *n = inside_context->s.n;
3768 boxf bbox;
3769
3770 /* convert point to node coordinate system */
3771 p = ccwrotatepf(p, 90 * GD_rankdir(agraphof(n)));
3772
3773 if (bp == NULL) {
3774 fld0 = ND_shape_info(n);
3775 bbox = fld0->b;
3776 } else
3777 bbox = *bp;
3778
3779 // adjust bbox to outline, i.e., the periphery with penwidth taken into account
3782 const pointf extension = {penwidth / 2, penwidth / 2};
3783 bbox.LL = sub_pointf(bbox.LL, extension);
3784 bbox.UR = add_pointf(bbox.UR, extension);
3785
3786 return INSIDE(p, bbox);
3787}
3788
3789/* record_path:
3790 * Generate box path from port to border.
3791 * See poly_path for constraints.
3792 */
3793static int record_path(node_t * n, port * prt, int side, boxf rv[],
3794 int *kptr)
3795{
3796 int i;
3797 double ls, rs;
3798 pointf p;
3799 field_t *info;
3800
3801 if (!prt->defined)
3802 return 0;
3803 p = prt->p;
3804 info = ND_shape_info(n);
3805
3806 for (i = 0; i < info->n_flds; i++) {
3807 if (!GD_flip(agraphof(n))) {
3808 ls = info->fld[i]->b.LL.x;
3809 rs = info->fld[i]->b.UR.x;
3810 } else {
3811 ls = info->fld[i]->b.LL.y;
3812 rs = info->fld[i]->b.UR.y;
3813 }
3814 if (BETWEEN(ls, p.x, rs)) {
3815 /* FIXME: I don't understand this code */
3816 if (GD_flip(agraphof(n))) {
3817 rv[0] = flip_rec_boxf(info->fld[i]->b, ND_coord(n));
3818 } else {
3819 rv[0].LL.x = ND_coord(n).x + ls;
3820 rv[0].LL.y = ND_coord(n).y - (ND_ht(n) / 2);
3821 rv[0].UR.x = ND_coord(n).x + rs;
3822 }
3823 rv[0].UR.y = ND_coord(n).y + (ND_ht(n) / 2);
3824 *kptr = 1;
3825 break;
3826 }
3827 }
3828 return side;
3829}
3830
3831static void gen_fields(GVJ_t * job, node_t * n, field_t * f)
3832{
3833 int i;
3834 pointf AF[2], coord;
3835
3836 if (f->lp) {
3837 f->lp->pos = add_pointf(mid_pointf(f->b.LL, f->b.UR), ND_coord(n));
3838 emit_label(job, EMIT_NLABEL, f->lp);
3839 penColor(job, n);
3840 }
3841
3842 coord = ND_coord(n);
3843 for (i = 0; i < f->n_flds; i++) {
3844 if (i > 0) {
3845 if (f->LR) {
3846 AF[0] = f->fld[i]->b.LL;
3847 AF[1].x = AF[0].x;
3848 AF[1].y = f->fld[i]->b.UR.y;
3849 } else {
3850 AF[1] = f->fld[i]->b.UR;
3851 AF[0].x = f->fld[i]->b.LL.x;
3852 AF[0].y = AF[1].y;
3853 }
3854 AF[0] = add_pointf(AF[0], coord);
3855 AF[1] = add_pointf(AF[1], coord);
3856 gvrender_polyline(job, AF, 2);
3857 }
3858 gen_fields(job, n, f->fld[i]);
3859 }
3860}
3861
3862static void record_gencode(GVJ_t * job, node_t * n)
3863{
3864 obj_state_t *obj = job->obj;
3865 boxf BF;
3866 pointf AF[4];
3867 field_t *f;
3868 int doMap = obj->url || obj->explicit_tooltip;
3869 int filled;
3870
3871 f = ND_shape_info(n);
3872 BF = f->b;
3873 BF.LL.x += ND_coord(n).x;
3874 BF.LL.y += ND_coord(n).y;
3875 BF.UR.x += ND_coord(n).x;
3876 BF.UR.y += ND_coord(n).y;
3877
3878 if (doMap && !(job->flags & EMIT_CLUSTERS_LAST))
3880 obj->url, obj->tooltip, obj->target,
3881 obj->id);
3882 graphviz_polygon_style_t style = stylenode(job, n);
3883 penColor(job, n);
3884 char *clrs[2] = {0};
3885 if (style.filled) {
3886 char* fillcolor = findFill (n);
3887 double frac;
3888
3889 if (findStopColor (fillcolor, clrs, &frac)) {
3890 gvrender_set_fillcolor(job, clrs[0]);
3891 if (clrs[1])
3892 gvrender_set_gradient_vals(job,clrs[1],late_int(n,N_gradientangle,0,0), frac);
3893 else
3895 if (style.radial)
3896 filled = RGRADIENT;
3897 else
3898 filled = GRADIENT;
3899 }
3900 else {
3901 filled = FILL;
3902 gvrender_set_fillcolor(job, fillcolor);
3903 }
3904 }
3905 else filled = 0;
3906
3907 if (streq(ND_shape(n)->name, "Mrecord"))
3908 style.rounded = true;
3909 if (SPECIAL_CORNERS(style)) {
3910 AF[0] = BF.LL;
3911 AF[2] = BF.UR;
3912 AF[1].x = AF[2].x;
3913 AF[1].y = AF[0].y;
3914 AF[3].x = AF[0].x;
3915 AF[3].y = AF[2].y;
3916 round_corners(job, AF, 4, style, filled);
3917 } else {
3918 gvrender_box(job, BF, filled);
3919 }
3920
3921 gen_fields(job, n, f);
3922
3923 free (clrs[0]);
3924 free(clrs[1]);
3925
3926 if (doMap) {
3927 if (job->flags & EMIT_CLUSTERS_LAST)
3929 obj->url, obj->tooltip, obj->target,
3930 obj->id);
3932 }
3933}
3934
3936static size_t N_UserShape;
3937
3938shape_desc *find_user_shape(const char *name)
3939{
3940 if (UserShape) {
3941 for (size_t i = 0; i < N_UserShape; i++) {
3942 if (streq(UserShape[i]->name, name))
3943 return UserShape[i];
3944 }
3945 }
3946 return NULL;
3947}
3948
3949static shape_desc *user_shape(char *name)
3950{
3951 shape_desc *p;
3952
3953 if ((p = find_user_shape(name)))
3954 return p;
3955 size_t i = N_UserShape++;
3957 p = UserShape[i] = gv_alloc(sizeof(shape_desc));
3958 *p = Shapes[0];
3959 p->name = strdup(name);
3960 if (Lib == NULL && !streq(name, "custom")) {
3961 agwarningf("using %s for unknown shape %s\n", Shapes[0].name,
3962 p->name);
3963 p->usershape = false;
3964 } else {
3965 p->usershape = true;
3966 }
3967 return p;
3968}
3969
3970shape_desc *bind_shape(char *name, node_t * np)
3971{
3972 shape_desc *ptr, *rv = NULL;
3973 const char *str;
3974
3975 str = safefile(agget(np, "shapefile"));
3976 /* If shapefile is defined and not epsf, set shape = custom */
3977 if (str && !streq(name, "epsf"))
3978 name = "custom";
3979 if (!streq(name, "custom")) {
3980 for (ptr = Shapes; ptr->name; ptr++) {
3981 if (streq(ptr->name, name)) {
3982 rv = ptr;
3983 break;
3984 }
3985 }
3986 }
3987 if (rv == NULL)
3988 rv = user_shape(name);
3989 return rv;
3990}
3991
3992static bool epsf_inside(inside_t * inside_context, pointf p)
3993{
3994 pointf P;
3995 double x2;
3996 node_t *n = inside_context->s.n;
3997
3998 P = ccwrotatepf(p, 90 * GD_rankdir(agraphof(n)));
3999 x2 = ND_ht(n) / 2;
4000 return P.y >= -x2 && P.y <= x2 && P.x >= -ND_lw(n) && P.x <= ND_rw(n);
4001}
4002
4003static void epsf_gencode(GVJ_t * job, node_t * n)
4004{
4005 obj_state_t *obj = job->obj;
4006 epsf_t *desc;
4007 int doMap = obj->url || obj->explicit_tooltip;
4008
4009 desc = ND_shape_info(n);
4010 if (!desc)
4011 return;
4012
4013 if (doMap && !(job->flags & EMIT_CLUSTERS_LAST))
4015 obj->url, obj->tooltip, obj->target,
4016 obj->id);
4017 if (desc)
4018 fprintf(job->output_file,
4019 "%.5g %.5g translate newpath user_shape_%d\n",
4020 ND_coord(n).x + desc->offset.x,
4021 ND_coord(n).y + desc->offset.y, desc->macro_id);
4022 ND_label(n)->pos = ND_coord(n);
4023
4025 if (doMap) {
4026 if (job->flags & EMIT_CLUSTERS_LAST)
4028 obj->url, obj->tooltip, obj->target,
4029 obj->id);
4031 }
4032}
4033
4034#define alpha (M_PI/10.0)
4035#define alpha2 (2*alpha)
4036#define alpha3 (3*alpha)
4037#define alpha4 (2*alpha2)
4038
4040{
4041 pointf sz;
4042 double r, rx, ry;
4043
4044 rx = sz0.x/(2*cos(alpha));
4045 ry = sz0.y/(sin(alpha) + sin(alpha3));
4046 const double r0 = fmax(rx, ry);
4047 r = r0 * sin(alpha4) * cos(alpha2) / (cos(alpha) * cos(alpha4));
4048
4049 sz.x = 2*r*cos(alpha);
4050 sz.y = r*(1 + sin(alpha3));
4051 return sz;
4052}
4053
4054static void star_vertices (pointf* vertices, pointf* bb)
4055{
4056 int i;
4057 pointf sz = *bb;
4058 double offset, a, aspect = (1 + sin(alpha3))/(2*cos(alpha));
4059 double r, r0, theta = alpha;
4060
4061 /* Scale up width or height to required aspect ratio */
4062 a = sz.y/sz.x;
4063 if (a > aspect) {
4064 sz.x = sz.y/aspect;
4065 }
4066 else if (a < aspect) {
4067 sz.y = sz.x*aspect;
4068 }
4069
4070 /* for given sz, get radius */
4071 r = sz.x/(2*cos(alpha));
4072 r0 = r * cos(alpha) * cos(alpha4) / (sin(alpha4) * cos(alpha2));
4073
4074 /* offset is the y shift of circle center from bb center */
4075 offset = (r*(1 - sin(alpha3)))/2;
4076
4077 for (i = 0; i < 10; i += 2) {
4078 vertices[i].x = r*cos(theta);
4079 vertices[i].y = r*sin(theta) - offset;
4080 theta += alpha2;
4081 vertices[i+1].x = r0*cos(theta);
4082 vertices[i+1].y = r0*sin(theta) - offset;
4083 theta += alpha2;
4084 }
4085
4086 *bb = sz;
4087}
4088
4089static bool star_inside(inside_t * inside_context, pointf p)
4090{
4091 size_t sides;
4092 pointf *vertex;
4093 const pointf O = {0};
4094
4095 if (!inside_context) {
4096 return false;
4097 }
4098 boxf *bp = inside_context->s.bp;
4099 node_t *n = inside_context->s.n;
4100 pointf P, Q, R;
4101 int outcnt;
4102
4103 P = ccwrotatepf(p, 90 * GD_rankdir(agraphof(n)));
4104
4105 /* Quick test if port rectangle is target */
4106 if (bp) {
4107 boxf bbox = *bp;
4108 return INSIDE(P, bbox);
4109 }
4110
4111 if (n != inside_context->s.lastn) {
4112 inside_context->s.last_poly = ND_shape_info(n);
4113 vertex = inside_context->s.last_poly->vertices;
4114 sides = inside_context->s.last_poly->sides;
4115
4118 if (inside_context->s.last_poly->peripheries >= 1 && penwidth > 0) {
4119 /* index to outline, i.e., the outer-periphery with penwidth taken into account */
4120 inside_context->s.outp = (inside_context->s.last_poly->peripheries + 1 - 1)
4121 * sides;
4122 } else if (inside_context->s.last_poly->peripheries < 1) {
4123 inside_context->s.outp = 0;
4124 } else {
4125 /* index to outer-periphery */
4126 inside_context->s.outp = (inside_context->s.last_poly->peripheries - 1)
4127 * sides;
4128 }
4129 inside_context->s.lastn = n;
4130 } else {
4131 vertex = inside_context->s.last_poly->vertices;
4132 sides = inside_context->s.last_poly->sides;
4133 }
4134
4135 outcnt = 0;
4136 for (size_t i = 0; i < sides; i += 2) {
4137 Q = vertex[i + inside_context->s.outp];
4138 R = vertex[(i + 4) % sides + inside_context->s.outp];
4139 if (!(same_side(P, O, Q, R))) {
4140 outcnt++;
4141 }
4142 if (outcnt == 2) {
4143 return false;
4144 }
4145 }
4146 return true;
4147}
4148
4149/* cylinder:
4150 * Code based on PostScript version by Brandon Rhodes.
4151 * http://rhodesmill.org/brandon/2007/a-database-symbol-for-graphviz/
4152 */
4154{
4155 sz.y *= 1.375;
4156 return sz;
4157}
4158
4159static void cylinder_vertices (pointf* vertices, pointf* bb)
4160{
4161 double x = bb->x/2;
4162 double y = bb->y/2;
4163 double yr = bb->y/11;
4164
4165 vertices[0].x = x;
4166 vertices[0].y = y-yr;
4167 vertices[1].x = x;
4168 vertices[1].y = y-(1-0.551784)*yr;
4169 vertices[2].x = 0.551784*x;
4170 vertices[2].y = y;
4171 vertices[3].x = 0;
4172 vertices[3].y = y;
4173 vertices[4].x = -0.551784*x;
4174 vertices[4].y = y;
4175 vertices[5].x = -x;
4176 vertices[5].y = vertices[1].y;
4177 vertices[6].x = -x;
4178 vertices[6].y = y-yr;
4179 vertices[7] = vertices[6];
4180 vertices[8].x = -x;
4181 vertices[8].y = yr-y;
4182 vertices[9] = vertices[8];
4183 vertices[10].x = -x;
4184 vertices[10].y = -vertices[1].y;
4185 vertices[11].x = vertices[4].x;
4186 vertices[11].y = -vertices[4].y;
4187 vertices[12].x = vertices[3].x;
4188 vertices[12].y = -vertices[3].y;
4189 vertices[13].x = vertices[2].x;
4190 vertices[13].y = -vertices[2].y;
4191 vertices[14].x = vertices[1].x;
4192 vertices[14].y = -vertices[1].y;
4193 vertices[15].x = vertices[0].x;
4194 vertices[15].y = -vertices[0].y;
4195 vertices[16] = vertices[15];
4196 vertices[18] = vertices[17] = vertices[0];
4197}
4198
4199static void cylinder_draw(GVJ_t *job, pointf *AF, size_t sides, int filled) {
4200 pointf vertices[7];
4201 double y0 = AF[0].y;
4202 double y02 = y0+y0;
4203
4204 vertices[0] = AF[0];
4205 vertices[1].x = AF[1].x;
4206 vertices[1].y = y02 - AF[1].y;
4207 vertices[2].x = AF[2].x;
4208 vertices[2].y = y02 - AF[2].y;
4209 vertices[3].x = AF[3].x;
4210 vertices[3].y = y02 - AF[3].y;
4211 vertices[4].x = AF[4].x;
4212 vertices[4].y = y02 - AF[4].y;
4213 vertices[5].x = AF[5].x;
4214 vertices[5].y = y02 - AF[5].y;
4215 vertices[6] = AF[6];
4216
4217 gvrender_beziercurve(job, AF, sides, filled);
4218 gvrender_beziercurve(job, vertices, 7, 0);
4219}
4220
4221static const char *side_port[] = {"s", "e", "n", "w"};
4222
4223static pointf cvtPt(pointf p, int rankdir) {
4224 pointf q = { 0, 0 };
4225
4226 switch (rankdir) {
4227 case RANKDIR_TB:
4228 q = p;
4229 break;
4230 case RANKDIR_BT:
4231 q.x = p.x;
4232 q.y = -p.y;
4233 break;
4234 case RANKDIR_LR:
4235 q.y = p.x;
4236 q.x = -p.y;
4237 break;
4238 case RANKDIR_RL:
4239 q.y = p.x;
4240 q.x = p.y;
4241 break;
4242 default:
4243 UNREACHABLE();
4244 }
4245 return q;
4246}
4247
4248/* closestSide:
4249 * Resolve unspecified compass-point port to best available port.
4250 * At present, this finds the available side closest to the center
4251 * of the other port.
4252 *
4253 * This could be improved:
4254 * - if other is unspecified, do them together
4255 * - if dot, bias towards bottom of one to top of another, if possible
4256 * - if line segment from port centers uses available sides, use these
4257 * or center. (This latter may require spline routing to cooperate.)
4258 */
4259static const char *closestSide(node_t *n, node_t *other, port *oldport) {
4260 boxf b;
4261 int rkd = GD_rankdir(agraphof(n)->root);
4262 pointf p = {0};
4263 const pointf pt = cvtPt(ND_coord(n), rkd);
4264 const pointf opt = cvtPt(ND_coord(other), rkd);
4265 int sides = oldport->side;
4266 const char *rv = NULL;
4267
4268 if (sides == 0 || sides == (TOP | BOTTOM | LEFT | RIGHT))
4269 return rv; /* use center */
4270
4271 if (oldport->bp) {
4272 b = *oldport->bp;
4273 } else {
4274 if (GD_flip(agraphof(n))) {
4275 b.UR.x = ND_ht(n) / 2;
4276 b.LL.x = -b.UR.x;
4277 b.UR.y = ND_lw(n);
4278 b.LL.y = -b.UR.y;
4279 } else {
4280 b.UR.y = ND_ht(n) / 2;
4281 b.LL.y = -b.UR.y;
4282 b.UR.x = ND_lw(n);
4283 b.LL.x = -b.UR.x;
4284 }
4285 }
4286
4287 double mind = 0;
4288 for (int i = 0; i < 4; i++) {
4289 if ((sides & (1 << i)) == 0)
4290 continue;
4291 switch (i) {
4292 case BOTTOM_IX:
4293 p.y = b.LL.y;
4294 p.x = (b.LL.x + b.UR.x) / 2;
4295 break;
4296 case RIGHT_IX:
4297 p.x = b.UR.x;
4298 p.y = (b.LL.y + b.UR.y) / 2;
4299 break;
4300 case TOP_IX:
4301 p.y = b.UR.y;
4302 p.x = (b.LL.x + b.UR.x) / 2;
4303 break;
4304 case LEFT_IX:
4305 p.x = b.LL.x;
4306 p.y = (b.LL.y + b.UR.y) / 2;
4307 break;
4308 default:
4309 UNREACHABLE();
4310 }
4311 p.x += pt.x;
4312 p.y += pt.y;
4313 const double d = DIST2(p, opt);
4314 if (!rv || d < mind) {
4315 mind = d;
4316 rv = side_port[i];
4317 }
4318 }
4319 return rv;
4320}
4321
4322port resolvePort(node_t * n, node_t * other, port * oldport)
4323{
4324 port rv;
4325 const char *compass = closestSide(n, other, oldport);
4326
4327 /* transfer name pointer; all other necessary fields will be regenerated */
4328 rv.name = oldport->name;
4329 compassPort(n, oldport->bp, &rv, compass, oldport->side, NULL);
4330
4331 return rv;
4332}
4333
4335{
4336 if (ED_tail_port(e).dyna)
4337 ED_tail_port(e) =
4339 if (ED_head_port(e).dyna)
4340 ED_head_port(e) =
4342}
static agxbuf last
last message
Definition agerror.c:31
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static char * gv_strdup(const char *original)
Definition alloc.h:101
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define BETWEEN(a, b, c)
Definition arith.h:38
#define RADIANS(deg)
Definition arith.h:49
#define SQRT2
Definition arith.h:45
#define SQR(a)
Definition arith.h:52
#define M_PI
Definition arith.h:41
#define MAX(a, b)
Definition arith.h:33
#define sub(h, i)
Definition closest.c:62
char * late_nnstring(void *obj, attrsym_t *attr, char *defaultValue)
Definition utils.c:91
bool mapbool(const char *p)
Definition utils.c:341
char * late_string(void *obj, attrsym_t *attr, char *defaultValue)
Definition utils.c:85
boxf polyBB(polygon_t *poly)
Definition utils.c:593
int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum)
Definition utils.c:40
double late_double(void *obj, attrsym_t *attr, double defaultValue, double minimum)
Definition utils.c:55
const char * safefile(const char *filename)
Definition utils.c:266
#define DEFAULT_SELECTEDFILLCOLOR
Definition const.h:53
#define BOTTOM_IX
Definition const.h:111
#define TOP_IX
Definition const.h:113
#define GAP
Definition const.h:251
#define RANKDIR_BT
Definition const.h:183
#define LEFT
Definition const.h:120
#define MC_SCALE
Definition const.h:99
#define LEFT_IX
Definition const.h:114
#define RANKDIR_TB
Definition const.h:181
#define DEFAULT_NODEPENWIDTH
Definition const.h:77
#define DEFAULT_COLOR
Definition const.h:48
#define RANKDIR_LR
Definition const.h:182
#define RIGHT
Definition const.h:118
#define DEFAULT_ACTIVEFILLCOLOR
Definition const.h:50
#define MIN_NODEWIDTH
Definition const.h:75
#define MIN_NODEPENWIDTH
Definition const.h:78
#define DEFAULT_FILL
Definition const.h:69
#define DEFAULT_DELETEDFILLCOLOR
Definition const.h:56
#define RANKDIR_RL
Definition const.h:184
#define DEFAULT_SELECTEDPENCOLOR
Definition const.h:52
#define GRADIENT
Definition const.h:223
#define DEFAULT_VISITEDFILLCOLOR
Definition const.h:59
#define BOTTOM
Definition const.h:117
#define RIGHT_IX
Definition const.h:112
#define DEFAULT_DELETEDPENCOLOR
Definition const.h:55
#define RGRADIENT
Definition const.h:224
#define MIN_NODEHEIGHT
Definition const.h:73
#define DEFAULT_VISITEDPENCOLOR
Definition const.h:58
@ LARROW
Definition const.h:216
@ LPROMOTER
Definition const.h:217
@ NOVERHANG
Definition const.h:206
@ FIVEPOVERHANG
Definition const.h:204
@ UTR
Definition const.h:201
@ SIGNATURE
Definition const.h:208
@ ASSEMBLY
Definition const.h:207
@ BOX3D
Definition const.h:196
@ RPROMOTER
Definition const.h:214
@ PRIMERSITE
Definition const.h:202
@ PROTEINSTAB
Definition const.h:213
@ TERMINATOR
Definition const.h:200
@ CYLINDER
Definition const.h:218
@ TAB
Definition const.h:194
@ INSULATOR
Definition const.h:209
@ RARROW
Definition const.h:215
@ COMPONENT
Definition const.h:197
@ THREEPOVERHANG
Definition const.h:205
@ PROTEASESITE
Definition const.h:212
@ CDS
Definition const.h:199
@ DOGEAR
Definition const.h:193
@ RIBOSITE
Definition const.h:210
@ FOLDER
Definition const.h:195
@ RESTRICTIONSITE
Definition const.h:203
@ PROMOTER
Definition const.h:198
@ RNASTAB
Definition const.h:211
#define DEFAULT_ACTIVEPENCOLOR
Definition const.h:49
#define TOP
Definition const.h:119
mode
Definition cvtgxl.c:33
static float dy
Definition draw.c:43
static float dx
Definition draw.c:42
int stripedBox(GVJ_t *job, pointf *AF, const char *clrs, int rotate)
Definition emit.c:595
bool findStopColor(const char *colorlist, char *clrs[2], double *frac)
Definition emit.c:4337
char ** parse_style(char *s)
Definition emit.c:4010
int wedgedEllipse(GVJ_t *job, pointf *pf, const char *clrs)
Definition emit.c:549
#define O
Definition gdefs.h:8
boxf flip_rec_boxf(boxf b, pointf p)
Definition geom.c:166
pointf ccwrotatepf(pointf p, int ccwrot)
Definition geom.c:148
pointf cwrotatepf(pointf p, int cwrot)
Definition geom.c:130
#define PS2INCH(a_points)
Definition geom.h:64
struct pointf_s pointf
#define INSIDE(p, b)
Definition geom.h:45
#define DIST2(p, q)
Definition geom.h:55
#define POINTS_PER_INCH
Definition geom.h:58
#define INCH2PS(a_inches)
Definition geom.h:63
double xmax
Definition geometry.c:17
double ymax
Definition geometry.c:17
static WUR pointf mid_pointf(pointf p, pointf q)
Definition geomprocs.h:104
static WUR pointf sub_pointf(pointf p, pointf q)
Definition geomprocs.h:96
static WUR pointf add_pointf(pointf p, pointf q)
Definition geomprocs.h:88
static WUR pointf interpolate_pointf(double t, pointf p, pointf q)
Definition geomprocs.h:112
Agsym_t * N_imagescale
Definition globals.h:80
Agsym_t * N_width
Definition globals.h:77
Agsym_t * N_orientation
Definition globals.h:80
Agsym_t * N_sides
Definition globals.h:79
Agsym_t * N_style
Definition globals.h:79
Agsym_t * N_penwidth
Definition globals.h:81
Agsym_t * N_gradientangle
Definition globals.h:82
Agsym_t * N_skew
Definition globals.h:80
Agsym_t * N_nojustify
Definition globals.h:79
Agsym_t * N_fillcolor
Definition globals.h:78
Agsym_t * N_fixed
Definition globals.h:80
Agsym_t * N_peripheries
Definition globals.h:79
Agsym_t * N_distortion
Definition globals.h:80
Agsym_t * N_imagepos
Definition globals.h:81
Agsym_t * N_color
Definition globals.h:77
Agsym_t * N_height
Definition globals.h:77
const char ** Lib
Definition globals.h:47
static double len(glCompPoint p)
Definition glutils.c:138
void free(void *)
#define FILL
Definition gmlparse.h:119
node NULL
Definition grammar.y:181
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:204
char * agget(void *obj, char *name)
Definition attr.c:447
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:457
#define agtail(e)
Definition cgraph.h:982
#define aghead(e)
Definition cgraph.h:983
#define ED_head_port(e)
Definition types.h:588
#define ED_tail_port(e)
Definition types.h:597
void agwarningf(const char *fmt,...)
Definition agerror.c:175
void agerrorf(const char *fmt,...)
Definition agerror.c:167
int agerr(agerrlevel_t level, const char *fmt,...)
Definition agerror.c:157
@ AGPREV
Definition cgraph.h:951
#define GD_realflip(g)
Definition types.h:380
#define GD_drawing(g)
Definition types.h:353
#define GD_has_images(g)
Definition types.h:369
#define GD_rankdir(g)
Definition types.h:377
#define GD_flip(g)
Definition types.h:378
#define ND_outline_width(n)
Definition types.h:516
#define ND_outline_height(n)
Definition types.h:517
#define ND_gui_state(n)
Definition types.h:494
#define ND_ht(n)
Definition types.h:500
#define ND_label(n)
Definition types.h:502
#define ND_rw(n)
Definition types.h:525
#define ND_height(n)
Definition types.h:498
#define ND_width(n)
Definition types.h:536
#define ND_lw(n)
Definition types.h:506
#define ND_shape_info(n)
Definition types.h:529
#define ND_coord(n)
Definition types.h:490
#define ND_shape(n)
Definition types.h:528
Agraph_t * agraphof(void *obj)
Definition obj.c:187
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
static void indent(int ix)
Definition gv2gml.c:96
Arithmetic helper functions.
static bool is_exactly_equal(double a, double b)
are two values precisely the same?
Definition gv_math.h:49
static bool is_exactly_pos_zero(double v)
is a value precisely +0.0?
Definition gv_math.h:66
#define EMIT_CLUSTERS_LAST
Definition gvcjob.h:84
@ EMIT_NLABEL
Definition gvcjob.h:176
static void color(Agraph_t *g)
Definition gvcolor.c:118
int LR
Definition gvcolor.c:53
void gvrender_beziercurve(GVJ_t *job, pointf *AF, size_t n, int filled)
Definition gvrender.c:579
void gvrender_usershape(GVJ_t *job, char *name, pointf *AF, size_t n, bool filled, char *imagescale, char *imagepos)
Definition gvrender.c:670
void gvrender_set_style(GVJ_t *job, char **s)
Definition gvrender.c:481
void gvrender_set_fillcolor(GVJ_t *job, char *name)
Definition gvrender.c:450
void gvrender_polyline(GVJ_t *job, pointf *AF, size_t n)
Definition gvrender.c:596
void gvrender_polygon(GVJ_t *job, pointf *af, size_t n, int filled)
Definition gvrender.c:537
point gvusershape_size(graph_t *g, char *name)
void gvrender_box(GVJ_t *job, boxf BF, int filled)
Definition gvrender.c:565
void gvrender_set_gradient_vals(GVJ_t *job, char *stopcolor, int angle, double frac)
Definition gvrender.c:467
void gvrender_ellipse(GVJ_t *job, pointf *AF, int filled)
Definition gvrender.c:520
void gvrender_begin_anchor(GVJ_t *job, char *href, char *tooltip, char *target, char *id)
Definition gvrender.c:373
void gvrender_end_anchor(GVJ_t *job)
Definition gvrender.c:384
void gvrender_set_penwidth(GVJ_t *job, double penwidth)
Definition gvrender.c:798
void gvrender_set_pencolor(GVJ_t *job, char *name)
Definition gvrender.c:433
static double penwidth[]
#define B
Definition hierarchy.c:120
#define D
Definition hierarchy.c:122
textitem scanner parser str
Definition htmlparse.y:218
boxf * html_port(node_t *n, char *pname, unsigned char *sides)
Definition htmltable.c:914
textlabel_t * make_label(void *obj, char *str, bool is_html, bool is_record, double fontsize, char *fontname, char *fontcolor)
Definition labels.c:110
void free_label(textlabel_t *p)
Definition labels.c:204
void emit_label(GVJ_t *job, emit_state_t emit_state, textlabel_t *lp)
Definition labels.c:217
#define PAD(d)
Definition macros.h:29
static boxf bbox(Ppoly_t **obsp, int npoly, int *np)
NEATOPROCS_API void s1(graph_t *, node_t *)
Definition stuff.c:651
arithmetic overflow helpers
static bool fdiv_overflow(double n, double d, double *res)
Definition overflow.h:142
#define C
Definition pack.c:33
void epsf_free(node_t *n)
void epsf_init(node_t *n)
Definition psusershape.c:99
pointf coord(node_t *n)
Definition utils.c:157
void bezier_clip(inside_t *inside_context, bool(*insidefn)(inside_t *inside_context, pointf p), pointf *sp, bool left_inside)
Definition splines.c:107
static field_t * parse_error(field_t *rv, char *portname)
Definition shapes.c:3352
static polygon_t p_terminator
Definition shapes.c:168
static polygon_t p_larrow
Definition shapes.c:201
#define alpha4
Definition shapes.c:4037
static char ** checkStyle(node_t *n, graphviz_polygon_style_t *flagp)
Definition shapes.c:466
static polygon_t p_proteasesite
Definition shapes.c:178
static bool poly_inside(inside_t *inside_context, pointf p)
Definition shapes.c:2402
static polygon_t p_triangle
Definition shapes.c:103
#define HASPORT
Definition shapes.c:3324
static char * findFillDflt(node_t *n, char *dflt)
Definition shapes.c:402
static double quant(double val, double q)
Definition shapes.c:368
static bool IS_BOX(node_t *n)
Definition shapes.c:206
static polygon_t p_proteinstab
Definition shapes.c:180
static void record_init(node_t *n)
Definition shapes.c:3663
static pointf cylinder_size(pointf)
Definition shapes.c:4153
static poly_desc_t star_gen
Definition shapes.c:81
static void record_gencode(GVJ_t *job, node_t *n)
Definition shapes.c:3862
static shape_desc * user_shape(char *name)
Definition shapes.c:3949
static pointf star_size(pointf)
Definition shapes.c:4039
static bool point_inside(inside_t *inside_context, pointf p)
Definition shapes.c:3194
static void poly_gencode(GVJ_t *job, node_t *n)
Definition shapes.c:2922
static shape_desc Shapes[]
Definition shapes.c:293
static polygon_t p_parallelogram
Definition shapes.c:110
static polygon_t p_house
Definition shapes.c:111
static polygon_t p_box
Definition shapes.c:104
static polygon_t p_Mcircle
Definition shapes.c:153
static polygon_t p_doublecircle
Definition shapes.c:134
static double invflip_angle(int angle, int rankdir)
Definition shapes.c:2607
static polygon_t p_insulator
Definition shapes.c:172
static int record_path(node_t *n, port *p, int side, boxf rv[], int *kptr)
Definition shapes.c:3793
#define INTEXT
Definition shapes.c:3326
static polygon_t p_rnastab
Definition shapes.c:176
#define HASTABLE
Definition shapes.c:3325
static shape_functions poly_fns
Definition shapes.c:244
static shape_functions star_fns
Definition shapes.c:276
static graphviz_polygon_style_t style_or(graphviz_polygon_style_t a, graphviz_polygon_style_t b)
bitwise-OR styles
Definition shapes.c:443
static polygon_t p_diamond
Definition shapes.c:108
void resolvePorts(edge_t *e)
Definition shapes.c:4334
static bool epsf_inside(inside_t *inside_context, pointf p)
Definition shapes.c:3992
static const char * side_port[]
Definition shapes.c:4221
static polygon_t p_signature
Definition shapes.c:195
static bool SPECIAL_CORNERS(graphviz_polygon_style_t style)
True if style requires processing through round_corners.
Definition shapes.c:215
static bool star_inside(inside_t *inside_context, pointf p)
Definition shapes.c:4089
bool isPolygon(node_t *n)
Definition shapes.c:1929
static void point_init(node_t *n)
Definition shapes.c:3103
static field_t * parse_reclbl(node_t *n, bool LR, bool flag, char *text)
Definition shapes.c:3358
static size_t N_UserShape
Definition shapes.c:3936
#define INPORT
Definition shapes.c:3327
static polygon_t p_tripleoctagon
Definition shapes.c:143
static bool record_inside(inside_t *inside_context, pointf p)
Definition shapes.c:3762
static double userSize(node_t *n)
Definition shapes.c:1901
#define alpha3
Definition shapes.c:4036
static polygon_t p_rarrow
Definition shapes.c:199
#define alpha2
Definition shapes.c:4035
static char * reclblp
Definition shapes.c:3333
void round_corners(GVJ_t *job, pointf *AF, size_t sides, graphviz_polygon_style_t style, int filled)
Handle some special graphical cases, such as rounding the shape, adding diagonals at corners,...
Definition shapes.c:710
static polygon_t p_star
Definition shapes.c:159
static port record_port(node_t *n, char *portname, char *)
Definition shapes.c:3732
static graphviz_polygon_style_t stylenode(GVJ_t *job, node_t *n)
Definition shapes.c:532
static polygon_t p_plain
Definition shapes.c:107
static void star_vertices(pointf *, pointf *)
Definition shapes.c:4054
static void cylinder_vertices(pointf *, pointf *)
Definition shapes.c:4159
static polygon_t p_plaintext
Definition shapes.c:106
static polygon_t p_cylinder
Definition shapes.c:128
static shape_functions point_fns
Definition shapes.c:252
static polygon_t p_trapezium
Definition shapes.c:109
#define RBCURVE
Definition shapes.c:32
static polygon_t p_invtriangle
Definition shapes.c:136
#define HASTEXT
Definition shapes.c:3323
shape_kind shapeOf(node_t *n)
Definition shapes.c:1909
static int same_side(pointf p0, pointf p1, pointf L0, pointf L1)
Definition shapes.c:374
static pointf * alloc_interpolation_points(pointf *AF, size_t sides, graphviz_polygon_style_t style, bool rounded)
Definition shapes.c:567
static polygon_t p_Msquare
Definition shapes.c:149
static char * findFill(node_t *n)
Definition shapes.c:418
#define DEF_POINT
Definition shapes.c:43
static bool isBox(node_t *n)
Definition shapes.c:423
static polygon_t p_lpromoter
Definition shapes.c:203
static field_t * map_rec_port(field_t *f, char *str)
Definition shapes.c:3716
static void rounded_draw(GVJ_t *job, pointf *AF, size_t sides, graphviz_polygon_style_t style, int filled)
draws rounded polygons with Bézier curve
Definition shapes.c:644
static polygon_t p_invtrapezium
Definition shapes.c:138
static void resize_reclbl(field_t *f, pointf sz, bool nojustify_p)
Definition shapes.c:3547
static polygon_t p_pentagon
Definition shapes.c:112
static char * point_style[3]
Definition shapes.c:52
static polygon_t p_restrictionsite
Definition shapes.c:185
static int poly_path(node_t *n, port *p, int side, boxf rv[], int *kptr)
Definition shapes.c:2536
static polygon_t p_tab
Definition shapes.c:118
static polygon_t p_octagon
Definition shapes.c:115
static port Center
Definition shapes.c:39
static pointf size_reclbl(node_t *n, field_t *f)
Definition shapes.c:3502
#define RBCONST
Definition shapes.c:31
static polygon_t p_septagon
Definition shapes.c:114
static polygon_t p_cds
Definition shapes.c:166
static char * penColor(GVJ_t *job, node_t *n)
Definition shapes.c:390
static polygon_t p_polygon
Definition shapes.c:97
#define MIN_POINT
Definition shapes.c:48
shape_desc * bind_shape(char *name, node_t *np)
Definition shapes.c:3970
static polygon_t p_assembly
Definition shapes.c:193
static pointf compassPoint(inside_t *ictxt, double y, double x)
Definition shapes.c:2650
static polygon_t p_egg
Definition shapes.c:102
static void epsf_gencode(GVJ_t *job, node_t *n)
Definition shapes.c:4003
static polygon_t p_rpromoter
Definition shapes.c:197
static void diagonals_draw(GVJ_t *job, pointf *AF, size_t sides, graphviz_polygon_style_t style, int filled)
draws polygons with diagonals on corners
Definition shapes.c:625
#define alpha
Definition shapes.c:4034
static polygon_t p_ribosite
Definition shapes.c:174
static void gen_fields(GVJ_t *job, node_t *n, field_t *f)
Definition shapes.c:3831
static bool isEllipse(node_t *n)
Definition shapes.c:433
static polygon_t p_square
Definition shapes.c:105
static polygon_t p_box3d
Definition shapes.c:122
static shape_desc ** UserShape
Definition shapes.c:3935
static const char * closestSide(node_t *n, node_t *other, port *oldport)
Definition shapes.c:4259
static polygon_t p_fivepoverhang
Definition shapes.c:187
shape_desc * find_user_shape(const char *name)
Definition shapes.c:3938
static polygon_t p_component
Definition shapes.c:124
static unsigned char invflip_side(unsigned char side, int rankdir)
Definition shapes.c:2547
static int compassPort(node_t *n, boxf *bp, port *pp, const char *compass, unsigned char sides, inside_t *ictxt)
Definition shapes.c:2698
static polygon_t p_invhouse
Definition shapes.c:140
static polygon_t p_threepoverhang
Definition shapes.c:189
static bool multicolor(const char *f)
Definition shapes.c:2917
static bool ISCTRL(int c)
Definition shapes.c:3329
static double mid_x(const pointf line[2])
X coordinate of line midpoint.
Definition shapes.c:688
static polygon_t p_doubleoctagon
Definition shapes.c:142
static bool IS_PLAIN(node_t *n)
Definition shapes.c:210
static void record_free(node_t *n)
Definition shapes.c:3709
static void free_field(field_t *f)
Definition shapes.c:3335
static void poly_init(node_t *n)
Definition shapes.c:1934
static shape_functions record_fns
Definition shapes.c:260
static void point_gencode(GVJ_t *job, node_t *n)
Definition shapes.c:3235
port resolvePort(node_t *n, node_t *other, port *oldport)
Definition shapes.c:4322
static shape_functions epsf_fns
Definition shapes.c:268
static void pos_reclbl(field_t *f, pointf ul, unsigned char sides)
Definition shapes.c:3589
static polygon_t p_underline
Definition shapes.c:126
static polygon_t p_note
Definition shapes.c:116
static polygon_t p_promoter
Definition shapes.c:164
static polygon_t p_noverhang
Definition shapes.c:191
static void cylinder_draw(GVJ_t *job, pointf *AF, size_t sides, int filled)
Definition shapes.c:4199
static void poly_free(node_t *n)
Definition shapes.c:2386
static port poly_port(node_t *n, char *portname, char *)
Definition shapes.c:2880
static polygon_t p_hexagon
Definition shapes.c:113
static polygon_t p_primersite
Definition shapes.c:183
static polygon_t p_Mdiamond
Definition shapes.c:144
static void unrecognized(node_t *n, char *p)
Definition shapes.c:363
static poly_desc_t cylinder_gen
Definition shapes.c:89
static polygon_t p_circle
Definition shapes.c:101
static pointf cvtPt(pointf p, int rankdir)
Definition shapes.c:4223
static double mid_y(const pointf line[2])
Y coordinate of line midpoint.
Definition shapes.c:697
static void Mcircle_hack(GVJ_t *job, node_t *n)
Definition shapes.c:548
static polygon_t p_folder
Definition shapes.c:120
static polygon_t p_utr
Definition shapes.c:170
static polygon_t p_ellipse
Definition shapes.c:100
static shape_functions cylinder_fns
Definition shapes.c:284
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
graph or subgraph
Definition cgraph.h:424
int flags
Definition gvcjob.h:299
obj_state_t * obj
Definition gvcjob.h:269
FILE * output_file
Definition gvcjob.h:277
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
pointf offset
Definition render.h:46
int macro_id
Definition render.h:45
int n_flds
Definition types.h:238
textlabel_t * lp
Definition types.h:239
pointf size
Definition types.h:236
boxf b
Definition types.h:237
struct field_t ** fld
Definition types.h:240
unsigned char LR
Definition types.h:242
char * id
Definition types.h:241
unsigned char sides
Definition types.h:243
char * tooltip
Definition gvcjob.h:216
char * url
Definition gvcjob.h:210
unsigned explicit_tooltip
Definition gvcjob.h:226
char * target
Definition gvcjob.h:221
char * id
Definition gvcjob.h:211
Definition geom.h:27
int y
Definition geom.h:27
int x
Definition geom.h:27
double x
Definition geom.h:29
double y
Definition geom.h:29
pointf(* size_gen)(pointf)
Definition shapes.c:35
void(* vertex_gen)(pointf *, pointf *)
Definition shapes.c:36
size_t sides
number of sides
Definition types.h:146
int regular
Definition types.h:144
graphviz_polygon_style_t option
ROUNDED, DIAGONAL corners, etc.
Definition types.h:150
size_t peripheries
number of periphery lines
Definition types.h:145
double skew
Definition types.h:149
double orientation
Definition types.h:147
double distortion
Definition types.h:148
pointf * vertices
Definition types.h:151
Definition types.h:48
boxf * bp
Definition types.h:51
pointf p
Definition types.h:49
bool dyna
Definition types.h:57
unsigned char side
Definition types.h:59
bool clip
Definition types.h:56
double theta
Definition types.h:50
char * name
Definition types.h:63
unsigned char order
Definition types.h:58
bool constrained
Definition types.h:55
bool defined
Definition types.h:54
bool usershape
Definition types.h:193
char * name
Definition types.h:190
pointf pos
Definition types.h:114
char * fontcolor
Definition types.h:107
double fontsize
Definition types.h:109
pointf space
Definition types.h:111
htmllabel_t * html
Definition types.h:120
char * fontname
Definition types.h:106
pointf dimen
Definition types.h:110
Definition legal.c:34
struct poly_s poly
#define GUI_STATE_ACTIVE
Definition types.h:256
shape_kind
Definition types.h:187
@ SH_EPSF
Definition types.h:187
@ SH_RECORD
Definition types.h:187
@ SH_UNSET
Definition types.h:187
@ SH_POINT
Definition types.h:187
@ SH_POLY
Definition types.h:187
#define GUI_STATE_SELECTED
Definition types.h:257
#define GUI_STATE_DELETED
Definition types.h:259
#define GUI_STATE_VISITED
Definition types.h:258
double scaley
Definition types.h:167
double scalex
Definition types.h:167
struct inside_t::@57 s
double radius
last radius seen
Definition types.h:163
node_t * n
Definition types.h:160
double box_URx
Definition types.h:167
polygon_t * last_poly
last seen polygon
Definition types.h:164
node_t * lastn
last node argument
Definition types.h:162
size_t last
last used polygon vertex
Definition types.h:165
double box_URy
various computed sizes of aspects of the last seen polygon
Definition types.h:167
boxf * bp
Definition types.h:161
size_t outp
last used outline periphery
Definition types.h:166
Definition grammar.c:90
#define UNREACHABLE()
Definition unreachable.h:30