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