adjust.c 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at https://graphviz.org
  9. *************************************************************************/
  10. /* adjust.c
  11. * Routines for repositioning nodes after initial layout in
  12. * order to reduce/remove node overlaps.
  13. */
  14. #include <assert.h>
  15. #include <cgraph/gv_ctype.h>
  16. #include <neatogen/neato.h>
  17. #include <common/utils.h>
  18. #include <float.h>
  19. #include <math.h>
  20. #include <neatogen/voronoi.h>
  21. #include <neatogen/info.h>
  22. #include <neatogen/edges.h>
  23. #include <neatogen/site.h>
  24. #include <neatogen/hedges.h>
  25. #include <neatogen/digcola.h>
  26. #if ((defined(HAVE_GTS) || defined(HAVE_TRIANGLE)) && defined(SFDP))
  27. #include <neatogen/overlap.h>
  28. #endif
  29. #include <stdbool.h>
  30. #ifdef IPSEPCOLA
  31. #include <vpsc/csolve_VPSC.h>
  32. #include <neatogen/quad_prog_vpsc.h>
  33. #endif
  34. #include <stddef.h>
  35. #include <util/agxbuf.h>
  36. #include <util/alloc.h>
  37. #include <util/startswith.h>
  38. #include <util/strcasecmp.h>
  39. #define SEPFACT 0.8 // default esep/sep
  40. static const double incr = 0.05; /* Increase bounding box by adding
  41. * incr * dimension around box.
  42. */
  43. typedef struct {
  44. Site **sites; ///< array of pointers to sites; used in qsort
  45. Site **endSite; ///< sentinel on sites array
  46. Point nw, ne, sw, se; ///< corners of clipping window
  47. Site **nextSite;
  48. } state_t;
  49. static void setBoundBox(state_t *st, Point *ll, Point *ur) {
  50. pxmin = ll->x;
  51. pxmax = ur->x;
  52. pymin = ll->y;
  53. pymax = ur->y;
  54. st->nw.x = st->sw.x = pxmin;
  55. st->ne.x = st->se.x = pxmax;
  56. st->nw.y = st->ne.y = pymax;
  57. st->sw.y = st->se.y = pymin;
  58. }
  59. /// Free node resources.
  60. static void freeNodes(void)
  61. {
  62. for (size_t i = 0; i < nsites; i++) {
  63. breakPoly(&nodeInfo[i].poly);
  64. }
  65. polyFree();
  66. if (nodeInfo != NULL) {
  67. free(nodeInfo->verts); // Free vertices
  68. }
  69. free(nodeInfo);
  70. }
  71. /* Compute extremes of graph, then set up bounding box.
  72. * If user supplied a bounding box, use that;
  73. * else if "window" is a graph attribute, use that;
  74. * otherwise, define bounding box as a percentage expansion of
  75. * graph extremes.
  76. * In the first two cases, check that graph fits in bounding box.
  77. */
  78. static void chkBoundBox(state_t *st, Agraph_t *graph) {
  79. Point ll, ur;
  80. double x_min = DBL_MAX;
  81. double y_min = DBL_MAX;
  82. double x_max = -DBL_MAX;
  83. double y_max = -DBL_MAX;
  84. assert(nsites > 0);
  85. for (size_t i = 0; i < nsites; ++i) {
  86. Info_t *ip = &nodeInfo[i];
  87. Poly *pp = &ip->poly;
  88. double x = ip->site.coord.x;
  89. double y = ip->site.coord.y;
  90. x_min = fmin(x_min, pp->origin.x + x);
  91. y_min = fmin(y_min, pp->origin.y + y);
  92. x_max = fmax(x_max, pp->corner.x + x);
  93. y_max = fmax(y_max, pp->corner.y + y);
  94. }
  95. // create initial bounding box by adding margin × dimension around box
  96. // enclosing nodes
  97. char *marg = agget(graph, "voro_margin");
  98. const double margin = (marg && *marg != '\0') ? atof(marg) : 0.05;
  99. double ydelta = margin * (y_max - y_min);
  100. double xdelta = margin * (x_max - x_min);
  101. ll.x = x_min - xdelta;
  102. ll.y = y_min - ydelta;
  103. ur.x = x_max + xdelta;
  104. ur.y = y_max + ydelta;
  105. setBoundBox(st, &ll, &ur);
  106. }
  107. /// For each node in the graph, create a Info data structure
  108. static int makeInfo(Agraph_t * graph)
  109. {
  110. int (*polyf)(Poly *, Agnode_t *, double, double);
  111. assert(agnnodes(graph) >= 0);
  112. nsites = (size_t)agnnodes(graph);
  113. geominit();
  114. nodeInfo = gv_calloc(nsites, sizeof(Info_t));
  115. Agnode_t *node = agfstnode(graph);
  116. expand_t pmargin = sepFactor (graph);
  117. if (pmargin.doAdd) {
  118. polyf = makeAddPoly;
  119. /* we need inches for makeAddPoly */
  120. pmargin.x = PS2INCH(pmargin.x);
  121. pmargin.y = PS2INCH(pmargin.y);
  122. }
  123. else polyf = makePoly;
  124. for (size_t i = 0; i < nsites; i++) {
  125. Info_t *ip = &nodeInfo[i];
  126. ip->site.coord.x = ND_pos(node)[0];
  127. ip->site.coord.y = ND_pos(node)[1];
  128. if (polyf(&ip->poly, node, pmargin.x, pmargin.y)) {
  129. free (nodeInfo);
  130. nodeInfo = NULL;
  131. return 1;
  132. }
  133. ip->site.sitenbr = i;
  134. ip->site.refcnt = 1;
  135. ip->node = node;
  136. ip->verts = NULL;
  137. ip->n_verts = 0;
  138. node = agnxtnode(graph, node);
  139. }
  140. return 0;
  141. }
  142. /* sort sites on y, then x, coord */
  143. static int scomp(const void *S1, const void *S2)
  144. {
  145. const Site *s1 = *(Site *const *)S1;
  146. const Site *s2 = *(Site *const *)S2;
  147. if (s1->coord.y < s2->coord.y)
  148. return -1;
  149. if (s1->coord.y > s2->coord.y)
  150. return 1;
  151. if (s1->coord.x < s2->coord.x)
  152. return -1;
  153. if (s1->coord.x > s2->coord.x)
  154. return 1;
  155. return 0;
  156. }
  157. /// Fill array of pointer to sites and sort the sites using scomp
  158. static void sortSites(state_t *st) {
  159. if (st->sites == NULL) {
  160. st->sites = gv_calloc(nsites, sizeof(Site*));
  161. st->endSite = st->sites + nsites;
  162. }
  163. for (size_t i = 0; i < nsites; i++) {
  164. Info_t *ip = &nodeInfo[i];
  165. st->sites[i] = &ip->site;
  166. ip->verts = NULL;
  167. ip->n_verts = 0;
  168. ip->site.refcnt = 1;
  169. }
  170. qsort(st->sites, nsites, sizeof(Site *), scomp);
  171. /* Reset site index for nextOne */
  172. st->nextSite = st->sites;
  173. }
  174. static void geomUpdate(state_t *st, int doSort) {
  175. if (doSort)
  176. sortSites(st);
  177. /* compute ranges */
  178. xmin = DBL_MAX;
  179. xmax = -DBL_MAX;
  180. assert(nsites > 0);
  181. for (size_t i = 0; i < nsites; ++i) {
  182. xmin = fmin(xmin, st->sites[i]->coord.x);
  183. xmax = fmax(xmax, st->sites[i]->coord.x);
  184. }
  185. ymin = st->sites[0]->coord.y;
  186. ymax = st->sites[nsites - 1]->coord.y;
  187. deltax = xmax - xmin;
  188. }
  189. static Site *nextOne(void *state) {
  190. state_t *st = state;
  191. if (st->nextSite < st->endSite) {
  192. return *st->nextSite++;
  193. } else
  194. return NULL;
  195. }
  196. /// Check for nodes with identical positions and tweak the positions.
  197. static void rmEquality(state_t *st) {
  198. sortSites(st);
  199. for (Site **ip = st->sites; ip < st->endSite; ) {
  200. Site **jp = ip + 1;
  201. if (jp >= st->endSite ||
  202. (*jp)->coord.x != (*ip)->coord.x ||
  203. (*jp)->coord.y != (*ip)->coord.y) {
  204. ip = jp;
  205. continue;
  206. }
  207. /* Find first node kp with position different from ip */
  208. int cnt = 2;
  209. Site **kp = jp + 1;
  210. while (kp < st->endSite &&
  211. (*kp)->coord.x == (*ip)->coord.x &&
  212. (*kp)->coord.y == (*ip)->coord.y) {
  213. cnt++;
  214. jp = kp;
  215. kp = jp + 1;
  216. }
  217. /* If next node exists and is on the same line */
  218. if (kp < st->endSite && (*kp)->coord.y == (*ip)->coord.y) {
  219. const double xdel = ((*kp)->coord.x - (*ip)->coord.x) / cnt;
  220. int i = 1;
  221. for (jp = ip + 1; jp < kp; jp++) {
  222. (*jp)->coord.x += i * xdel;
  223. i++;
  224. }
  225. } else { /* nothing is to the right */
  226. Info_t *info;
  227. for (jp = ip + 1; jp < kp; ip++, jp++) {
  228. info = nodeInfo + (*ip)->sitenbr;
  229. double xdel = info->poly.corner.x - info->poly.origin.x;
  230. info = nodeInfo + (*jp)->sitenbr;
  231. xdel += info->poly.corner.x - info->poly.origin.x;
  232. (*jp)->coord.x = (*ip)->coord.x + xdel / 2;
  233. }
  234. }
  235. ip = kp;
  236. }
  237. }
  238. /// Count number of node-node overlaps at iteration iter.
  239. static unsigned countOverlap(unsigned iter) {
  240. unsigned count = 0;
  241. for (size_t i = 0; i < nsites; i++)
  242. nodeInfo[i].overlaps = false;
  243. for (size_t i = 0; i < nsites - 1; i++) {
  244. Info_t *ip = &nodeInfo[i];
  245. for (size_t j = i + 1; j < nsites; j++) {
  246. Info_t *jp = &nodeInfo[j];
  247. if (polyOverlap(ip->site.coord, &ip->poly, jp->site.coord, &jp->poly)) {
  248. count++;
  249. ip->overlaps = true;
  250. jp->overlaps = true;
  251. }
  252. }
  253. }
  254. if (Verbose > 1)
  255. fprintf(stderr, "overlap [%u] : %u\n", iter, count);
  256. return count;
  257. }
  258. static void increaseBoundBox(state_t *st) {
  259. Point ur = {.x = pxmax, .y = pymax};
  260. Point ll = {.x = pxmin, .y = pymin};
  261. const double ydelta = incr * (ur.y - ll.y);
  262. const double xdelta = incr * (ur.x - ll.x);
  263. ur.x += xdelta;
  264. ur.y += ydelta;
  265. ll.x -= xdelta;
  266. ll.y -= ydelta;
  267. setBoundBox(st, &ll, &ur);
  268. }
  269. /// Area of triangle whose vertices are a,b,c
  270. static double areaOf(Point a, Point b, Point c)
  271. {
  272. return fabs(a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2;
  273. }
  274. /* Compute centroid of triangle with vertices a, b, c.
  275. * Return coordinates in x and y.
  276. */
  277. static void centroidOf(Point a, Point b, Point c, double *x, double *y)
  278. {
  279. *x = (a.x + b.x + c.x) / 3;
  280. *y = (a.y + b.y + c.y) / 3;
  281. }
  282. /* The new position is the centroid of the voronoi polygon. This is the weighted
  283. * sum of the centroids of a triangulation, normalized to the total area.
  284. */
  285. static void newpos(Info_t * ip)
  286. {
  287. const Point anchor = ip->verts[0];
  288. double totalArea = 0.0;
  289. double cx = 0.0;
  290. double cy = 0.0;
  291. double x;
  292. double y;
  293. for (size_t i = 1; i + 1 < ip->n_verts; ++i) {
  294. const Point p = ip->verts[i];
  295. const Point q = ip->verts[i + 1];
  296. const double area = areaOf(anchor, p, q);
  297. centroidOf(anchor, p, q, &x, &y);
  298. cx += area * x;
  299. cy += area * y;
  300. totalArea += area;
  301. }
  302. ip->site.coord.x = cx / totalArea;
  303. ip->site.coord.y = cy / totalArea;
  304. }
  305. /* Add corners of clipping window to appropriate sites.
  306. * A site gets a corner if it is the closest site to that corner.
  307. */
  308. static void addCorners(const state_t *st) {
  309. Info_t *ip = nodeInfo;
  310. Info_t *sws = ip;
  311. Info_t *nws = ip;
  312. Info_t *ses = ip;
  313. Info_t *nes = ip;
  314. double swd = dist_2(ip->site.coord, st->sw);
  315. double nwd = dist_2(ip->site.coord, st->nw);
  316. double sed = dist_2(ip->site.coord, st->se);
  317. double ned = dist_2(ip->site.coord, st->ne);
  318. for (size_t i = 1; i < nsites; i++) {
  319. ip = &nodeInfo[i];
  320. double d = dist_2(ip->site.coord, st->sw);
  321. if (d < swd) {
  322. swd = d;
  323. sws = ip;
  324. }
  325. d = dist_2(ip->site.coord, st->se);
  326. if (d < sed) {
  327. sed = d;
  328. ses = ip;
  329. }
  330. d = dist_2(ip->site.coord, st->nw);
  331. if (d < nwd) {
  332. nwd = d;
  333. nws = ip;
  334. }
  335. d = dist_2(ip->site.coord, st->ne);
  336. if (d < ned) {
  337. ned = d;
  338. nes = ip;
  339. }
  340. }
  341. addVertex(&sws->site, st->sw.x, st->sw.y);
  342. addVertex(&ses->site, st->se.x, st->se.y);
  343. addVertex(&nws->site, st->nw.x, st->nw.y);
  344. addVertex(&nes->site, st->ne.x, st->ne.y);
  345. }
  346. /* Calculate the new position of a site as the centroid
  347. * of its voronoi polygon, if it overlaps other nodes.
  348. * The polygons are finite by being clipped to the clipping
  349. * window.
  350. * We first add the corner of the clipping windows to the
  351. * vertex lists of the appropriate sites.
  352. *
  353. * @param st Algorithm state
  354. * @param doAll Move all nodes, regardless of overlap
  355. */
  356. static void newPos(const state_t *st, bool doAll) {
  357. addCorners(st);
  358. for (size_t i = 0; i < nsites; i++) {
  359. Info_t *ip = &nodeInfo[i];
  360. if (doAll || ip->overlaps)
  361. newpos(ip);
  362. }
  363. }
  364. /* Cleanup voronoi memory.
  365. * Note that ELcleanup relies on the number
  366. * of sites, so should at least be reset every time we use vAdjust.
  367. * This could be optimized, over multiple components or
  368. * even multiple graphs, but probably not worth it.
  369. */
  370. static void cleanup(void)
  371. {
  372. ELcleanup();
  373. siteinit(); /* free memory */
  374. edgeinit(); /* free memory */
  375. }
  376. static int vAdjust(state_t *st) {
  377. unsigned iterCnt = 0;
  378. unsigned badLevel = 0;
  379. unsigned increaseCnt = 0;
  380. unsigned overlapCnt = countOverlap(iterCnt);
  381. if (overlapCnt == 0)
  382. return 0;
  383. rmEquality(st);
  384. geomUpdate(st, 0);
  385. voronoi(nextOne, st);
  386. for (bool doAll = false;;) {
  387. newPos(st, doAll);
  388. iterCnt++;
  389. const unsigned cnt = countOverlap(iterCnt);
  390. if (cnt == 0)
  391. break;
  392. if (cnt >= overlapCnt)
  393. badLevel++;
  394. else
  395. badLevel = 0;
  396. overlapCnt = cnt;
  397. switch (badLevel) {
  398. case 0:
  399. doAll = true;
  400. break;
  401. default:
  402. doAll = true;
  403. increaseCnt++;
  404. increaseBoundBox(st);
  405. break;
  406. }
  407. geomUpdate(st, 1);
  408. voronoi(nextOne, st);
  409. }
  410. if (Verbose) {
  411. fprintf(stderr, "Number of iterations = %u\n", iterCnt);
  412. fprintf(stderr, "Number of increases = %u\n", increaseCnt);
  413. }
  414. cleanup();
  415. return 1;
  416. }
  417. static void rePos(void) {
  418. double f = 1.0 + incr;
  419. for (size_t i = 0; i < nsites; i++) {
  420. Info_t *ip = &nodeInfo[i];
  421. ip->site.coord.x *= f;
  422. ip->site.coord.y *= f;
  423. }
  424. }
  425. static int sAdjust(state_t *st) {
  426. unsigned iterCnt = 0;
  427. const unsigned overlapCnt = countOverlap(iterCnt);
  428. if (overlapCnt == 0)
  429. return 0;
  430. rmEquality(st);
  431. while (1) {
  432. rePos();
  433. iterCnt++;
  434. const unsigned cnt = countOverlap(iterCnt);
  435. if (cnt == 0)
  436. break;
  437. }
  438. if (Verbose) {
  439. fprintf(stderr, "Number of iterations = %u\n", iterCnt);
  440. }
  441. return 1;
  442. }
  443. /// Enter new node positions into the graph
  444. static void updateGraph(void)
  445. {
  446. for (size_t i = 0; i < nsites; i++) {
  447. Info_t *ip = &nodeInfo[i];
  448. ND_pos(ip->node)[0] = ip->site.coord.x;
  449. ND_pos(ip->node)[1] = ip->site.coord.y;
  450. }
  451. }
  452. #define ELS "|edgelabel|"
  453. /* Return true if node name starts with ELS */
  454. #define IS_LNODE(n) startswith(agnameof(n), ELS)
  455. /// Set up array of half sizes in inches.
  456. double *getSizes(Agraph_t * g, pointf pad, int* n_elabels, int** elabels)
  457. {
  458. double *sizes = gv_calloc(Ndim * agnnodes(g), sizeof(double));
  459. int nedge_nodes = 0;
  460. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  461. if (elabels && IS_LNODE(n)) nedge_nodes++;
  462. const int i = ND_id(n);
  463. sizes[i * Ndim] = ND_width(n) * .5 + pad.x;
  464. sizes[i * Ndim + 1] = ND_height(n) * .5 + pad.y;
  465. }
  466. if (elabels && nedge_nodes) {
  467. int* elabs = gv_calloc(nedge_nodes, sizeof(int));
  468. nedge_nodes = 0;
  469. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  470. if (IS_LNODE(n))
  471. elabs[nedge_nodes++] = ND_id(n);
  472. }
  473. *elabels = elabs;
  474. *n_elabels = nedge_nodes;
  475. }
  476. return sizes;
  477. }
  478. /* Assumes g is connected and simple, i.e., we can have a->b and b->a
  479. * but not a->b and a->b
  480. */
  481. SparseMatrix makeMatrix(Agraph_t *g) {
  482. if (!g)
  483. return NULL;
  484. const int nnodes = agnnodes(g);
  485. const int nedges = agnedges(g);
  486. /* Assign node ids */
  487. int i = 0;
  488. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n))
  489. ND_id(n) = i++;
  490. int *I = gv_calloc(nedges, sizeof(int));
  491. int *J = gv_calloc(nedges, sizeof(int));
  492. double *val = gv_calloc(nedges, sizeof(double));
  493. Agsym_t *sym = agfindedgeattr(g, "weight");
  494. i = 0;
  495. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  496. const int row = ND_id(n);
  497. for (Agedge_t *e = agfstout(g, n); e; e = agnxtout(g, e)) {
  498. I[i] = row;
  499. J[i] = ND_id(aghead(e));
  500. double v;
  501. if (!sym || sscanf(agxget(e, sym), "%lf", &v) != 1)
  502. v = 1;
  503. val[i] = v;
  504. /* edge length */
  505. i++;
  506. }
  507. }
  508. SparseMatrix A = SparseMatrix_from_coordinate_arrays(nedges, nnodes, nnodes,
  509. I, J, val,
  510. MATRIX_TYPE_REAL,
  511. sizeof(double));
  512. free(I);
  513. free(J);
  514. free(val);
  515. return A;
  516. }
  517. #if ((defined(HAVE_GTS) || defined(HAVE_TRIANGLE)) && defined(SFDP))
  518. static void fdpAdjust(graph_t *g, adjust_data *am) {
  519. SparseMatrix A0 = makeMatrix(g);
  520. SparseMatrix A = A0;
  521. double *pos = gv_calloc(Ndim * agnnodes(g), sizeof(double));
  522. expand_t sep = sepFactor(g);
  523. pointf pad;
  524. if (sep.doAdd) {
  525. pad.x = PS2INCH(sep.x);
  526. pad.y = PS2INCH(sep.y);
  527. } else {
  528. pad.x = PS2INCH(DFLT_MARGIN);
  529. pad.y = PS2INCH(DFLT_MARGIN);
  530. }
  531. double *sizes = getSizes(g, pad, NULL, NULL);
  532. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  533. double* npos = pos + Ndim * ND_id(n);
  534. for (int i = 0; i < Ndim; i++) {
  535. npos[i] = ND_pos(n)[i];
  536. }
  537. }
  538. if (!SparseMatrix_is_symmetric(A, false) || A->type != MATRIX_TYPE_REAL) {
  539. A = SparseMatrix_get_real_adjacency_matrix_symmetrized(A);
  540. } else {
  541. A = SparseMatrix_remove_diagonal(A);
  542. }
  543. remove_overlap(Ndim, A, pos, sizes, am->value, am->scaling,
  544. ELSCHEME_NONE, 0, NULL, NULL,
  545. mapBool(agget(g, "overlap_shrink"), true));
  546. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  547. double *npos = pos + Ndim * ND_id(n);
  548. for (int i = 0; i < Ndim; i++) {
  549. ND_pos(n)[i] = npos[i];
  550. }
  551. }
  552. free(sizes);
  553. free(pos);
  554. if (A != A0)
  555. SparseMatrix_delete(A);
  556. SparseMatrix_delete (A0);
  557. }
  558. #endif
  559. #ifdef IPSEPCOLA
  560. static int
  561. vpscAdjust(graph_t* G)
  562. {
  563. enum { dim = 2 };
  564. int nnodes = agnnodes(G);
  565. ipsep_options opt;
  566. pointf *nsize = gv_calloc(nnodes, sizeof(pointf));
  567. float* coords[dim];
  568. float *f_storage = gv_calloc(dim * nnodes, sizeof(float));
  569. for (size_t i = 0; i < dim; i++) {
  570. coords[i] = f_storage + i * nnodes;
  571. }
  572. size_t j = 0;
  573. for (Agnode_t *v = agfstnode(G); v; v = agnxtnode(G, v)) {
  574. for (size_t i = 0; i < dim; i++) {
  575. coords[i][j] = (float)ND_pos(v)[i];
  576. }
  577. nsize[j].x = ND_width(v);
  578. nsize[j].y = ND_height(v);
  579. j++;
  580. }
  581. opt.diredges = 0;
  582. opt.edge_gap = 0;
  583. opt.noverlap = 2;
  584. opt.clusters = (cluster_data){0};
  585. expand_t exp_margin = sepFactor (G);
  586. /* Multiply by 2 since opt.gap is the gap size, not the margin */
  587. if (exp_margin.doAdd) {
  588. opt.gap.x = 2.0*PS2INCH(exp_margin.x);
  589. opt.gap.y = 2.0*PS2INCH(exp_margin.y);
  590. }
  591. else {
  592. opt.gap.x = opt.gap.y = 2.0*PS2INCH(DFLT_MARGIN);
  593. }
  594. opt.nsize = nsize;
  595. removeoverlaps(nnodes, coords, &opt);
  596. j = 0;
  597. for (Agnode_t *v = agfstnode(G); v; v = agnxtnode(G, v)) {
  598. for (size_t i = 0; i < dim; i++) {
  599. ND_pos(v)[i] = coords[i][j];
  600. }
  601. j++;
  602. }
  603. free (f_storage);
  604. free (nsize);
  605. return 0;
  606. }
  607. #endif
  608. /* Return true if "normalize" is defined and valid; return angle in phi.
  609. * Read angle as degrees, convert to radians.
  610. * Guarantee -PI < phi <= PI.
  611. */
  612. static int
  613. angleSet (graph_t* g, double* phi)
  614. {
  615. char* p;
  616. char* a = agget(g, "normalize");
  617. if (!a || *a == '\0')
  618. return 0;
  619. double ang = strtod (a, &p);
  620. if (p == a) { /* no number */
  621. if (mapbool(a))
  622. ang = 0.0;
  623. else
  624. return 0;
  625. }
  626. while (ang > 180) ang -= 360;
  627. while (ang <= -180) ang += 360;
  628. *phi = RADIANS(ang);
  629. return 1;
  630. }
  631. /* If normalize is set, move first node to origin, then
  632. * rotate graph so that the angle of the first edge is given
  633. * by the degrees from normalize.
  634. * FIX: Generalize to allow rotation determined by graph shape.
  635. */
  636. int normalize(graph_t * g)
  637. {
  638. double phi;
  639. int ret;
  640. if (!angleSet(g, &phi))
  641. return 0;
  642. node_t *v = agfstnode(g);
  643. pointf p = {.x = ND_pos(v)[0], .y = ND_pos(v)[1]};
  644. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  645. ND_pos(v)[0] -= p.x;
  646. ND_pos(v)[1] -= p.y;
  647. }
  648. if (p.x || p.y) ret = 1;
  649. else ret = 0;
  650. edge_t *e = NULL;
  651. for (v = agfstnode(g); v; v = agnxtnode(g, v))
  652. if ((e = agfstout(g, v)))
  653. break;
  654. if (e == NULL)
  655. return ret;
  656. /* rotation necessary; pos => ccw */
  657. phi -= atan2(ND_pos(aghead(e))[1] - ND_pos(agtail(e))[1],
  658. ND_pos(aghead(e))[0] - ND_pos(agtail(e))[0]);
  659. if (phi) {
  660. const pointf orig = {.x = ND_pos(agtail(e))[0],
  661. .y = ND_pos(agtail(e))[1]};
  662. const double cosv = cos(phi);
  663. const double sinv = sin(phi);
  664. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  665. p.x = ND_pos(v)[0] - orig.x;
  666. p.y = ND_pos(v)[1] - orig.y;
  667. ND_pos(v)[0] = p.x * cosv - p.y * sinv + orig.x;
  668. ND_pos(v)[1] = p.x * sinv + p.y * cosv + orig.y;
  669. }
  670. return 1;
  671. }
  672. else return ret;
  673. }
  674. typedef struct {
  675. adjust_mode mode;
  676. char *attrib;
  677. char *print;
  678. } lookup_t;
  679. /* Translation table from overlap values to algorithms.
  680. * adjustMode[0] corresponds to overlap=true
  681. * adjustMode[1] corresponds to overlap=false
  682. */
  683. static const lookup_t adjustMode[] = {
  684. {AM_NONE, "", "none"},
  685. #if ((defined(HAVE_GTS) || defined(HAVE_TRIANGLE)) && defined(SFDP))
  686. {AM_PRISM, "prism", "prism"},
  687. #endif
  688. {AM_VOR, "voronoi", "Voronoi"},
  689. {AM_NSCALE, "scale", "scaling"},
  690. {AM_COMPRESS, "compress", "compress"},
  691. {AM_VPSC, "vpsc", "vpsc"},
  692. {AM_IPSEP, "ipsep", "ipsep"},
  693. {AM_SCALE, "oscale", "old scaling"},
  694. {AM_SCALEXY, "scalexy", "x and y scaling"},
  695. {AM_ORTHO, "ortho", "orthogonal constraints"},
  696. {AM_ORTHO_YX, "ortho_yx", "orthogonal constraints"},
  697. {AM_ORTHOXY, "orthoxy", "xy orthogonal constraints"},
  698. {AM_ORTHOYX, "orthoyx", "yx orthogonal constraints"},
  699. {AM_PORTHO, "portho", "pseudo-orthogonal constraints"},
  700. {AM_PORTHO_YX, "portho_yx", "pseudo-orthogonal constraints"},
  701. {AM_PORTHOXY, "porthoxy", "xy pseudo-orthogonal constraints"},
  702. {AM_PORTHOYX, "porthoyx", "yx pseudo-orthogonal constraints"},
  703. #if !((defined(HAVE_GTS) || defined(HAVE_TRIANGLE)) && defined(SFDP))
  704. {AM_PRISM, "prism", 0},
  705. #endif
  706. {0}
  707. };
  708. /// Initialize and set prism values
  709. static void setPrismValues(Agraph_t *g, const char *s, adjust_data *dp) {
  710. int v;
  711. if (sscanf (s, "%d", &v) > 0 && v >= 0)
  712. dp->value = v;
  713. else
  714. dp->value = 1000;
  715. dp->scaling = late_double(g, agfindgraphattr(g, "overlap_scaling"), -4.0, -1.e10);
  716. }
  717. /// Convert string value to internal value of adjustment mode.
  718. static void getAdjustMode(Agraph_t *g, const char *s, adjust_data *dp) {
  719. const lookup_t *ap = adjustMode + 1;
  720. if (s == NULL || *s == '\0') {
  721. dp->mode = adjustMode[0].mode;
  722. dp->print = adjustMode[0].print;
  723. }
  724. else {
  725. while (ap->attrib) {
  726. bool matches = strcasecmp(s, ap->attrib) == 0;
  727. // "prism" takes parameters, so needs to match "prism.*"
  728. matches |= ap->mode == AM_PRISM
  729. && strncasecmp(s, ap->attrib, strlen(ap->attrib)) == 0;
  730. if (matches) {
  731. if (ap->print == NULL) {
  732. agwarningf("Overlap value \"%s\" unsupported - ignored\n", ap->attrib);
  733. ap = &adjustMode[1];
  734. }
  735. dp->mode = ap->mode;
  736. dp->print = ap->print;
  737. if (ap->mode == AM_PRISM)
  738. setPrismValues(g, s + strlen(ap->attrib), dp);
  739. break;
  740. }
  741. ap++;
  742. }
  743. if (ap->attrib == NULL ) {
  744. bool v = mapbool(s);
  745. bool unmappable = v != mapBool(s, true);
  746. if (unmappable) {
  747. agwarningf("Unrecognized overlap value \"%s\" - using false\n", s);
  748. v = false;
  749. }
  750. if (v) {
  751. dp->mode = adjustMode[0].mode;
  752. dp->print = adjustMode[0].print;
  753. }
  754. else {
  755. dp->mode = adjustMode[1].mode;
  756. dp->print = adjustMode[1].print;
  757. }
  758. if (dp->mode == AM_PRISM)
  759. setPrismValues (g, "", dp);
  760. }
  761. }
  762. if (Verbose) {
  763. fprintf(stderr, "overlap: %s value %d scaling %.04f\n", dp->print, dp->value, dp->scaling);
  764. }
  765. }
  766. void graphAdjustMode(graph_t *G, adjust_data *dp, char *dflt) {
  767. char* am = agget(G, "overlap");
  768. getAdjustMode (G, am ? am : (dflt ? dflt : ""), dp);
  769. }
  770. #define ISZERO(d) (fabs(d) < 0.000000001)
  771. static int simpleScale (graph_t* g)
  772. {
  773. pointf sc;
  774. int i;
  775. char* p;
  776. if ((p = agget(g, "scale"))) {
  777. if ((i = sscanf(p, "%lf,%lf", &sc.x, &sc.y))) {
  778. if (ISZERO(sc.x)) return 0;
  779. if (i == 1) sc.y = sc.x;
  780. else if (ISZERO(sc.y)) return 0;
  781. if (sc.y == 1 && sc.x == 1) return 0;
  782. if (Verbose)
  783. fprintf (stderr, "scale = (%.03f,%.03f)\n", sc.x, sc.y);
  784. for (node_t *n = agfstnode(g); n; n = agnxtnode(g,n)) {
  785. ND_pos(n)[0] *= sc.x;
  786. ND_pos(n)[1] *= sc.y;
  787. }
  788. return 1;
  789. }
  790. }
  791. return 0;
  792. }
  793. /* Use adjust_data to determine if and how to remove
  794. * node overlaps.
  795. * Return non-zero if nodes are moved.
  796. */
  797. int
  798. removeOverlapWith (graph_t * G, adjust_data* am)
  799. {
  800. int ret;
  801. if (agnnodes(G) < 2)
  802. return 0;
  803. int nret = normalize (G);
  804. nret += simpleScale (G);
  805. if (am->mode == AM_NONE)
  806. return nret;
  807. if (Verbose)
  808. fprintf(stderr, "Adjusting %s using %s\n", agnameof(G), am->print);
  809. if (am->mode > AM_SCALE) {
  810. switch (am->mode) {
  811. case AM_NSCALE:
  812. ret = scAdjust(G, 1);
  813. break;
  814. case AM_SCALEXY:
  815. ret = scAdjust(G, 0);
  816. break;
  817. case AM_PUSH:
  818. ret = 0;
  819. break;
  820. case AM_PUSHPULL:
  821. ret = 0;
  822. break;
  823. case AM_PORTHO_YX:
  824. case AM_PORTHO:
  825. case AM_PORTHOXY:
  826. case AM_PORTHOYX:
  827. case AM_ORTHO_YX:
  828. case AM_ORTHO:
  829. case AM_ORTHOXY:
  830. case AM_ORTHOYX:
  831. cAdjust(G, am->mode);
  832. ret = 0;
  833. break;
  834. case AM_COMPRESS:
  835. ret = scAdjust(G, -1);
  836. break;
  837. #if ((defined(HAVE_GTS) || defined(HAVE_TRIANGLE)) && defined(SFDP))
  838. case AM_PRISM:
  839. fdpAdjust(G, am);
  840. ret = 0;
  841. break;
  842. #endif
  843. #ifdef IPSEPCOLA
  844. case AM_IPSEP:
  845. return nret; /* handled during layout */
  846. break;
  847. case AM_VPSC:
  848. ret = vpscAdjust(G);
  849. break;
  850. #endif
  851. default: /* to silence warnings */
  852. if (am->mode != AM_VOR && am->mode != AM_SCALE)
  853. agwarningf("Unhandled adjust option %s\n", am->print);
  854. ret = 0;
  855. break;
  856. }
  857. return nret+ret;
  858. }
  859. /* create main array */
  860. if (makeInfo(G)) {
  861. freeNodes();
  862. return nret;
  863. }
  864. /* establish and verify bounding box */
  865. state_t st = {0};
  866. chkBoundBox(&st, G);
  867. if (am->mode == AM_SCALE)
  868. ret = sAdjust(&st);
  869. else
  870. ret = vAdjust(&st);
  871. if (ret)
  872. updateGraph();
  873. freeNodes();
  874. free(st.sites);
  875. return ret+nret;
  876. }
  877. /// Use flag value to determine if and how to remove node overlaps.
  878. int
  879. removeOverlapAs(graph_t * G, char* flag)
  880. {
  881. adjust_data am;
  882. if (agnnodes(G) < 2)
  883. return 0;
  884. getAdjustMode(G, flag, &am);
  885. return removeOverlapWith (G, &am);
  886. }
  887. /* Remove node overlap relying on graph's overlap attribute.
  888. * Return non-zero if graph has changed.
  889. */
  890. int adjustNodes(graph_t * G)
  891. {
  892. return removeOverlapAs(G, agget(G, "overlap"));
  893. }
  894. /* Convert "sep" attribute into expand_t.
  895. * Input "+x,y" becomes {x,y,true}
  896. * Input "x,y" becomes {1 + x/sepfact,1 + y/sepfact,false}
  897. * Return 1 on success, 0 on failure
  898. */
  899. static int parseFactor(char *s, expand_t *pp, double sepfact, double dflt) {
  900. int i;
  901. while (gv_isspace(*s)) s++;
  902. if (*s == '+') {
  903. s++;
  904. pp->doAdd = true;
  905. }
  906. else pp->doAdd = false;
  907. double x, y;
  908. if ((i = sscanf(s, "%lf,%lf", &x, &y))) {
  909. if (i == 1) y = x;
  910. if (pp->doAdd) {
  911. if (sepfact > 1) {
  912. pp->x = fmin(dflt, x / sepfact);
  913. pp->y = fmin(dflt, y / sepfact);
  914. }
  915. else if (sepfact < 1) {
  916. pp->x = fmax(dflt, x / sepfact);
  917. pp->y = fmax(dflt, y / sepfact);
  918. }
  919. else {
  920. pp->x = x;
  921. pp->y = y;
  922. }
  923. }
  924. else {
  925. pp->x = 1.0 + x / sepfact;
  926. pp->y = 1.0 + y / sepfact;
  927. }
  928. return 1;
  929. }
  930. else return 0;
  931. }
  932. expand_t
  933. sepFactor(graph_t* g)
  934. {
  935. expand_t pmargin;
  936. char* marg;
  937. if ((marg = agget(g, "sep")) && parseFactor(marg, &pmargin, 1.0, 0)) {
  938. }
  939. else if ((marg = agget(g, "esep")) && parseFactor(marg, &pmargin, SEPFACT, DFLT_MARGIN)) {
  940. }
  941. else { /* default */
  942. pmargin.x = pmargin.y = DFLT_MARGIN;
  943. pmargin.doAdd = true;
  944. }
  945. if (Verbose)
  946. fprintf (stderr, "Node separation: add=%d (%f,%f)\n",
  947. pmargin.doAdd, pmargin.x, pmargin.y);
  948. return pmargin;
  949. }
  950. /* This value should be smaller than the sep value used to expand
  951. * nodes during adjustment. If not, when the adjustment pass produces
  952. * a fairly tight layout, the spline code will find that some nodes
  953. * still overlap.
  954. */
  955. expand_t
  956. esepFactor(graph_t* g)
  957. {
  958. expand_t pmargin;
  959. char* marg;
  960. if ((marg = agget(g, "esep")) && parseFactor(marg, &pmargin, 1.0, 0)) {
  961. }
  962. else if ((marg = agget(g, "sep")) &&
  963. parseFactor(marg, &pmargin, 1.0 / SEPFACT, SEPFACT * DFLT_MARGIN)) {
  964. }
  965. else {
  966. pmargin.x = pmargin.y = SEPFACT*DFLT_MARGIN;
  967. pmargin.doAdd = true;
  968. }
  969. if (Verbose)
  970. fprintf (stderr, "Edge separation: add=%d (%f,%f)\n",
  971. pmargin.doAdd, pmargin.x, pmargin.y);
  972. return pmargin;
  973. }