PageRenderTime 27ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/graphviz-cmake/lib/dotgen/compound.c

https://bitbucket.org/akristmann/custom_build
C | 489 lines | 337 code | 41 blank | 111 comment | 102 complexity | 70ea973eae7185178d5a78a60a5d3dcb MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, EPL-1.0, CPL-1.0, BSD-3-Clause, LGPL-2.1
  1. /* $Id: compound.c,v 1.12 2011/01/25 16:30:48 ellson Exp $ $Revision: 1.12 $ */
  2. /* vim:set shiftwidth=4 ts=8: */
  3. /*************************************************************************
  4. * Copyright (c) 2011 AT&T Intellectual Property
  5. * All rights reserved. This program and the accompanying materials
  6. * are made available under the terms of the Eclipse Public License v1.0
  7. * which accompanies this distribution, and is available at
  8. * http://www.eclipse.org/legal/epl-v10.html
  9. *
  10. * Contributors: See CVS logs. Details at http://www.graphviz.org/
  11. *************************************************************************/
  12. /* Module for clipping splines to cluster boxes.
  13. */
  14. #include "dot.h"
  15. /* pf2s:
  16. * Convert a pointf to its string representation.
  17. */
  18. static char *pf2s(pointf p, char *buf)
  19. {
  20. sprintf(buf, "(%.5g,%.5g)", p.x, p.y);
  21. return buf;
  22. }
  23. /* Return point where line segment [pp,cp] intersects
  24. * the box bp. Assume cp is outside the box, and pp is
  25. * on or in the box.
  26. */
  27. static pointf boxIntersectf(pointf pp, pointf cp, boxf * bp)
  28. {
  29. pointf ipp;
  30. double ppx = pp.x;
  31. double ppy = pp.y;
  32. double cpx = cp.x;
  33. double cpy = cp.y;
  34. pointf ll;
  35. pointf ur;
  36. ll = bp->LL;
  37. ur = bp->UR;
  38. if (cp.x < ll.x) {
  39. ipp.x = ll.x;
  40. ipp.y = pp.y + (int) ((ipp.x - ppx) * (ppy - cpy) / (ppx - cpx));
  41. if (ipp.y >= ll.y && ipp.y <= ur.y)
  42. return ipp;
  43. }
  44. if (cp.x > ur.x) {
  45. ipp.x = ur.x;
  46. ipp.y = pp.y + (int) ((ipp.x - ppx) * (ppy - cpy) / (ppx - cpx));
  47. if (ipp.y >= ll.y && ipp.y <= ur.y)
  48. return ipp;
  49. }
  50. if (cp.y < ll.y) {
  51. ipp.y = ll.y;
  52. ipp.x = pp.x + (int) ((ipp.y - ppy) * (ppx - cpx) / (ppy - cpy));
  53. if (ipp.x >= ll.x && ipp.x <= ur.x)
  54. return ipp;
  55. }
  56. if (cp.y > ur.y) {
  57. ipp.y = ur.y;
  58. ipp.x = pp.x + (int) ((ipp.y - ppy) * (ppx - cpx) / (ppy - cpy));
  59. if (ipp.x >= ll.x && ipp.x <= ur.x)
  60. return ipp;
  61. }
  62. /* failure */
  63. {
  64. char ppbuf[100], cpbuf[100], llbuf[100], urbuf[100];
  65. agerr(AGERR,
  66. "segment [%s,%s] does not intersect box ll=%s,ur=%s\n",
  67. pf2s(pp, ppbuf), pf2s(cp, cpbuf),
  68. pf2s(ll, llbuf), pf2s(ur, urbuf));
  69. assert(0);
  70. }
  71. return ipp;
  72. }
  73. /* inBoxf:
  74. * Returns true if p is on or in box bb
  75. */
  76. static int inBoxf(pointf p, boxf * bb)
  77. {
  78. return INSIDE(p, *bb);
  79. }
  80. /* getCluster:
  81. * Returns subgraph of g with given name.
  82. * Returns NULL if no name is given, or subgraph of
  83. * that name does not exist.
  84. */
  85. static graph_t *getCluster(graph_t * g, char *cluster_name)
  86. {
  87. graph_t *sg;
  88. if (!cluster_name || (*cluster_name == '\0'))
  89. return NULL;
  90. #ifndef WITH_CGRAPH
  91. sg = agfindsubg(g, cluster_name);
  92. #else
  93. sg = agsubg(g, cluster_name, 0);
  94. #endif
  95. if (sg == NULL)
  96. agerr(AGWARN, "cluster named %s not found\n", cluster_name);
  97. return sg;
  98. }
  99. /* The following functions are derived from pp. 411-415 (pp. 791-795)
  100. * of Graphics Gems. In the code there, they use a SGN function to
  101. * count crossings. This doesn't seem to handle certain special cases,
  102. * as when the last point is on the line. It certainly didn't work
  103. * for us when we used int values; see bug 145. We needed to use CMP instead.
  104. *
  105. * Possibly unnecessary with double values, but harmless.
  106. */
  107. /* countVertCross:
  108. * Return the number of times the Bezier control polygon crosses
  109. * the vertical line x = xcoord.
  110. */
  111. static int countVertCross(pointf * pts, double xcoord)
  112. {
  113. int i;
  114. int sign, old_sign;
  115. int num_crossings = 0;
  116. sign = CMP(pts[0].x, xcoord);
  117. if (sign == 0)
  118. num_crossings++;
  119. for (i = 1; i <= 3; i++) {
  120. old_sign = sign;
  121. sign = CMP(pts[i].x, xcoord);
  122. if ((sign != old_sign) && (old_sign != 0))
  123. num_crossings++;
  124. }
  125. return num_crossings;
  126. }
  127. /* countHorzCross:
  128. * Return the number of times the Bezier control polygon crosses
  129. * the horizontal line y = ycoord.
  130. */
  131. static int countHorzCross(pointf * pts, double ycoord)
  132. {
  133. int i;
  134. int sign, old_sign;
  135. int num_crossings = 0;
  136. sign = CMP(pts[0].y, ycoord);
  137. if (sign == 0)
  138. num_crossings++;
  139. for (i = 1; i <= 3; i++) {
  140. old_sign = sign;
  141. sign = CMP(pts[i].y, ycoord);
  142. if ((sign != old_sign) && (old_sign != 0))
  143. num_crossings++;
  144. }
  145. return num_crossings;
  146. }
  147. /* findVertical:
  148. * Given 4 Bezier control points pts, corresponding to the portion
  149. * of an initial spline with path parameter in the range
  150. * 0.0 <= tmin <= t <= tmax <= 1.0, return t where the spline
  151. * first crosses a vertical line segment
  152. * [(xcoord,ymin),(xcoord,ymax)]. Return -1 if not found.
  153. * This is done by binary subdivision.
  154. */
  155. static double
  156. findVertical(pointf * pts, double tmin, double tmax,
  157. double xcoord, double ymin, double ymax)
  158. {
  159. pointf Left[4];
  160. pointf Right[4];
  161. double t;
  162. int no_cross = countVertCross(pts, xcoord);
  163. if (no_cross == 0)
  164. return -1.0;
  165. /* if 1 crossing and on the line x == xcoord (within 1 point) */
  166. if ((no_cross == 1) && (ROUND(pts[3].x) == ROUND(xcoord))) {
  167. if ((ymin <= pts[3].y) && (pts[3].y <= ymax)) {
  168. return tmax;
  169. } else
  170. return -1.0;
  171. }
  172. /* split the Bezier into halves, trying the first half first. */
  173. Bezier(pts, 3, 0.5, Left, Right);
  174. t = findVertical(Left, tmin, (tmin + tmax) / 2.0, xcoord, ymin, ymax);
  175. if (t >= 0.0)
  176. return t;
  177. return findVertical(Right, (tmin + tmax) / 2.0, tmax, xcoord, ymin,
  178. ymax);
  179. }
  180. /* findHorizontal:
  181. * Given 4 Bezier control points pts, corresponding to the portion
  182. * of an initial spline with path parameter in the range
  183. * 0.0 <= tmin <= t <= tmax <= 1.0, return t where the spline
  184. * first crosses a horizontal line segment
  185. * [(xmin,ycoord),(xmax,ycoord)]. Return -1 if not found.
  186. * This is done by binary subdivision.
  187. */
  188. static double
  189. findHorizontal(pointf * pts, double tmin, double tmax,
  190. double ycoord, double xmin, double xmax)
  191. {
  192. pointf Left[4];
  193. pointf Right[4];
  194. double t;
  195. int no_cross = countHorzCross(pts, ycoord);
  196. if (no_cross == 0)
  197. return -1.0;
  198. /* if 1 crossing and on the line y == ycoord (within 1 point) */
  199. if ((no_cross == 1) && (ROUND(pts[3].y) == ROUND(ycoord))) {
  200. if ((xmin <= pts[3].x) && (pts[3].x <= xmax)) {
  201. return tmax;
  202. } else
  203. return -1.0;
  204. }
  205. /* split the Bezier into halves, trying the first half first. */
  206. Bezier(pts, 3, 0.5, Left, Right);
  207. t = findHorizontal(Left, tmin, (tmin + tmax) / 2.0, ycoord, xmin,
  208. xmax);
  209. if (t >= 0.0)
  210. return t;
  211. return findHorizontal(Right, (tmin + tmax) / 2.0, tmax, ycoord, xmin,
  212. xmax);
  213. }
  214. /* splineIntersectf:
  215. * Given four spline control points and a box,
  216. * find the shortest portion of the spline from
  217. * pts[0] to the intersection with the box, if any.
  218. * If an intersection is found, the four points are stored in pts[0..3]
  219. * with pts[3] being on the box, and 1 is returned. Otherwise, pts
  220. * is left unchanged and 0 is returned.
  221. */
  222. static int splineIntersectf(pointf * pts, boxf * bb)
  223. {
  224. double tmin = 2.0;
  225. double t;
  226. pointf origpts[4];
  227. int i;
  228. for (i = 0; i < 4; i++) {
  229. origpts[i] = pts[i];
  230. }
  231. t = findVertical(pts, 0.0, 1.0, bb->LL.x, bb->LL.y, bb->UR.y);
  232. if ((t >= 0) && (t < tmin)) {
  233. Bezier(origpts, 3, t, pts, NULL);
  234. tmin = t;
  235. }
  236. t = findVertical(pts, 0.0, MIN(1.0, tmin), bb->UR.x, bb->LL.y,
  237. bb->UR.y);
  238. if ((t >= 0) && (t < tmin)) {
  239. Bezier(origpts, 3, t, pts, NULL);
  240. tmin = t;
  241. }
  242. t = findHorizontal(pts, 0.0, MIN(1.0, tmin), bb->LL.y, bb->LL.x,
  243. bb->UR.x);
  244. if ((t >= 0) && (t < tmin)) {
  245. Bezier(origpts, 3, t, pts, NULL);
  246. tmin = t;
  247. }
  248. t = findHorizontal(pts, 0.0, MIN(1.0, tmin), bb->UR.y, bb->LL.x,
  249. bb->UR.x);
  250. if ((t >= 0) && (t < tmin)) {
  251. Bezier(origpts, 3, t, pts, NULL);
  252. tmin = t;
  253. }
  254. if (tmin < 2.0) {
  255. return 1;
  256. } else
  257. return 0;
  258. }
  259. /* makeCompoundEdge:
  260. * If edge e has a cluster head and/or cluster tail,
  261. * clip spline to outside of cluster.
  262. * Requirement: spline is composed of only one part,
  263. * with n control points where n >= 4 and n (mod 3) = 1.
  264. * If edge has arrowheads, reposition them.
  265. */
  266. static void makeCompoundEdge(graph_t * g, edge_t * e)
  267. {
  268. graph_t *lh; /* cluster containing head */
  269. graph_t *lt; /* cluster containing tail */
  270. bezier *bez; /* original Bezier for e */
  271. bezier *nbez; /* new Bezier for e */
  272. int starti = 0, endi = 0; /* index of first and last control point */
  273. node_t *head;
  274. node_t *tail;
  275. boxf *bb;
  276. int i, j;
  277. int size;
  278. pointf pts[4];
  279. pointf p;
  280. int fixed;
  281. /* find head and tail target clusters, if defined */
  282. lh = getCluster(g, agget(e, "lhead"));
  283. lt = getCluster(g, agget(e, "ltail"));
  284. if (!lt && !lh)
  285. return;
  286. if (!ED_spl(e)) return;
  287. /* at present, we only handle single spline case */
  288. if (ED_spl(e)->size > 1) {
  289. agerr(AGWARN, "%s -> %s: spline size > 1 not supported\n",
  290. agnameof(agtail(e)), agnameof(aghead(e)));
  291. return;
  292. }
  293. bez = ED_spl(e)->list;
  294. size = bez->size;
  295. head = aghead(e);
  296. tail = agtail(e);
  297. /* allocate new Bezier */
  298. nbez = GNEW(bezier);
  299. nbez->eflag = bez->eflag;
  300. nbez->sflag = bez->sflag;
  301. /* if Bezier has four points, almost collinear,
  302. * make line - unimplemented optimization?
  303. */
  304. /* If head cluster defined, find first Bezier
  305. * crossing head cluster, and truncate spline to
  306. * box edge.
  307. * Otherwise, leave end alone.
  308. */
  309. fixed = 0;
  310. if (lh) {
  311. bb = &(GD_bb(lh));
  312. if (!inBoxf(ND_coord(head), bb)) {
  313. agerr(AGWARN, "%s -> %s: head not inside head cluster %s\n",
  314. agnameof(agtail(e)), agnameof(aghead(e)), agget(e, "lhead"));
  315. } else {
  316. /* If first control point is in bb, degenerate case. Spline
  317. * reduces to four points between the arrow head and the point
  318. * where the segment between the first control point and arrow head
  319. * crosses box.
  320. */
  321. if (inBoxf(bez->list[0], bb)) {
  322. if (inBoxf(ND_coord(tail), bb)) {
  323. agerr(AGWARN,
  324. "%s -> %s: tail is inside head cluster %s\n",
  325. agnameof(agtail(e)), agnameof(aghead(e)), agget(e, "lhead"));
  326. } else {
  327. assert(bez->sflag); /* must be arrowhead on tail */
  328. p = boxIntersectf(bez->list[0], bez->sp, bb);
  329. bez->list[3] = p;
  330. bez->list[1] = mid_pointf(p, bez->sp);
  331. bez->list[0] = mid_pointf(bez->list[1], bez->sp);
  332. bez->list[2] = mid_pointf(bez->list[1], p);
  333. if (bez->eflag)
  334. endi = arrowEndClip(e, bez->list,
  335. starti, 0, nbez, bez->eflag);
  336. endi += 3;
  337. fixed = 1;
  338. }
  339. } else {
  340. for (endi = 0; endi < size - 1; endi += 3) {
  341. if (splineIntersectf(&(bez->list[endi]), bb))
  342. break;
  343. }
  344. if (endi == size - 1) { /* no intersection */
  345. assert(bez->eflag);
  346. nbez->ep = boxIntersectf(bez->ep, bez->list[endi], bb);
  347. } else {
  348. if (bez->eflag)
  349. endi =
  350. arrowEndClip(e, bez->list,
  351. starti, endi, nbez, bez->eflag);
  352. endi += 3;
  353. }
  354. fixed = 1;
  355. }
  356. }
  357. }
  358. if (fixed == 0) { /* if no lh, or something went wrong, use original head */
  359. endi = size - 1;
  360. if (bez->eflag)
  361. nbez->ep = bez->ep;
  362. }
  363. /* If tail cluster defined, find last Bezier
  364. * crossing tail cluster, and truncate spline to
  365. * box edge.
  366. * Otherwise, leave end alone.
  367. */
  368. fixed = 0;
  369. if (lt) {
  370. bb = &(GD_bb(lt));
  371. if (!inBoxf(ND_coord(tail), bb)) {
  372. agerr(AGWARN, "%s -> %s: tail not inside tail cluster %s\n",
  373. agnameof(agtail(e)), agnameof(aghead(e)), agget(e, "ltail"));
  374. } else {
  375. /* If last control point is in bb, degenerate case. Spline
  376. * reduces to four points between arrow head, and the point
  377. * where the segment between the last control point and the
  378. * arrow head crosses box.
  379. */
  380. if (inBoxf(bez->list[endi], bb)) {
  381. if (inBoxf(ND_coord(head), bb)) {
  382. agerr(AGWARN,
  383. "%s -> %s: head is inside tail cluster %s\n",
  384. agnameof(agtail(e)), agnameof(aghead(e)), agget(e, "ltail"));
  385. } else {
  386. assert(bez->eflag); /* must be arrowhead on head */
  387. p = boxIntersectf(bez->list[endi], nbez->ep, bb);
  388. starti = endi - 3;
  389. bez->list[starti] = p;
  390. bez->list[starti + 2] = mid_pointf(p, nbez->ep);
  391. bez->list[starti + 3] = mid_pointf(bez->list[starti + 2], nbez->ep);
  392. bez->list[starti + 1] = mid_pointf(bez->list[starti + 2], p);
  393. if (bez->sflag)
  394. starti = arrowStartClip(e, bez->list, starti,
  395. endi - 3, nbez, bez->sflag);
  396. fixed = 1;
  397. }
  398. } else {
  399. for (starti = endi; starti > 0; starti -= 3) {
  400. for (i = 0; i < 4; i++)
  401. pts[i] = bez->list[starti - i];
  402. if (splineIntersectf(pts, bb)) {
  403. for (i = 0; i < 4; i++)
  404. bez->list[starti - i] = pts[i];
  405. break;
  406. }
  407. }
  408. if (starti == 0) {
  409. assert(bez->sflag);
  410. nbez->sp =
  411. boxIntersectf(bez->sp, bez->list[starti], bb);
  412. } else {
  413. starti -= 3;
  414. if (bez->sflag)
  415. starti = arrowStartClip(e, bez->list, starti,
  416. endi - 3, nbez, bez->sflag);
  417. }
  418. fixed = 1;
  419. }
  420. }
  421. }
  422. if (fixed == 0) { /* if no lt, or something went wrong, use original tail */
  423. /* Note: starti == 0 */
  424. if (bez->sflag)
  425. nbez->sp = bez->sp;
  426. }
  427. /* complete Bezier, free garbage and attach new Bezier to edge
  428. */
  429. nbez->size = endi - starti + 1;
  430. nbez->list = N_GNEW(nbez->size, pointf);
  431. for (i = 0, j = starti; i < nbez->size; i++, j++)
  432. nbez->list[i] = bez->list[j];
  433. free(bez->list);
  434. free(bez);
  435. ED_spl(e)->list = nbez;
  436. }
  437. /* dot_compoundEdges:
  438. */
  439. void dot_compoundEdges(graph_t * g)
  440. {
  441. edge_t *e;
  442. node_t *n;
  443. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  444. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  445. makeCompoundEdge(g, e);
  446. }
  447. }
  448. }