static void figptarray(GVJ_t *job, pointf * A, int n, int close) { int i; point p; for (i = 0; i < n; i++) { PF2P(A[i],p); gvprintf(job, " %d %d", p.x, p.y); } if (close) { PF2P(A[0],p); gvprintf(job, " %d %d", p.x, p.y); } gvputs(job, "\n"); }
point dotneato_closest(splines * spl, point p) { int i, j, k, besti, bestj; double bestdist2, d2, dlow2, dhigh2; /* squares of distances */ double low, high, t; pointf c[4], pt2, pt; point rv; bezier bz; besti = bestj = -1; bestdist2 = 1e+38; P2PF(p, pt); for (i = 0; i < spl->size; i++) { bz = spl->list[i]; for (j = 0; j < bz.size; j++) { pointf b; b.x = bz.list[j].x; b.y = bz.list[j].y; d2 = DIST2(b, pt); if ((bestj == -1) || (d2 < bestdist2)) { besti = i; bestj = j; bestdist2 = d2; } } } bz = spl->list[besti]; j = bestj / 3; if (j >= spl->size) j--; for (k = 0; k < 4; k++) { c[k].x = bz.list[j + k].x; c[k].y = bz.list[j + k].y; } low = 0.0; high = 1.0; dlow2 = DIST2(c[0], pt); dhigh2 = DIST2(c[3], pt); do { t = (low + high) / 2.0; pt2 = Bezier(c, 3, t, NULL, NULL); if (fabs(dlow2 - dhigh2) < 1.0) break; if (fabs(high - low) < .00001) break; if (dlow2 < dhigh2) { high = t; dhigh2 = DIST2(pt2, pt); } else { low = t; dlow2 = DIST2(pt2, pt); } } while (1); PF2P(pt2, rv); return rv; }
/* Ensures that a new canvas item is created for this node and moved to right * position, or just moves an existing canvas item if already created. */ static void cls_node_ensure_draw (gpointer klass_id, ClsNode *cls_node, ClsBox *bounding_box) { gdouble x, y; point node_pos; #ifndef ND_coord_i pointf node_posf; #endif /* get some infos from the node */ #ifdef ND_coord_i node_pos = ND_coord_i (cls_node->agnode); #else node_posf = ND_coord (cls_node->agnode); PF2P (node_posf, node_pos); #endif /* Determine node coords and size *in* canvas world coordinate system */ cls_node->width = INCH_TO_PIXELS (ND_width (cls_node->agnode)); cls_node->height = INCH_TO_PIXELS (ND_height (cls_node->agnode)); cls_node->x1 = node_pos.x - cls_node->width/2; cls_node->y1 = GRAPH_TO_CANVAS_Y ((node_pos.y + cls_node->height/2)); cls_node->x2 = node_pos.x + cls_node->width/2; cls_node->y2 = GRAPH_TO_CANVAS_Y ((node_pos.y - cls_node->height/2)); if (cls_node->x1 < bounding_box->x1) bounding_box->x1 = cls_node->x1; if (cls_node->y1 < bounding_box->y1) bounding_box->y1 = cls_node->y1; if (cls_node->x2 > bounding_box->x2) bounding_box->x2 = cls_node->x2; if (cls_node->y2 > bounding_box->y2) bounding_box->y2 = cls_node->y2; if (cls_node->canvas_group == NULL || cls_node->drawn_expansion_status != cls_node->expansion_status) { if (cls_node->expansion_status == CLS_NODE_COLLAPSED) cls_node_draw_collapsed (cls_node); else cls_node_draw_expanded (cls_node); } /* Move the canvas item to right place */ x = cls_node->x1; y = cls_node->y1; foo_canvas_item_set (cls_node->canvas_group, "x", x, "y", y, NULL); g_hash_table_foreach (cls_node->edges_to, (GHFunc) cls_node_draw_edge, cls_node); }
static void map_output_shape (GVJ_t *job, map_shape_t map_shape, pointf * AF, int nump, char* url, char *tooltip, char *target) { int i; static point *A; static int size_A; if (!AF || !nump) return; if (size_A < nump) { size_A = nump + 10; A = realloc(A, size_A * sizeof(point)); } for (i = 0; i < nump; i++) PF2P(AF[i], A[i]); if (job->render.id == FORMAT_IMAP && url && url[0]) { switch (map_shape) { case MAP_RECTANGLE: /* Y_GOES_DOWN so need UL to LR */ core_printf(job, "rect %s %d,%d %d,%d\n", url, A[0].x, A[1].y, A[1].x, A[0].y); break; case MAP_CIRCLE: core_printf(job, "circle %s %d,%d,%d\n", url, A[0].x, A[0].y, A[1].x-A[0].x); break; case MAP_POLYGON: core_printf(job, "poly %s", url); for (i = 0; i < nump; i++) core_printf(job, " %d,%d", A[i].x, A[i].y); core_fputs(job, "\n"); break; default: assert(0); break; } } else if (job->render.id == FORMAT_ISMAP && url && url[0]) { switch (map_shape) { case MAP_RECTANGLE: /* Y_GOES_DOWN so need UL to LR */ core_printf(job, "rectangle (%d,%d) (%d,%d) %s %s\n", A[0].x, A[1].y, A[1].x, A[0].y, url, tooltip); break; default: assert(0); break; } } else if (job->render.id == FORMAT_CMAP || job->render.id == FORMAT_CMAPX) { switch (map_shape) { case MAP_CIRCLE: core_fputs(job, "<area shape=\"circle\""); break; case MAP_RECTANGLE: core_fputs(job, "<area shape=\"rect\""); break; case MAP_POLYGON: core_fputs(job, "<area shape=\"poly\""); break; default: assert(0); break; } if (url && url[0]) { core_fputs(job, " href=\""); core_fputs(job, xml_string(url)); core_fputs(job, "\""); } if (target && target[0]) { core_fputs(job, " target=\""); core_fputs(job, xml_string(target)); core_fputs(job, "\""); } if (tooltip && tooltip[0]) { core_fputs(job, " title=\""); core_fputs(job, xml_string(tooltip)); core_fputs(job, "\""); } /* * alt text is intended for the visually impaired, but such * folk are not likely to be clicking around on a graph anyway. * IE on the PC platform (but not on Macs) incorrectly * uses (non-empty) alt strings instead of title strings for tooltips. * To make tooltips work and avoid this IE issue, * while still satisfying usability guidelines * that require that there is always an alt string, * we generate just an empty alt string. */ core_fputs(job, " alt=\"\""); core_fputs(job, " coords=\""); switch (map_shape) { case MAP_CIRCLE: core_printf(job, "%d,%d,%d", A[0].x, A[0].y, A[1].x); break; case MAP_RECTANGLE: /* Y_GOES_DOWN so need UL to LR */ core_printf(job, "%d,%d,%d,%d", A[0].x, A[1].y, A[1].x, A[0].y); break; case MAP_POLYGON: core_printf(job, "%d,%d", A[0].x, A[0].y); for (i = 1; i < nump; i++) core_printf(job, " %d,%d", A[i].x, A[i].y); break; default: break; } if (job->render.id == FORMAT_CMAPX) core_fputs(job, "\"/>\n"); else core_fputs(job, "\">\n"); } }
static void vrml_ellipse(GVJ_t * job, pointf * A, int filled) { FILE *out = job->output_file; obj_state_t *obj = job->obj; node_t *n; edge_t *e; double z = obj->z; double rx, ry; int dx, dy; pointf npf, nqf; point np; int pen; gdImagePtr brush = NULL; rx = A[1].x - A[0].x; ry = A[1].y - A[0].y; switch (obj->type) { case ROOTGRAPH_OBJTYPE: case CLUSTER_OBJTYPE: break; case NODE_OBJTYPE: n = obj->u.n; if (shapeOf(n) == SH_POINT) { doSphere (job, n, A[0], z, rx, ry); return; } pen = set_penstyle(job, im, brush); npf = vrml_node_point(job, n, A[0]); nqf = vrml_node_point(job, n, A[1]); dx = ROUND(2 * (nqf.x - npf.x)); dy = ROUND(2 * (nqf.y - npf.y)); PF2P(npf, np); if (filled) gdImageFilledEllipse(im, np.x, np.y, dx, dy, color_index(im, obj->fillcolor)); gdImageArc(im, np.x, np.y, dx, dy, 0, 360, pen); if (brush) gdImageDestroy(brush); fprintf(out, "Transform {\n"); fprintf(out, " translation %.3f %.3f %.3f\n", A[0].x, A[0].y, z); fprintf(out, " scale %.3f %.3f 1\n", rx, ry); fprintf(out, " children [\n"); fprintf(out, " Transform {\n"); fprintf(out, " rotation 1 0 0 1.57\n"); fprintf(out, " children [\n"); fprintf(out, " Shape {\n"); fprintf(out, " geometry Cylinder { side FALSE }\n"); fprintf(out, " appearance Appearance {\n"); fprintf(out, " material Material {\n"); fprintf(out, " ambientIntensity 0.33\n"); fprintf(out, " diffuseColor 1 1 1\n"); fprintf(out, " }\n"); fprintf(out, " texture ImageTexture { url \"node%d.png\" }\n", n->id); fprintf(out, " }\n"); fprintf(out, " }\n"); fprintf(out, " ]\n"); fprintf(out, " }\n"); fprintf(out, " ]\n"); fprintf(out, "}\n"); break; case EDGE_OBJTYPE: e = obj->u.e; /* this is gruesome, but how else can we get z coord */ if (DIST2(A[0], ND_coord_i(e->tail)) < DIST2(A[0], ND_coord_i(e->head))) z = obj->tail_z; else z = obj->head_z; fprintf(out, "Transform {\n"); fprintf(out, " translation %.3f %.3f %.3f\n", A[0].x, A[0].y, z); fprintf(out, " children [\n"); fprintf(out, " Shape {\n"); fprintf(out, " geometry Sphere {radius %.3f }\n", (double) rx); fprintf(out, " appearance USE E%d\n", e->id); fprintf(out, " }\n"); fprintf(out, " ]\n"); fprintf(out, "}\n"); } }
/* do_graph_label: * Set characteristics of graph label if it exists. * */ void do_graph_label(graph_t * sg) { char *p, *pos, *just; int pos_ix; /* it would be nice to allow multiple graph labels in the future */ if ((p = agget(sg, "label"))) { char pos_flag; int lbl_kind = LT_NONE; point dpt; pointf dimen; if (aghtmlstr(p)) lbl_kind = LT_HTML; GD_has_labels(sg->root) |= GRAPH_LABEL; if (lbl_kind) p = strdup (p); else p = strdup_and_subst_obj(p, (void*)sg); GD_label(sg) = make_label(sg->root, lbl_kind, p, late_double(sg, agfindattr(sg, "fontsize"), DEFAULT_FONTSIZE, MIN_FONTSIZE), late_nnstring(sg, agfindattr(sg, "fontname"), DEFAULT_FONTNAME), late_nnstring(sg, agfindattr(sg, "fontcolor"), DEFAULT_COLOR)); if (lbl_kind) { if (make_html_label(sg->root, GD_label(sg), sg) == 1) agerr(AGPREV, "in label of graph %s\n", sg->name); } /* set label position */ pos = agget(sg, "labelloc"); if (sg != sg->root) { if (pos && (pos[0] == 'b')) pos_flag = LABEL_AT_BOTTOM; else pos_flag = LABEL_AT_TOP; } else { if (pos && (pos[0] == 't')) pos_flag = LABEL_AT_TOP; else pos_flag = LABEL_AT_BOTTOM; } just = agget(sg, "labeljust"); if (just) { if (just[0] == 'l') pos_flag |= LABEL_AT_LEFT; else if (just[0] == 'r') pos_flag |= LABEL_AT_RIGHT; } GD_label_pos(sg) = pos_flag; if (sg == sg->root) return; /* Set border information for cluster labels to allow space */ dimen = GD_label(sg)->dimen; PAD(dimen); PF2P(dimen, dpt); if (!GD_flip(sg->root)) { if (GD_label_pos(sg) & LABEL_AT_TOP) pos_ix = TOP_IX; else pos_ix = BOTTOM_IX; GD_border(sg)[pos_ix] = dpt; } else { /* when rotated, the labels will be restored to TOP or BOTTOM */ if (GD_label_pos(sg) & LABEL_AT_TOP) pos_ix = RIGHT_IX; else pos_ix = LEFT_IX; GD_border(sg)[pos_ix].x = dpt.y; GD_border(sg)[pos_ix].y = dpt.x; } } }
/* dotneato_postprocess: * Set graph and cluster label positions. * Add space for root graph label and translate graph accordingly. * Set final nodesize using ns. * Assumes the boxes of all clusters have been computed. * When done, the bounding box of g has LL at origin. */ void dotneato_postprocess(Agraph_t * g) { int diff; pointf dimen; point d = { 0, 0 }; Rankdir = GD_rankdir(g); Flip = GD_flip(g); if (Flip) place_flip_graph_label(g); else place_graph_label(g); if (GD_label(g) && !GD_label(g)->set) { dimen = GD_label(g)->dimen; PAD(dimen); PF2P(dimen, d); if (Flip) { if (GD_label_pos(g) & LABEL_AT_TOP) { GD_bb(g).UR.x += d.y; } else { GD_bb(g).LL.x -= d.y; } if (d.x > GD_bb(g).UR.y - GD_bb(g).LL.y) { diff = d.x - (GD_bb(g).UR.y - GD_bb(g).LL.y); diff = diff / 2; GD_bb(g).LL.y -= diff; GD_bb(g).UR.y += diff; } } else { if (GD_label_pos(g) & LABEL_AT_TOP) { if (Rankdir == RANKDIR_TB) GD_bb(g).UR.y += d.y; else GD_bb(g).LL.y -= d.y; } else { if (Rankdir == RANKDIR_TB) GD_bb(g).LL.y -= d.y; else GD_bb(g).UR.y += d.y; } if (d.x > GD_bb(g).UR.x - GD_bb(g).LL.x) { diff = d.x - (GD_bb(g).UR.x - GD_bb(g).LL.x); diff = diff / 2; GD_bb(g).LL.x -= diff; GD_bb(g).UR.x += diff; } } } switch (Rankdir) { case RANKDIR_TB: Offset = GD_bb(g).LL; break; case RANKDIR_LR: Offset = pointof(-GD_bb(g).UR.y, GD_bb(g).LL.x); break; case RANKDIR_BT: Offset = pointof(GD_bb(g).LL.x, -GD_bb(g).UR.y); break; case RANKDIR_RL: Offset = pointof(GD_bb(g).LL.y, GD_bb(g).LL.x); break; } translate_drawing(g); if (GD_label(g) && !GD_label(g)->set) place_root_label(g, d); if (Show_boxes) { char buf[BUFSIZ]; if (Flip) sprintf(buf, M2, Offset.x, Offset.y, Offset.x, Offset.y); else sprintf(buf, M1, Offset.y, Offset.x, Offset.y, Offset.x, -Offset.x, -Offset.y); Show_boxes[0] = strdup(buf); } }
/* finalCC: * Set graph bounding box given list of connected * components, each with its bounding box set. * If c_cnt > 1, then pts != NULL and gives translations for components. * Add margin about whole graph unless isRoot is true. * Reposition nodes based on final position of * node's connected component. * Also, entire layout is translated to origin. */ static void finalCC(graph_t * g, int c_cnt, graph_t ** cc, point * pts, graph_t * rg, layout_info* infop) { attrsym_t * G_width = infop->G_width; attrsym_t * G_height = infop->G_height; graph_t *cg; box b, bb; boxf bbf; point pt; int margin; graph_t **cp = cc; point *pp = pts; int isRoot = (rg == infop->rootg); int isEmpty = 0; /* compute graph bounding box in points */ if (c_cnt) { cg = *cp++; BF2B(GD_bb(cg), bb); if (c_cnt > 1) { pt = *pp++; bb.LL.x += pt.x; bb.LL.y += pt.y; bb.UR.x += pt.x; bb.UR.y += pt.y; while ((cg = *cp++)) { BF2B(GD_bb(cg), b); pt = *pp++; b.LL.x += pt.x; b.LL.y += pt.y; b.UR.x += pt.x; b.UR.y += pt.y; bb.LL.x = MIN(bb.LL.x, b.LL.x); bb.LL.y = MIN(bb.LL.y, b.LL.y); bb.UR.x = MAX(bb.UR.x, b.UR.x); bb.UR.y = MAX(bb.UR.y, b.UR.y); } } } else { /* empty graph */ bb.LL.x = 0; bb.LL.y = 0; bb.UR.x = late_int(rg, G_width, POINTS(DEFAULT_NODEWIDTH), 3); bb.UR.y = late_int(rg, G_height, POINTS(DEFAULT_NODEHEIGHT), 3); isEmpty = 1; } if (GD_label(rg)) { point p; int d; isEmpty = 0; PF2P(GD_label(rg)->dimen, p); d = p.x - (bb.UR.x - bb.LL.x); if (d > 0) { /* height of label added below */ d /= 2; bb.LL.x -= d; bb.UR.x += d; } } if (isRoot || isEmpty) margin = 0; else margin = late_int (g, G_margin, CL_OFFSET, 0); pt.x = -bb.LL.x + margin; pt.y = -bb.LL.y + margin + GD_border(rg)[BOTTOM_IX].y; bb.LL.x = 0; bb.LL.y = 0; bb.UR.x += pt.x + margin; bb.UR.y += pt.y + margin + GD_border(rg)[TOP_IX].y; /* translate nodes */ if (c_cnt) { cp = cc; pp = pts; while ((cg = *cp++)) { point p; node_t *n; pointf del; if (pp) { p = *pp++; p.x += pt.x; p.y += pt.y; } else { p = pt; } del.x = PS2INCH(p.x); del.y = PS2INCH(p.y); for (n = agfstnode(cg); n; n = agnxtnode(cg, n)) { ND_pos(n)[0] += del.x; ND_pos(n)[1] += del.y; } } } bbf.LL.x = PS2INCH(bb.LL.x); bbf.LL.y = PS2INCH(bb.LL.y); bbf.UR.x = PS2INCH(bb.UR.x); bbf.UR.y = PS2INCH(bb.UR.y); BB(g) = bbf; }