Exemple #1
0
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));
}
Exemple #2
0
/* 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");
}
Exemple #6
0
/* 
 * 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;
}
Exemple #7
0
/* 
 * 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;
}
Exemple #8
0
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;
}
Exemple #9
0
/*-------------------------------------------------------------------------*\
 * 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;
}
Exemple #10
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);
}