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