Graphviz  2.41.20171026.1811
shapes.c
Go to the documentation of this file.
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 
21 typedef struct {
22  pointf (*size_gen) (pointf);
23  void (*vertex_gen) (pointf*, pointf*);
24 } poly_desc_t;
25 
26 static 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  */
39 static char *point_style[3] = { "invis\0", "filled\0", 0 };
40 
41 /* forward declarations of functions used in shapes tables */
42 
43 static void poly_init(node_t * n);
44 static void poly_free(node_t * n);
45 static port poly_port(node_t * n, char *portname, char *);
46 static boolean poly_inside(inside_t * inside_context, pointf p);
47 static int poly_path(node_t * n, port * p, int side, boxf rv[], int *kptr);
48 static void poly_gencode(GVJ_t * job, node_t * n);
49 
50 static void record_init(node_t * n);
51 static void record_free(node_t * n);
52 static port record_port(node_t * n, char *portname, char *);
53 static boolean record_inside(inside_t * inside_context, pointf p);
54 static int record_path(node_t * n, port * p, int side, boxf rv[],
55  int *kptr);
56 static void record_gencode(GVJ_t * job, node_t * n);
57 
58 static void point_init(node_t * n);
59 static void point_gencode(GVJ_t * job, node_t * n);
60 static boolean point_inside(inside_t * inside_context, pointf p);
61 
62 static boolean epsf_inside(inside_t * inside_context, pointf p);
63 static void epsf_gencode(GVJ_t * job, node_t * n);
64 
65 static pointf star_size (pointf);
66 static void star_vertices (pointf*, pointf*);
67 static boolean star_inside(inside_t * inside_context, pointf p);
68 static poly_desc_t star_gen = {
69  star_size,
70  star_vertices,
71 };
72 
73 static pointf cylinder_size (pointf);
74 static void cylinder_vertices (pointf*, pointf*);
75 static 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); */
77 static 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 */
85 static polygon_t p_polygon = { FALSE, 1, 0, 0., 0., 0. };
86 
87 /* builtin polygon descriptions */
88 static polygon_t p_ellipse = { FALSE, 1, 1, 0., 0., 0. };
89 static polygon_t p_circle = { TRUE, 1, 1, 0., 0., 0. };
90 static polygon_t p_egg = { FALSE, 1, 1, 0., -.3, 0. };
91 static polygon_t p_triangle = { FALSE, 1, 3, 0., 0., 0. };
92 static polygon_t p_box = { FALSE, 1, 4, 0., 0., 0. };
93 static polygon_t p_square = { TRUE, 1, 4, 0., 0., 0. };
94 static polygon_t p_plaintext = { FALSE, 0, 4, 0., 0., 0. };
95 static polygon_t p_plain = { FALSE, 0, 4, 0., 0., 0. };
96 static polygon_t p_diamond = { FALSE, 1, 4, 45., 0., 0. };
97 static polygon_t p_trapezium = { FALSE, 1, 4, 0., -.4, 0. };
98 static polygon_t p_parallelogram = { FALSE, 1, 4, 0., 0., .6 };
99 static polygon_t p_house = { FALSE, 1, 5, 0., -.64, 0. };
100 static polygon_t p_pentagon = { FALSE, 1, 5, 0., 0., 0. };
101 static polygon_t p_hexagon = { FALSE, 1, 6, 0., 0., 0. };
102 static polygon_t p_septagon = { FALSE, 1, 7, 0., 0., 0. };
103 static polygon_t p_octagon = { FALSE, 1, 8, 0., 0., 0. };
104 static polygon_t p_note = { FALSE, 1, 4, 0., 0., 0., DOGEAR };
105 static polygon_t p_tab = { FALSE, 1, 4, 0., 0., 0., TAB };
106 static polygon_t p_folder = { FALSE, 1, 4, 0., 0., 0., FOLDER };
107 static polygon_t p_box3d = { FALSE, 1, 4, 0., 0., 0., BOX3D };
108 static polygon_t p_component = { FALSE, 1, 4, 0., 0., 0., COMPONENT };
109 static polygon_t p_underline = { FALSE, 1, 4, 0., 0., 0., UNDERLINE };
110 static polygon_t p_cylinder = { FALSE, 1, 19, 0., 0., 0., CYLINDER, (pointf*)&cylinder_gen };
111 
112 /* redundant and undocumented builtin polygons */
113 static polygon_t p_doublecircle = { TRUE, 2, 1, 0., 0., 0. };
114 static polygon_t p_invtriangle = { FALSE, 1, 3, 180., 0., 0. };
115 static polygon_t p_invtrapezium = { FALSE, 1, 4, 180., -.4, 0. };
116 static polygon_t p_invhouse = { FALSE, 1, 5, 180., -.64, 0. };
117 static polygon_t p_doubleoctagon = { FALSE, 2, 8, 0., 0., 0. };
118 static polygon_t p_tripleoctagon = { FALSE, 3, 8, 0., 0., 0. };
119 static polygon_t p_Mdiamond =
120  { FALSE, 1, 4, 45., 0., 0., DIAGONALS | AUXLABELS };
121 static polygon_t p_Msquare = { TRUE, 1, 4, 0., 0., 0., DIAGONALS };
122 static polygon_t p_Mcircle =
123  { TRUE, 1, 1, 0., 0., 0., DIAGONALS | AUXLABELS };
124 
125 /* non-convex polygons */
126 static polygon_t p_star = { FALSE, 1, 10, 0., 0., 0., 0, (pointf*)&star_gen };
127 
128 /* biological circuit shapes, as specified by SBOLv*/
130 static polygon_t p_promoter = { FALSE, 1, 4, 0., 0., 0., PROMOTER };
131 static polygon_t p_cds = { FALSE, 1, 4, 0., 0., 0., CDS };
132 static polygon_t p_terminator = { FALSE, 1, 4, 0., 0., 0., TERMINATOR};
133 static polygon_t p_utr = { FALSE, 1, 4, 0., 0., 0., UTR};
134 static polygon_t p_insulator = { FALSE, 1, 4, 0., 0., 0., INSULATOR};
135 static polygon_t p_ribosite = { FALSE, 1, 4, 0., 0., 0., RIBOSITE};
136 static polygon_t p_rnastab = { FALSE, 1, 4, 0., 0., 0., RNASTAB};
137 static polygon_t p_proteasesite = { FALSE, 1, 4, 0., 0., 0., PROTEASESITE};
138 static polygon_t p_proteinstab = { FALSE, 1, 4, 0., 0., 0., PROTEINSTAB};
140 static polygon_t p_primersite = { FALSE, 1, 4, 0., 0., 0., PRIMERSITE};
141 static polygon_t p_restrictionsite = { FALSE, 1, 4, 0., 0., 0., RESTRICTIONSITE};
142 static polygon_t p_fivepoverhang = { FALSE, 1, 4, 0., 0., 0., FIVEPOVERHANG};
143 static polygon_t p_threepoverhang = { FALSE, 1, 4, 0., 0., 0., THREEPOVERHANG};
144 static polygon_t p_noverhang = { FALSE, 1, 4, 0., 0., 0., NOVERHANG};
145 static polygon_t p_assembly = { FALSE, 1, 4, 0., 0., 0., ASSEMBLY};
146 static polygon_t p_signature = { FALSE, 1, 4, 0., 0., 0., SIGNATURE};
147 static polygon_t p_rpromoter = { FALSE, 1, 4, 0., 0., 0., RPROMOTER};
148 static polygon_t p_rarrow = { FALSE, 1, 4, 0., 0., 0., RARROW};
149 static polygon_t p_larrow = { FALSE, 1, 4, 0., 0., 0., LARROW};
150 static 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 
184 static shape_functions poly_fns = {
185  poly_init,
186  poly_free,
187  poly_port,
188  poly_inside,
189  poly_path,
190  poly_gencode
191 };
192 static shape_functions point_fns = {
193  point_init,
194  poly_free,
195  poly_port,
196  point_inside,
197  NULL,
198  point_gencode
199 };
200 static shape_functions record_fns = {
201  record_init,
202  record_free,
203  record_port,
204  record_inside,
205  record_path,
206  record_gencode
207 };
208 static shape_functions epsf_fns = {
209  epsf_init,
210  epsf_free,
211  poly_port,
212  epsf_inside,
213  NULL,
214  epsf_gencode
215 };
216 static shape_functions star_fns = {
217  poly_init,
218  poly_free,
219  poly_port,
220  star_inside,
221  poly_path,
222  poly_gencode
223 };
224 static shape_functions cylinder_fns = {
225  poly_init,
226  poly_free,
227  poly_port,
228  poly_inside,
229  poly_path,
230  poly_gencode
231 };
232 
233 static 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*/
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},
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 
303 static void unrecognized(node_t * n, char *p)
304 {
305  agerr(AGWARN, "node %s, port %s unrecognized\n", agnameof(n), p);
306 }
307 
308 static 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 */
318 static 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 
333 static
334 char* 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 
345 static
346 char *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 
361 static
362 char *findFill(node_t * n)
363 {
364  return (findFillDflt(n, DEFAULT_FILL));
365 }
366 
367 char *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 
380 static int
381 isBox (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  */
394 static int
395 isEllipse(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 
406 static 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 
472 static 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 
489 static 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  */
516 void 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  */
1812 static 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 
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 
1840 boolean isPolygon(node_t * n)
1841 {
1842  return (ND_shape(n) && (ND_shape(n)->fns->initfn == poly_init));
1843 }
1844 
1845 static 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 
2243 static 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  */
2261 static 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  */
2385 static 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  */
2397 static 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  */
2452 static 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  */
2502 static 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  */
2550 static int
2551 compassPort(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 
2734 static 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 */
2774 static 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))
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) {
2817  gvrender_set_pencolor(job, pencolor);
2818  color =
2820  gvrender_set_fillcolor(job, color);
2821  filled = FILL;
2822  } else if (ND_gui_state(n) & GUI_STATE_SELECTED) {
2823  pencolor =
2825  gvrender_set_pencolor(job, pencolor);
2826  color =
2829  gvrender_set_fillcolor(job, color);
2830  filled = FILL;
2831  } else if (ND_gui_state(n) & GUI_STATE_DELETED) {
2832  pencolor =
2834  gvrender_set_pencolor(job, pencolor);
2835  color =
2837  gvrender_set_fillcolor(job, color);
2838  filled = FILL;
2839  } else if (ND_gui_state(n) & GUI_STATE_VISITED) {
2840  pencolor =
2842  gvrender_set_pencolor(job, pencolor);
2843  color =
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
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)
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  */
2992 static 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 
3058 static 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 
3093 static 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))
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) {
3129  gvrender_set_pencolor(job, color);
3130  color =
3132  gvrender_set_fillcolor(job, color);
3133  } else if (ND_gui_state(n) & GUI_STATE_SELECTED) {
3134  color =
3136  gvrender_set_pencolor(job, color);
3137  color =
3140  gvrender_set_fillcolor(job, color);
3141  } else if (ND_gui_state(n) & GUI_STATE_DELETED) {
3142  color =
3144  gvrender_set_pencolor(job, color);
3145  color =
3147  gvrender_set_fillcolor(job, color);
3148  } else if (ND_gui_state(n) & GUI_STATE_VISITED) {
3149  color =
3151  gvrender_set_pencolor(job, color);
3152  color =
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)
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 
3199 static char *reclblp;
3200 
3201 static 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  */
3218 static 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 
3226 static 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 
3374 static 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 
3419 static 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  */
3462 static 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
3504 static void indent(int l)
3505 {
3506  int i;
3507  for (i = 0; i < l; i++)
3508  fputs(" ", stderr);
3509 }
3510 
3511 static 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 
3517 static 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 */
3536 static 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 
3581 static void record_free(node_t * n)
3582 {
3583  field_t *p = ND_shape_info(n);
3584 
3585  free_field(p);
3586 }
3587 
3588 static 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 
3604 static 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  */
3634 static 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  */
3658 static 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 
3695 static 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 
3726 static 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))
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
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)
3794  obj->url, obj->tooltip, obj->target,
3795  obj->id);
3796  gvrender_end_anchor(job);
3797  }
3798 }
3799 
3800 static shape_desc **UserShape;
3801 static int N_UserShape;
3802 
3803 shape_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 
3815 static 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 
3837 shape_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 
3859 static 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 
3871 static 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))
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)
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 
3907 static 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 
3922 static 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 
3957 static 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  */
4012 static pointf cylinder_size (pointf sz)
4013 {
4014  sz.y *= 1.375;
4015  return sz;
4016 }
4017 
4018 static 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 
4058 static 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  */
4089 static boolean cylinder_inside(inside_t * inside_context, pointf p)
4090 {
4091  return TRUE;
4092 }
4093 #endif
4094 
4095 static char *side_port[] = { "s", "e", "n", "w" };
4096 
4097 static 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  */
4134 static 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 
4196 port 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 
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 
4218 void 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 }
void s1(graph_t *, node_t *)
Definition: stuff.c:686
#define LPROMOTER
Definition: const.h:249
double skew
Definition: types.h:152
shape_desc * find_user_shape(const char *)
Definition: shapes.c:3803
#define MAX(a, b)
Definition: agerror.c:17
void free_label(textlabel_t *p)
Definition: labels.c:209
#define SQR(a)
Definition: arith.h:88
void gvrender_box(GVJ_t *job, boxf BF, int filled)
Definition: gvrender.c:603
Definition: cgraph.h:388
void gvrender_polygon(GVJ_t *job, pointf *af, int n, int filled)
Definition: gvrender.c:572
Definition: types.h:67
EXTERN Agsym_t * N_selectedpencolor
Definition: globals.h:95
#define GUI_STATE_SELECTED
Definition: types.h:267
#define INSULATOR
Definition: const.h:241
#define N_NEW(n, t)
Definition: memory.h:36
#define INVISIBLE
Definition: const.h:214
Definition: legal.c:33
#define alpha2
Definition: shapes.c:3903
char ** parse_style(char *s)
Definition: emit.c:3891
double xmax
Definition: geometry.c:20
#define INPORT
Definition: shapes.c:3195
EXTERN Agsym_t * N_selectedfillcolor
Definition: globals.h:95
#define DEFAULT_DELETEDFILLCOLOR
Definition: const.h:59
#define DEF_POINT
Definition: shapes.c:30
#define MIN_NODEWIDTH
Definition: const.h:78
#define RPROMOTER
Definition: const.h:246
int option
Definition: types.h:153
htmllabel_t * html
Definition: types.h:139
#define LARROW
Definition: const.h:248
boolean clip
Definition: types.h:75
EXTERN Agsym_t * N_imagepos
Definition: globals.h:95
EXTERN Agsym_t * N_activefillcolor
Definition: globals.h:95
char * late_nnstring(void *obj, attrsym_t *attr, char *def)
Definition: utils.c:129
pointf cwrotatepf(pointf p, int cwrot)
Definition: geom.c:264
#define MIN(a, b)
Definition: arith.h:35
#define HASTABLE
Definition: shapes.c:3193
pointf size
Definition: types.h:246
Definition: types.h:245
shape_kind shapeOf(node_t *)
Definition: shapes.c:1820
boolean findStopColor(char *colorlist, char *clrs[2], float *frac)
Definition: emit.c:4223
#define SQRT2
Definition: arith.h:81
double distortion
Definition: types.h:151
#define ALLOC(size, ptr, type)
Definition: memory.h:41
unsigned char LR
Definition: types.h:252
#define SPECIAL_CORNERS(style)
Definition: shapes.c:156
#define UTR
Definition: const.h:233
#define ED_head_port(e)
Definition: types.h:591
#define C
Definition: pack.c:29
EXTERN Agsym_t * N_height
Definition: globals.h:95
Definition: legal.c:39
void gvrender_usershape(GVJ_t *job, char *name, pointf *AF, int n, boolean filled, char *imagescale, char *imagepos)
Definition: gvrender.c:718
#define GUI_STATE_VISITED
Definition: types.h:268
#define IS_PLAIN(n)
Definition: shapes.c:153
EXTERN Agsym_t * N_peripheries
Definition: globals.h:95
EXTERN Agsym_t * N_fixed
Definition: globals.h:95
#define ROUND(f)
Definition: arith.h:84
boxf flip_rec_boxf(boxf b, pointf p)
Definition: geom.c:364
#define assert(x)
Definition: cghdr.h:47
Definition: render.h:54
#define RANKDIR_BT
Definition: const.h:200
Definition: geom.h:28
char * text
Definition: types.h:124
#define UNDERLINE
Definition: const.h:219
char * fontcolor
Definition: types.h:126
#define PF2P(pf, p)
Definition: geom.h:72
boxf * bp
Definition: types.h:70
struct field_t ** fld
Definition: types.h:250
#define RIGHT
Definition: const.h:119
void gvrender_set_fillcolor(GVJ_t *job, char *name)
Definition: gvrender.c:481
char * name
Definition: types.h:189
#define ASSEMBLY
Definition: const.h:239
void emit_label(GVJ_t *job, emit_state_t emit_state, textlabel_t *lp)
Definition: labels.c:222
#define TOP
Definition: const.h:120
#define DEFAULT_SELECTEDFILLCOLOR
Definition: const.h:56
#define TAB
Definition: const.h:226
#define FIVEPOVERHANG
Definition: const.h:236
EXTERN Agsym_t * N_skew
Definition: globals.h:95
unsigned char side
Definition: types.h:78
#define alpha3
Definition: shapes.c:3904
EXTERN Agsym_t * N_gradientangle
Definition: globals.h:95
int agerr(agerrlevel_t level, const char *fmt,...)
Definition: agerror.c:141
#define FIXEDSHAPE
Definition: const.h:220
int flags
Definition: gvcjob.h:308
double fontsize
Definition: types.h:128
#define RADIAL
Definition: const.h:210
int html_path(node_t *n, port *p, int side, boxf *rv, int *k)
Definition: htmltable.c:959
boolean constrained
Definition: types.h:74
double ymax
Definition: geometry.c:20
boxf b
Definition: types.h:247
Definition: gvcjob.h:271
EXTERN Agsym_t * N_visitedfillcolor
Definition: globals.h:95
#define ND_shape_info(n)
Definition: types.h:535
#define PROTEASESITE
Definition: const.h:244
#define RBCURVE
Definition: shapes.c:19
double orientation
Definition: types.h:150
int x
Definition: geom.h:26
#define ND_label(n)
Definition: types.h:509
#define POINTS(a_inches)
Definition: geom.h:67
#define POINTS_PER_INCH
Definition: geom.h:62
EXTERN Agsym_t * N_style
Definition: globals.h:95
shape_kind
Definition: types.h:186
Definition: cgraph.h:388
obj_state_t * obj
Definition: gvcjob.h:278
#define PS2INCH(a_points)
Definition: geom.h:69
char * agget(void *obj, char *name)
Definition: attr.c:428
CGRAPH_API Agraph_t * agraphof(void *obj)
Definition: obj.c:185
CGRAPH_API Agnode_t * agtail(Agedge_t *e)
Definition: edge.c:525
point gvusershape_size(graph_t *g, char *name)
Definition: gvusershape.c:730
pointf ccwrotatepf(pointf p, int ccwrot)
Definition: geom.c:320
#define DEFAULT_DELETEDPENCOLOR
Definition: const.h:58
#define PROMOTER
Definition: const.h:230
void epsf_init(node_t *n)
Definition: psusershape.c:98
unsigned char order
Definition: types.h:77
#define ED_tail_port(e)
Definition: types.h:600
char * tooltip
Definition: gvcjob.h:225
pointf pos
Definition: types.h:133
int
Definition: grammar.c:1264
#define DEFAULT_VISITEDFILLCOLOR
Definition: const.h:62
#define ND_ht(n)
Definition: types.h:506
#define GUI_STATE_ACTIVE
Definition: types.h:266
EXTERN Agsym_t * N_imagescale
Definition: globals.h:95
CGRAPH_API Agnode_t * aghead(Agedge_t *e)
Definition: edge.c:533
#define ND_shape(n)
Definition: types.h:534
#define IS_BOX(n)
Definition: shapes.c:152
#define DEFAULT_VISITEDPENCOLOR
Definition: const.h:61
#define MIN_POINT
Definition: shapes.c:35
#define DIST2(p, q)
Definition: geom.h:59
#define INTEXT
Definition: shapes.c:3194
double y
Definition: geom.h:28
#define ROUNDED
Definition: const.h:211
EXTERN Agsym_t * N_deletedfillcolor
Definition: globals.h:95
#define HASTEXT
Definition: shapes.c:3191
CGRAPH_API char * agnameof(void *)
Definition: id.c:143
Definition: types.h:186
int macro_id
Definition: render.h:55
#define ND_height(n)
Definition: types.h:504
#define HASPORT
Definition: shapes.c:3192
#define RANKDIR_RL
Definition: const.h:201
#define PAD(d)
Definition: macros.h:41
#define DEFAULT_COLOR
Definition: const.h:51
void gvrender_begin_anchor(GVJ_t *job, char *href, char *tooltip, char *target, char *id)
Definition: gvrender.c:404
char * findAttrColor(void *obj, attrsym_t *colorattr, char *dflt)
Definition: shapes.c:367
#define LT_NONE
Definition: const.h:259
int stripedBox(GVJ_t *job, pointf *AF, char *clrs, int rotate)
Definition: emit.c:625
char * url
Definition: gvcjob.h:219
void gvrender_set_pencolor(GVJ_t *job, char *name)
Definition: gvrender.c:464
#define ND_gui_state(n)
Definition: types.h:500
#define DOGEAR
Definition: const.h:225
void epsf_free(node_t *n)
Definition: psusershape.c:121
EXTERN Agsym_t * N_activepencolor
Definition: globals.h:95
#define LT_HTML
Definition: const.h:260
htmllabel_t * lbl
Definition: htmlparse.c:81
#define RANKDIR_LR
Definition: const.h:199
#define SIGNATURE
Definition: const.h:240
#define LEFT
Definition: const.h:121
void gv_initShapes(void)
Definition: shapes.c:4218
int n_flds
Definition: types.h:248
#define ND_has_port(n)
Definition: types.h:501
#define ND_rw(n)
Definition: types.h:531
EXTERN Agsym_t * N_sides
Definition: globals.h:95
#define FOLDER
Definition: const.h:227
#define SHAPE_MASK
Definition: const.h:222
#define GRADIENT
Definition: const.h:254
#define RBCONST
Definition: shapes.c:18
int sides
Definition: types.h:149
#define RESTRICTIONSITE
Definition: const.h:235
void gvrender_set_style(GVJ_t *job, char **s)
Definition: gvrender.c:512
textlabel_t * lp
Definition: types.h:249
EXTERN Agsym_t * N_orientation
Definition: globals.h:95
#define PROTEINSTAB
Definition: const.h:245
int late_int(void *obj, attrsym_t *attr, int def, int low)
Definition: utils.c:71
Definition: grammar.c:79
#define BOTTOM
Definition: const.h:118
struct pointf_s pointf
EXTERN const char ** Lib
Definition: globals.h:57
pointf dimen
Definition: types.h:129
double theta
Definition: types.h:69
#define GD_flip(g)
Definition: types.h:385
EXTERN Agsym_t * N_penwidth
Definition: globals.h:95
void gvrender_set_gradient_vals(GVJ_t *job, char *stopcolor, int angle, float frac)
Definition: gvrender.c:498
#define BETWEEN(a, b, c)
Definition: arith.h:74
void gvrender_set_penwidth(GVJ_t *job, double penwidth)
Definition: gvrender.c:848
#define GUI_STATE_DELETED
Definition: types.h:269
#define DEFAULT_FILL
Definition: const.h:72
#define RIBOSITE
Definition: const.h:242
#define sub(h, i)
Definition: closest.c:75
EXTERN Agsym_t * N_color
Definition: globals.h:95
#define ND_width(n)
Definition: types.h:542
#define RARROW
Definition: const.h:247
#define DIAGONALS
Definition: const.h:212
const char * safefile(const char *filename)
Definition: utils.c:376
int wedgedEllipse(GVJ_t *job, pointf *pf, char *clrs)
Definition: emit.c:574
#define ND_lw(n)
Definition: types.h:513
#define DEFAULT_SELECTEDPENCOLOR
Definition: const.h:55
#define RADIANS(deg)
Definition: arith.h:85
#define NULL
Definition: logic.h:39
#define ISCTRL(c)
Definition: shapes.c:3197
#define RGRADIENT
Definition: const.h:255
#define MIN_NODEHEIGHT
Definition: const.h:76
#define FILLED
Definition: const.h:209
#define NOT(x)
Definition: cgraph.h:41
Definition: geom.h:26
double x
Definition: geom.h:28
#define GD_realflip(g)
Definition: types.h:387
#define ND_coord(n)
Definition: types.h:496
Definition: types.h:186
#define streq(s, t)
Definition: cghdr.h:52
EXTERN Agsym_t * N_nojustify
Definition: globals.h:95
double late_double(void *obj, attrsym_t *attr, double def, double low)
Definition: utils.c:87
void round_corners(GVJ_t *job, pointf *AF, int sides, int style, int filled)
Definition: shapes.c:516
boxf * bp
Definition: types.h:63
pointf p
Definition: types.h:68
#define multicolor(f)
Definition: shapes.c:2771
node_t * n
Definition: types.h:62
char * name
Definition: types.h:82
struct inside_t::@19 s
int regular
Definition: types.h:147
#define DEFAULT_ACTIVEPENCOLOR
Definition: const.h:52
#define alpha4
Definition: shapes.c:3905
boolean mapbool(char *p)
Definition: utils.c:472
pointf LL
Definition: geom.h:35
#define MC_SCALE
Definition: const.h:100
#define TERMINATOR
Definition: const.h:232
#define alpha
Definition: shapes.c:3902
#define DEFAULT_ACTIVEFILLCOLOR
Definition: const.h:53
#define INSIDE(p, b)
Definition: geom.h:39
#define BOX3D
Definition: const.h:228
#define FILL
Definition: const.h:253
#define sincos(x, s, c)
Definition: arith.h:93
char * target
Definition: gvcjob.h:230
port resolvePort(node_t *n, node_t *other, port *oldport)
Definition: shapes.c:4196
boxf * html_port(node_t *n, char *pname, int *sides)
Definition: htmltable.c:928
Definition: types.h:56
char * id
Definition: types.h:251
int peripheries
Definition: types.h:148
void(* vertex_gen)(pointf *, pointf *)
Definition: shapes.c:23
pointf(* size_gen)(pointf)
Definition: shapes.c:22
void gvrender_ellipse(GVJ_t *job, pointf *AF, int n, int filled)
Definition: gvrender.c:551
char * late_string(void *obj, attrsym_t *attr, char *def)
Definition: utils.c:122
pointf * vertices
Definition: types.h:154
EXTERN Agsym_t * N_deletedpencolor
Definition: globals.h:95
#define THREEPOVERHANG
Definition: const.h:237
#define M_PI
Definition: arith.h:77
EXTERN Agsym_t * N_visitedpencolor
Definition: globals.h:95
#define CYLINDER
Definition: const.h:250
void gvrender_beziercurve(GVJ_t *job, pointf *AF, int n, int arrow_at_start, int arrow_at_end, boolean filled)
Definition: gvrender.c:617
agxbuf * str
Definition: htmlparse.c:85
EXTERN Agsym_t * N_width
Definition: globals.h:95
#define NOVERHANG
Definition: const.h:238
boxf polyBB(polygon_t *poly)
Definition: utils.c:821
void resolvePorts(edge_t *e)
Definition: shapes.c:4208
#define GD_has_images(g)
Definition: types.h:373
#define STRIPED
Definition: const.h:215
boolean isPolygon(node_t *)
Definition: shapes.c:1840
char * agxget(void *obj, Agsym_t *sym)
Definition: attr.c:444
char * id
Definition: gvcjob.h:220
#define EMIT_CLUSTERS_LAST
Definition: gvcjob.h:86
#define GD_drawing(g)
Definition: types.h:356
pointf coord(node_t *n)
Definition: utils.c:202
int explicit_tooltip
Definition: gvcjob.h:235
#define WEDGED
Definition: const.h:218
#define COMPONENT
Definition: const.h:229
Definition: cgraph.h:388
void gvrender_end_anchor(GVJ_t *job)
Definition: gvrender.c:415
int y
Definition: geom.h:26
FILE * output_file
Definition: gvcjob.h:286
#define RANKDIR_TB
Definition: const.h:198
pointf UR
Definition: geom.h:35
#define PRIMERSITE
Definition: const.h:234
shape_desc * bind_shape(char *name, node_t *)
Definition: shapes.c:3837
EXTERN Agsym_t * N_fillcolor
Definition: globals.h:95
#define GD_rankdir(g)
Definition: types.h:384
pointf space
Definition: types.h:130
boolean usershape
Definition: types.h:192
Definition: geom.h:35
#define N_GNEW(n, t)
Definition: agxbuf.c:20
#define FALSE
Definition: cgraph.h:35
unsigned char sides
Definition: types.h:253
#define MAXDOUBLE
Definition: arith.h:64
point offset
Definition: render.h:56
#define RNASTAB
Definition: const.h:243
void bezier_clip(inside_t *inside_context, boolean(*insidefn)(inside_t *inside_context, pointf p), pointf *sp, boolean left_inside)
Definition: splines.c:106
#define CDS
Definition: const.h:231
EXTERN Agsym_t * N_distortion
Definition: globals.h:95
#define AUXLABELS
Definition: const.h:213
#define GAP
Definition: const.h:287
boolean dyna
Definition: types.h:76
textlabel_t * make_label(void *obj, char *str, int kind, double fontsize, char *fontname, char *fontcolor)
Definition: labels.c:115
void gvrender_polyline(GVJ_t *job, pointf *AF, int n)
Definition: gvrender.c:641
#define NEW(t)
Definition: memory.h:35
boolean defined
Definition: types.h:73
char * fontname
Definition: types.h:125
#define TRUE
Definition: cgraph.h:38