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