int agraphseqcmpf(Dict_t * d, void *arg0, void *arg1, Dtdisc_t * disc) { long v; Agraph_t *sg0, *sg1; sg0 = (Agraph_t *) arg0; sg1 = (Agraph_t *) arg1; v = (AGSEQ(sg0) - AGSEQ(sg1)); return ((v==0)?0:(v<0?-1:1)); }
/* emitEdge: */ static void emitEdge (Agraph_t* G, Agedge_t* e, FILE* outFile) { fprintf (outFile, " edge [\n id %lu\n", (unsigned long)AGSEQ(e)); fprintf (outFile, " source %lu\n", ID(agtail(e))); fprintf (outFile, " target %lu\n", ID(aghead(e))); emitEdgeAttrs (G, e, outFile, 2); fprintf (outFile, " ]\n"); }
static void tkgen_print_tags(GVJ_t *job) { char *ObjType; unsigned int ObjId; obj_state_t *obj = job->obj; int ObjFlag; switch (obj->emit_state) { case EMIT_NDRAW: ObjType = "node"; ObjFlag = 1; ObjId = AGSEQ(obj->u.n); break; case EMIT_NLABEL: ObjType = "node"; ObjFlag = 0; ObjId = AGSEQ(obj->u.n); break; case EMIT_EDRAW: case EMIT_TDRAW: case EMIT_HDRAW: ObjType = "edge"; ObjFlag = 1; ObjId = AGSEQ(obj->u.e); break; case EMIT_ELABEL: case EMIT_TLABEL: case EMIT_HLABEL: ObjType = "edge"; ObjFlag = 0; ObjId = AGSEQ(obj->u.e); break; case EMIT_GDRAW: ObjType = "graph"; ObjFlag = 1; ObjId = -1; /* hack! */ break; case EMIT_GLABEL: ObjFlag = 0; ObjType = "graph label"; ObjId = -1; /* hack! */ break; case EMIT_CDRAW: ObjType = "graph"; ObjFlag = 1; ObjId = AGSEQ(obj->u.sg); break; case EMIT_CLABEL: ObjType = "graph"; ObjFlag = 0; ObjId = AGSEQ(obj->u.sg); break; default: assert (0); break; } gvprintf(job, " -tags {%d%s%d}", ObjFlag, ObjType, ObjId); }
static char *nodefilename(const char *filename, node_t * n, char *buf) { static char *dir; static char disposable[1024]; if (dir == 0) { if (filename) dir = gdirname(strcpy(disposable, filename)); else dir = "."; } sprintf(buf, "%s/node%d.png", dir, AGSEQ(n)); return buf; }
static void vrml_bezier(GVJ_t *job, pointf * A, int n, int arrow_at_start, int arrow_at_end, int filled) { obj_state_t *obj = job->obj; edge_t *e = obj->u.e; double fstz, sndz; pointf p1, V[4]; int i, j, step; assert(e); fstz = Fstz = obj->tail_z; sndz = Sndz = obj->head_z; if (straight(A,n)) { doSegment (job, A, gvrender_ptf(job, ND_coord(agtail(e))),Fstz,gvrender_ptf(job, ND_coord(aghead(e))),Sndz); return; } gvputs(job, "Shape { geometry Extrusion {\n"); gvputs(job, " spine ["); V[3] = A[0]; for (i = 0; i + 3 < n; i += 3) { V[0] = V[3]; for (j = 1; j <= 3; j++) V[j] = A[i + j]; for (step = 0; step <= BEZIERSUBDIVISION; step++) { p1 = Bezier(V, 3, (double) step / BEZIERSUBDIVISION, NULL, NULL); gvprintf(job, " %.3f %.3f %.3f", p1.x, p1.y, interpolate_zcoord(job, p1, A[0], fstz, A[n - 1], sndz)); } } gvputs(job, " ]\n"); gvprintf(job, " crossSection [ %.3f %.3f, %.3f %.3f, %.3f %.3f, %.3f %.3f ]\n", (obj->penwidth), (obj->penwidth), -(obj->penwidth), (obj->penwidth), -(obj->penwidth), -(obj->penwidth), (obj->penwidth), -(obj->penwidth)); gvputs(job, "}\n"); gvprintf(job, " appearance DEF E%ld Appearance {\n", AGSEQ(e)); gvputs(job, " material Material {\n"); gvputs(job, " ambientIntensity 0.33\n"); gvprintf(job, " diffuseColor %.3f %.3f %.3f\n", obj->pencolor.u.rgba[0] / 255., obj->pencolor.u.rgba[1] / 255., obj->pencolor.u.rgba[2] / 255.); gvputs(job, " }\n"); gvputs(job, " }\n"); gvputs(job, "}\n"); }
/* * initialize dictionaries, set seq, invoke init method of new graph */ Agraph_t *agopen1(Agraph_t * g) { Agraph_t *par; g->n_seq = agdtopen(g, &Ag_obj_seq_disc, Dttree); g->n_id = agdtopen(g, &Ag_obj_id_disc, Dttree); g->e_seq = agdtopen(g, &Ag_obj_seq_disc, Dttree); g->e_id = agdtopen(g, &Ag_edge_disc, Dttree); g->g_dict = agdtopen(g, &Ag_obj_id_disc, Dttree); par = agparent(g); if (par) { AGSEQ(g) = agnextseq(par, AGRAPH); dtinsert(par->g_dict, g); } /* else AGSEQ=0 */ if (g->desc.has_attrs) agraphattr_init(g, FALSE); agmethod_init(g, g); return g; }
/* * initialize dictionaries, set seq, invoke init method of new graph */ Agraph_t *agopen1(Agraph_t * g) { Agraph_t *par; g->n_seq = agdtopen(g, &Ag_subnode_seq_disc, Dttree); g->n_id = agdtopen(g, &Ag_subnode_id_disc, Dttree); g->e_seq = agdtopen(g, g == agroot(g)? &Ag_mainedge_seq_disc : &Ag_subedge_seq_disc, Dttree); g->e_id = agdtopen(g, g == agroot(g)? &Ag_mainedge_id_disc : &Ag_subedge_id_disc, Dttree); g->g_dict = agdtopen(g, &Ag_subgraph_id_disc, Dttree); par = agparent(g); if (par) { AGSEQ(g) = agnextseq(par, AGRAPH); dtinsert(par->g_dict, g); } /* else AGSEQ=0 */ if (!par || par->desc.has_attrs) agraphattr_init(g); agmethod_init(g, g); return g; }
xbt_dynar_t SD_dotload_generic(const char* filename, bool sequential, bool schedule) { xbt_assert(filename, "Unable to use a null file descriptor\n"); FILE *in_file = fopen(filename, "r"); xbt_assert(in_file != nullptr, "Failed to open file: %s", filename); SD_task_t root; SD_task_t end; SD_task_t task; std::vector<SD_task_t>* computer; std::unordered_map<std::string, std::vector<SD_task_t>*> computers; bool schedule_success = true; std::unordered_map<std::string, SD_task_t> jobs; xbt_dynar_t result = xbt_dynar_new(sizeof(SD_task_t), dot_task_p_free); Agraph_t * dag_dot = agread(in_file, NIL(Agdisc_t *)); /* Create all the nodes */ Agnode_t *node = nullptr; for (node = agfstnode(dag_dot); node; node = agnxtnode(dag_dot, node)) { char *name = agnameof(node); double amount = atof(agget(node, (char*)"size")); if (jobs.find(name) == jobs.end()) { if (sequential) { XBT_DEBUG("See <job id=%s amount =%.0f>", name, amount); task = SD_task_create_comp_seq(name, nullptr , amount); } else { double alpha = atof(agget(node, (char *) "alpha")); XBT_DEBUG("See <job id=%s amount =%.0f alpha = %.3f>", name, amount, alpha); task = SD_task_create_comp_par_amdahl(name, nullptr , amount, alpha); } jobs.insert({std::string(name), task}); if (strcmp(name,"root") && strcmp(name,"end")) xbt_dynar_push(result, &task); if ((sequential) && ((schedule && schedule_success) || XBT_LOG_ISENABLED(sd_dotparse, xbt_log_priority_verbose))) { /* try to take the information to schedule the task only if all is right*/ char *char_performer = agget(node, (char *) "performer"); char *char_order = agget(node, (char *) "order"); /* Tasks will execute on in a given "order" on a given set of "performer" hosts */ int performer = ((not char_performer || not strcmp(char_performer, "")) ? -1 : atoi(char_performer)); int order = ((not char_order || not strcmp(char_order, "")) ? -1 : atoi(char_order)); if ((performer != -1 && order != -1) && performer < static_cast<int>(sg_host_count())) { /* required parameters are given and less performers than hosts are required */ XBT_DEBUG ("Task '%s' is scheduled on workstation '%d' in position '%d'", task->name, performer, order); auto comp = computers.find(char_performer); if (comp != computers.end()) { computer = comp->second; } else { computer = new std::vector<SD_task_t>; computers.insert({char_performer, computer}); } if (static_cast<unsigned int>(order) < computer->size()) { SD_task_t task_test = computer->at(order); if (task_test && task_test != task) { /* the user gave the same order to several tasks */ schedule_success = false; XBT_VERB("Task '%s' wants to start on performer '%s' at the same position '%s' as task '%s'", task_test->name, char_performer, char_order, task->name); continue; } } else computer->resize(order); computer->insert(computer->begin() + order, task); } else { /* one of required parameters is not given */ schedule_success = false; XBT_VERB("The schedule is ignored, task '%s' can not be scheduled on %d hosts", task->name, performer); } } } else { XBT_WARN("Task '%s' is defined more than once", name); } } /*Check if 'root' and 'end' nodes have been explicitly declared. If not, create them. */ if (jobs.find("root") == jobs.end()) root = (sequential ? SD_task_create_comp_seq("root", nullptr, 0) : SD_task_create_comp_par_amdahl("root", nullptr, 0, 0)); else root = jobs.at("root"); SD_task_set_state(root, SD_SCHEDULABLE); /* by design the root task is always SCHEDULABLE */ xbt_dynar_insert_at(result, 0, &root); /* Put it at the beginning of the dynar */ if (jobs.find("end") == jobs.end()) end = (sequential ? SD_task_create_comp_seq("end", nullptr, 0) : SD_task_create_comp_par_amdahl("end", nullptr, 0, 0)); else end = jobs.at("end"); /* Create edges */ std::vector<Agedge_t*> edges; for (node = agfstnode(dag_dot); node; node = agnxtnode(dag_dot, node)) { edges.clear(); for (Agedge_t* edge = agfstout(dag_dot, node); edge; edge = agnxtout(dag_dot, edge)) edges.push_back(edge); /* Be sure edges are sorted */ std::sort(edges.begin(), edges.end(), [](const Agedge_t* a, const Agedge_t* b) { return AGSEQ(a) < AGSEQ(b); }); for (Agedge_t* edge : edges) { char *src_name=agnameof(agtail(edge)); char *dst_name=agnameof(aghead(edge)); double size = atof(agget(edge, (char *) "size")); SD_task_t src = jobs.at(src_name); SD_task_t dst = jobs.at(dst_name); if (size > 0) { std::string name = std::string(src_name) + "->" + dst_name; XBT_DEBUG("See <transfer id=%s amount = %.0f>", name.c_str(), size); if (jobs.find(name) == jobs.end()) { if (sequential) task = SD_task_create_comm_e2e(name.c_str(), nullptr, size); else task = SD_task_create_comm_par_mxn_1d_block(name.c_str(), nullptr, size); SD_task_dependency_add(src, task); SD_task_dependency_add(task, dst); jobs.insert({name, task}); xbt_dynar_push(result, &task); } else { XBT_WARN("Task '%s' is defined more than once", name.c_str()); } } else { SD_task_dependency_add(src, dst); } } } XBT_DEBUG("All tasks have been created, put %s at the end of the dynar", end->name); xbt_dynar_push(result, &end); /* Connect entry tasks to 'root', and exit tasks to 'end'*/ unsigned i; xbt_dynar_foreach (result, i, task){ if (task->predecessors->empty() && task->inputs->empty() && task != root) { XBT_DEBUG("Task '%s' has no source. Add dependency from 'root'", task->name); SD_task_dependency_add(root, task); } if (task->successors->empty() && task->outputs->empty() && task != end) { XBT_DEBUG("Task '%s' has no destination. Add dependency to 'end'", task->name); SD_task_dependency_add(task, end); } } agclose(dag_dot); fclose(in_file); if(schedule){ if (schedule_success) { std::vector<simgrid::s4u::Host*> hosts = simgrid::s4u::Engine::get_instance()->get_all_hosts(); for (auto const& elm : computers) { SD_task_t previous_task = nullptr; for (auto const& cur_task : *elm.second) { /* add dependency between the previous and the task to avoid parallel execution */ if (cur_task) { if (previous_task && not SD_task_dependency_exists(previous_task, cur_task)) SD_task_dependency_add(previous_task, cur_task); SD_task_schedulel(cur_task, 1, hosts[std::stod(elm.first)]); previous_task = cur_task; } } delete elm.second; } } else { XBT_WARN("The scheduling is ignored"); for (auto const& elm : computers) delete elm.second; xbt_dynar_free(&result); result = nullptr; } } if (result && not acyclic_graph_detail(result)) { std::string base = simgrid::xbt::Path(filename).get_base_name(); XBT_ERROR("The DOT described in %s is not a DAG. It contains a cycle.", base.c_str()); xbt_dynar_free(&result); result = nullptr; } return result; }
/*-------------------------------------------------------------------------*\ * Write info about a node to stdout. * Example: * n:info() \*-------------------------------------------------------------------------*/ static int gr_info(lua_State *L) { Agraph_t *g; gr_node_t *ud = tonode(L, 1, STRICT); Agedge_t *se; Agsym_t *sym; g = agraphof(ud->n); printf("INFO NODE '%s' '%s' id=%lu seq=%d\n", agnameof(ud->n), ud->name, (unsigned long) AGID(ud->n), AGSEQ(ud->n)); printf(" ptr: %p\n", ud->n); printf(" Symbols:\n"); se = agfstout(g, ud->n); sym=0; while ((sym = agnxtattr(g,AGNODE,sym))!=NULL) printf(" %s = '%s'\n",sym->name,sym->defval); #if 0 printf(" Out edges: d-out=%d u-out=%d\n", agdegree(g, ud->n, 0, 1), agcountuniqedges(g, ud->n, 0, 1)); #endif while (se) { printf(" name: '%s', head: '%s', tail: '%s' id=%lud, seq=%d %p\n", agnameof(se), agnameof(aghead(se)), agnameof(agtail(se)), (unsigned long) AGID(se), AGSEQ(se), (void*)se); se = agnxtout(g, se); } #if 0 printf(" In edges: d-in=%d u-in=%d\n", agdegree(g, ud->n, 1, 0), agcountuniqedges(g, ud->n, 1, 0)); #endif se = agfstin(g, ud->n); while (se) { printf(" name: '%s', head: '%s', tail: '%s' îd=%lu seq=%d %p\n", agnameof(se), agnameof(aghead(se)), agnameof(agtail(se)), (unsigned long) AGID(se), AGSEQ(se), (void*)se); se = agnxtin(g, se); } #if 0 printf(" Edges: d-io=%d u-io=%d\n", agdegree(g, ud->n, 1, 1), agcountuniqedges(g, ud->n, 1, 1)); #endif se = agfstedge(g, ud->n); while (se) { printf(" name: '%s', head: '%s', tail: '%s' id=%lud seq=%d %p\n", agnameof(se), agnameof(aghead(se)), agnameof(agtail(se)), (unsigned long) AGID(se), AGSEQ(se), (void*)se); se = agnxtedge(g, se, ud->n); } return 0; }
/* orthoEdges: * For edges without position information, construct an orthogonal routing. * If doLbls is true, use edge label info when available to guide routing, * and set label pos for those edges for which this info is not available. */ void orthoEdges (Agraph_t* g, int doLbls) { sgraph* sg; maze* mp; int n_edges; route* route_list; int i, gstart; Agnode_t* n; Agedge_t* e; snode* sn; snode* dn; epair_t* es = N_GNEW(agnedges(g), epair_t); cell* start; cell* dest; PointSet* ps; textlabel_t* lbl; if (Concentrate) ps = newPS(); #ifdef DEBUG { char* s = agget(g, "odb"); char c; odb_flags = 0; if (s && (*s != '\0')) { while ((c = *s++)) { switch (c) { case 'c' : odb_flags |= ODB_CHANG; // emit channel graph break; case 'i' : odb_flags |= (ODB_SGRAPH|ODB_IGRAPH); // emit search graphs break; case 'm' : odb_flags |= ODB_MAZE; // emit maze break; case 'r' : odb_flags |= ODB_ROUTE; // emit routes in maze break; case 's' : odb_flags |= ODB_SGRAPH; // emit search graph break; } } } } #endif if (doLbls) { agerr(AGWARN, "Orthogonal edges do not currently handle edge labels. Try using xlabels.\n"); doLbls = 0; } mp = mkMaze (g, doLbls); sg = mp->sg; #ifdef DEBUG if (odb_flags & ODB_SGRAPH) emitSearchGraph (stderr, sg); #endif /* store edges to be routed in es, along with their lengths */ n_edges = 0; for (n = agfstnode (g); n; n = agnxtnode(g, n)) { for (e = agfstout(g, n); e; e = agnxtout(g,e)) { if ((Nop == 2) && ED_spl(e)) continue; if (Concentrate) { int ti = AGSEQ(agtail(e)); int hi = AGSEQ(aghead(e)); if (ti <= hi) { if (isInPS (ps,ti,hi)) continue; else addPS (ps,ti,hi); } else { if (isInPS (ps,hi,ti)) continue; else addPS (ps,hi,ti); } } es[n_edges].e = e; es[n_edges].d = edgeLen (e); n_edges++; } } route_list = N_NEW (n_edges, route); qsort((char *)es, n_edges, sizeof(epair_t), (qsort_cmpf) edgecmp); gstart = sg->nnodes; PQgen (sg->nnodes+2); sn = &sg->nodes[gstart]; dn = &sg->nodes[gstart+1]; for (i = 0; i < n_edges; i++) { #ifdef DEBUG if ((i > 0) && (odb_flags & ODB_IGRAPH)) emitSearchGraph (stderr, sg); #endif e = es[i].e; start = CELL(agtail(e)); dest = CELL(aghead(e)); if (doLbls && (lbl = ED_label(e)) && lbl->set) { } else { if (start == dest) addLoop (sg, start, dn, sn); else { addNodeEdges (sg, dest, dn); addNodeEdges (sg, start, sn); } if (shortPath (sg, dn, sn)) goto orthofinish; } route_list[i] = convertSPtoRoute(sg, sn, dn); reset (sg); } PQfree (); mp->hchans = extractHChans (mp); mp->vchans = extractVChans (mp); assignSegs (n_edges, route_list, mp); if (setjmp(jbuf)) goto orthofinish; assignTracks (n_edges, route_list, mp); #ifdef DEBUG if (odb_flags & ODB_ROUTE) emitGraph (stderr, mp, n_edges, route_list, es); #endif attachOrthoEdges (g, mp, n_edges, route_list, &sinfo, es, doLbls); orthofinish: if (Concentrate) freePS (ps); for (i=0; i < n_edges; i++) free (route_list[i].segs); free (route_list); freeMaze (mp); }
static void vrml_ellipse(GVJ_t * job, pointf * A, int filled) { 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); gvputs(job, "Transform {\n"); gvprintf(job, " translation %.3f %.3f %.3f\n", A[0].x, A[0].y, z); gvprintf(job, " scale %.3f %.3f 1\n", rx, ry); gvputs(job, " children [\n"); gvputs(job, " Transform {\n"); gvputs(job, " rotation 1 0 0 1.57\n"); gvputs(job, " children [\n"); gvputs(job, " Shape {\n"); gvputs(job, " geometry Cylinder { side FALSE }\n"); gvputs(job, " appearance Appearance {\n"); gvputs(job, " material Material {\n"); gvputs(job, " ambientIntensity 0.33\n"); gvputs(job, " diffuseColor 1 1 1\n"); gvputs(job, " }\n"); gvprintf(job, " texture ImageTexture { url \"node%ld.png\" }\n", AGSEQ(n)); gvputs(job, " }\n"); gvputs(job, " }\n"); gvputs(job, " ]\n"); gvputs(job, " }\n"); gvputs(job, " ]\n"); gvputs(job, "}\n"); break; case EDGE_OBJTYPE: e = obj->u.e; z = GETZ(job,obj,A[0],e); gvputs(job, "Transform {\n"); gvprintf(job, " translation %.3f %.3f %.3f\n", A[0].x, A[0].y, z); gvputs(job, " children [\n"); gvputs(job, " Shape {\n"); gvprintf(job, " geometry Sphere {radius %.3f }\n", (double) rx); gvprintf(job, " appearance USE E%d\n", AGSEQ(e)); gvputs(job, " }\n"); gvputs(job, " ]\n"); gvputs(job, "}\n"); } }
static void vrml_polygon(GVJ_t *job, pointf * A, int np, int filled) { obj_state_t *obj = job->obj; node_t *n; edge_t *e; double z = obj->z; pointf p, mp; gdPoint *points; int i, pen; gdImagePtr brush = NULL; double theta; switch (obj->type) { case ROOTGRAPH_OBJTYPE: gvprintf(job, " Background { skyColor %.3f %.3f %.3f }\n", obj->fillcolor.u.rgba[0] / 255., obj->fillcolor.u.rgba[1] / 255., obj->fillcolor.u.rgba[2] / 255.); Saw_skycolor = TRUE; break; case CLUSTER_OBJTYPE: break; case NODE_OBJTYPE: n = obj->u.n; pen = set_penstyle(job, im, brush); points = N_GGNEW(np, gdPoint); for (i = 0; i < np; i++) { mp = vrml_node_point(job, n, A[i]); points[i].x = ROUND(mp.x); points[i].y = ROUND(mp.y); } if (filled) gdImageFilledPolygon(im, points, np, color_index(im, obj->fillcolor)); gdImagePolygon(im, points, np, pen); free(points); if (brush) gdImageDestroy(brush); gvputs(job, "Shape {\n"); gvputs(job, " appearance Appearance {\n"); gvputs(job, " material Material {\n"); gvputs(job, " ambientIntensity 0.33\n"); gvputs(job, " diffuseColor 1 1 1\n"); gvputs(job, " }\n"); gvprintf(job, " texture ImageTexture { url \"node%ld.png\" }\n", AGSEQ(n)); gvputs(job, " }\n"); gvputs(job, " geometry Extrusion {\n"); gvputs(job, " crossSection ["); for (i = 0; i < np; i++) { p.x = A[i].x - ND_coord(n).x; p.y = A[i].y - ND_coord(n).y; gvprintf(job, " %.3f %.3f,", p.x, p.y); } p.x = A[0].x - ND_coord(n).x; p.y = A[0].y - ND_coord(n).y; gvprintf(job, " %.3f %.3f ]\n", p.x, p.y); gvprintf(job, " spine [ %.5g %.5g %.5g, %.5g %.5g %.5g ]\n", ND_coord(n).x, ND_coord(n).y, z - .01, ND_coord(n).x, ND_coord(n).y, z + .01); gvputs(job, " }\n"); gvputs(job, "}\n"); break; case EDGE_OBJTYPE: e = obj->u.e; if (np != 3) { static int flag; if (!flag) { flag++; agerr(AGWARN, "vrml_polygon: non-triangle arrowheads not supported - ignoring\n"); } } if (IsSegment) { doArrowhead (job, A); return; } p.x = p.y = 0.0; for (i = 0; i < np; i++) { p.x += A[i].x; p.y += A[i].y; } p.x = p.x / np; p.y = p.y / np; /* it is bad to know that A[1] is the aiming point, but we do */ theta = atan2((A[0].y + A[2].y) / 2.0 - A[1].y, (A[0].x + A[2].x) / 2.0 - A[1].x) + M_PI / 2.0; z = GETZ(job,obj,p,e); /* FIXME: arrow vector ought to follow z coord of bezier */ gvputs(job, "Transform {\n"); gvprintf(job, " translation %.3f %.3f %.3f\n", p.x, p.y, z); gvputs(job, " children [\n"); gvputs(job, " Transform {\n"); gvprintf(job, " rotation 0 0 1 %.3f\n", theta); gvputs(job, " children [\n"); gvputs(job, " Shape {\n"); gvprintf(job, " geometry Cone {bottomRadius %.3f height %.3f }\n", obj->penwidth * 2.5, obj->penwidth * 10.0); gvprintf(job, " appearance USE E%ld\n", AGSEQ(e)); gvputs(job, " }\n"); gvputs(job, " ]\n"); gvputs(job, " }\n"); gvputs(job, " ]\n"); gvputs(job, "}\n"); break; } }
static int edge_compare(const void *a, const void *b) { unsigned va = AGSEQ(*(Agedge_t **)a); unsigned vb = AGSEQ(*(Agedge_t **)b); return va == vb ? 0 : (va < vb ? -1 : 1); }