ink.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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. #include <algorithm>
  11. #include <cmath>
  12. #include <cstdlib>
  13. #include <common/types.h>
  14. #include <common/globals.h>
  15. #include <sparse/general.h>
  16. #include <mingle/ink.h>
  17. #include <vector>
  18. double ink_count;
  19. static point_t addPoint (point_t a, point_t b)
  20. {
  21. a.x += b.x;
  22. a.y += b.y;
  23. return a;
  24. }
  25. static point_t subPoint (point_t a, point_t b)
  26. {
  27. a.x -= b.x;
  28. a.y -= b.y;
  29. return a;
  30. }
  31. static point_t scalePoint (point_t a, double d)
  32. {
  33. a.x *= d;
  34. a.y *= d;
  35. return a;
  36. }
  37. static double dotPoint(point_t a, point_t b){
  38. return a.x*b.x + a.y*b.y;
  39. }
  40. static const point_t Origin = {0, 0};
  41. /* sumLengths:
  42. */
  43. static double sumLengths_avoid_bad_angle(const std::vector<point_t> &points,
  44. point_t end, point_t meeting,
  45. double angle_param) {
  46. /* avoid sharp turns, we want cos_theta to be as close to -1 as possible */
  47. double len0, len, sum = 0;
  48. double diff_x, diff_y, diff_x0, diff_y0;
  49. double cos_theta, cos_max = -10;
  50. diff_x0 = end.x-meeting.x;
  51. diff_y0 = end.y-meeting.y;
  52. len0 = sum = hypot(diff_x0, diff_y0);
  53. // distance form each of 'points' till 'meeting'
  54. for (const point_t &p : points) {
  55. diff_x = p.x - meeting.x;
  56. diff_y = p.y - meeting.y;
  57. len = hypot(diff_x, diff_y);
  58. sum += len;
  59. cos_theta = (diff_x0 * diff_x + diff_y0 * diff_y)
  60. / std::max(len * len0, 0.00001);
  61. cos_max = std::max(cos_max, cos_theta);
  62. }
  63. // distance of single line from 'meeting' to 'end'
  64. return sum*(cos_max + angle_param);/* straight line gives angle_param - 1, turning angle of 180 degree gives angle_param + 1 */
  65. }
  66. static double sumLengths(const std::vector<point_t> &points, point_t end,
  67. point_t meeting) {
  68. double sum = 0;
  69. double diff_x, diff_y;
  70. // distance form each of 'points' till 'meeting'
  71. for (const point_t &p : points) {
  72. diff_x = p.x - meeting.x;
  73. diff_y = p.y - meeting.y;
  74. sum += hypot(diff_x, diff_y);
  75. }
  76. // distance of single line from 'meeting' to 'end'
  77. diff_x = end.x-meeting.x;
  78. diff_y = end.y-meeting.y;
  79. sum += hypot(diff_x, diff_y);
  80. return sum;
  81. }
  82. /* bestInk:
  83. */
  84. static double bestInk(const std::vector<point_t> &points, point_t begin,
  85. point_t end, double prec, point_t *meet,
  86. double angle_param) {
  87. point_t first, second, third, fourth, diff, meeting;
  88. double value1, value2, value3, value4;
  89. first = begin;
  90. fourth = end;
  91. do {
  92. diff = subPoint(fourth,first);
  93. second = addPoint(first,scalePoint(diff,1.0/3.0));
  94. third = addPoint(first,scalePoint(diff,2.0/3.0));
  95. if (angle_param < 1){
  96. value1 = sumLengths(points, end, first);
  97. value2 = sumLengths(points, end, second);
  98. value3 = sumLengths(points, end, third);
  99. value4 = sumLengths(points, end, fourth);
  100. } else {
  101. value1 = sumLengths_avoid_bad_angle(points, end, first, angle_param);
  102. value2 = sumLengths_avoid_bad_angle(points, end, second, angle_param);
  103. value3 = sumLengths_avoid_bad_angle(points, end, third, angle_param);
  104. value4 = sumLengths_avoid_bad_angle(points, end, fourth, angle_param);
  105. }
  106. if (value1<value2) {
  107. if (value1<value3) {
  108. if (value1<value4) {
  109. // first is smallest
  110. fourth = second;
  111. }
  112. else {
  113. // fourth is smallest
  114. first = third;
  115. }
  116. }
  117. else {
  118. if (value3<value4) {
  119. // third is smallest
  120. first = second;
  121. }
  122. else {
  123. // fourth is smallest
  124. first = third;
  125. }
  126. }
  127. }
  128. else {
  129. if (value2<value3) {
  130. if (value2<value4) {
  131. // second is smallest
  132. fourth = third;
  133. }
  134. else {
  135. // fourth is smallest
  136. first = third;
  137. }
  138. }
  139. else {
  140. if (value3<value4) {
  141. // third is smallest
  142. first = second;
  143. }
  144. else {
  145. // fourth is smallest
  146. first = third;
  147. }
  148. }
  149. }
  150. } while (fabs(value1 - value4) / (std::min(value1, value4) + 1e-10) > prec
  151. && dotPoint(diff, diff) > 1.e-20);
  152. meeting = scalePoint(addPoint(first,fourth),0.5);
  153. *meet = meeting;
  154. return sumLengths(points, end, meeting);
  155. }
  156. static double project_to_line(point_t pt, point_t left, point_t right, double angle){
  157. /* pt
  158. ^ ^
  159. . \ \
  160. . \ \
  161. d . a\ \
  162. . \ \
  163. . \ \
  164. . c \ alpha \ b
  165. .<------left:0 ----------------------------> right:1. Find the projection of pt on the left--right line. If the turning angle is small,
  166. | |
  167. |<-------f---------
  168. we should get a negative number. Let a := left->pt, b := left->right, then we are calculating:
  169. c = |a| cos(a,b)/|b| b
  170. d = a - c
  171. f = -ctan(alpha)*|d|/|b| b
  172. and the project of alpha degree on the left->right line is
  173. c-f = |a| cos(a,b)/|b| b - -ctan(alpha)*|d|/|b| b
  174. = (|a| a.b/(|a||b|) + ctan(alpha)|a-c|)/|b| b
  175. = (a.b/|b| + ctan(alpha)|a-c|)/|b| b
  176. the dimentionless projection is:
  177. a.b/|b|^2 + ctan(alpha)|a-c|/|b|
  178. = a.b/|b|^2 + ctan(alpha)|d|/|b|
  179. */
  180. point_t b, a;
  181. double bnorm, dnorm;
  182. double alpha, ccord;
  183. if (angle <=0 || angle >= M_PI) return 2;/* return outside of the interval which should be handled as a sign of infeasible turning angle */
  184. alpha = angle;
  185. assert(alpha > 0 && alpha < M_PI);
  186. b = subPoint(right, left);
  187. a = subPoint(pt, left);
  188. bnorm = std::max(1.e-10, dotPoint(b, b));
  189. ccord = dotPoint(b, a)/bnorm;
  190. dnorm = dotPoint(a,a)/bnorm - ccord*ccord;
  191. if (alpha == M_PI/2){
  192. return ccord;
  193. }
  194. return ccord + sqrt(std::max(0.0, dnorm)) / tan(alpha);
  195. }
  196. /* ink:
  197. * Compute minimal ink used the input edges are bundled.
  198. * Assumes tails all occur on one side and heads on the other.
  199. */
  200. double ink(const std::vector<pedge> &edges, int numEdges, int *pick,
  201. double *ink0, point_t *meet1, point_t *meet2, double angle_param,
  202. double angle) {
  203. int i;
  204. point_t begin, end, mid, diff;
  205. double inkUsed;
  206. double eps = 1.0e-2;
  207. double cend = 0, cbegin = 0;
  208. double wgt = 0;
  209. ink_count += numEdges;
  210. *ink0 = 0;
  211. /* canonicalize so that edges 1,2,3 and 3,2,1 gives the same optimal ink */
  212. if (pick) vector_sort_int(numEdges, pick);
  213. begin = end = Origin;
  214. for (i = 0; i < numEdges; i++) {
  215. const pedge &e = pick ? edges[pick[i]] : edges[i];
  216. const std::vector<double> &x = e.x;
  217. point_t source = {x[0], x[1]};
  218. point_t target = {x[e.dim * e.npoints - e.dim],
  219. x[e.dim * e.npoints - e.dim + 1]};
  220. (*ink0) += hypot(source.x - target.x, source.y - target.y);
  221. begin = addPoint(begin, scalePoint(source, e.wgt));
  222. end = addPoint(end, scalePoint(target, e.wgt));
  223. wgt += e.wgt;
  224. }
  225. begin = scalePoint (begin, 1.0/wgt);
  226. end = scalePoint (end, 1.0/wgt);
  227. if (numEdges == 1){
  228. *meet1 = begin;
  229. *meet2 = end;
  230. return *ink0;
  231. }
  232. /* shift the begin and end point to avoid sharp turns */
  233. std::vector<point_t> sources;
  234. std::vector<point_t> targets;
  235. for (i = 0; i < numEdges; i++) {
  236. const pedge &e = pick ? edges[pick[i]] : edges[i];
  237. const std::vector<double> &x = e.x;
  238. sources.push_back(point_t{x[0], x[1]});
  239. targets.push_back(point_t{x[e.dim * e.npoints - e.dim],
  240. x[e.dim * e.npoints - e.dim + 1]});
  241. /* begin(1) ----------- mid(0) */
  242. if (i == 0){
  243. cbegin = project_to_line(sources[i], begin, end, angle);
  244. cend = project_to_line(targets[i], end, begin, angle);
  245. } else {
  246. cbegin = std::max(cbegin, project_to_line(sources[i], begin, end, angle));
  247. cend = std::max(cend, project_to_line(targets[i], end, begin, angle));
  248. }
  249. }
  250. if (angle > 0 && angle < M_PI){
  251. if (cbegin + cend > 1 || cbegin > 1 || cend > 1){
  252. /* no point can be found that satisfies the angular constraints, so we give up and set ink to a large value */
  253. inkUsed = 1000*(*ink0);
  254. return inkUsed;
  255. }
  256. /* make sure the turning angle is no more than alpha degree */
  257. cbegin = std::max(0.0, cbegin);/* make sure the new adjusted point is with in [begin,end] internal */
  258. diff = subPoint(end, begin);
  259. begin = addPoint(begin, scalePoint(diff, cbegin));
  260. cend = std::max(0.0, cend);/* make sure the new adjusted point is with in [end,begin] internal */
  261. end = subPoint(end, scalePoint(diff, cend));
  262. }
  263. mid = scalePoint (addPoint(begin,end),0.5);
  264. inkUsed = bestInk(sources, begin, mid, eps, meet1, angle_param)
  265. + bestInk(targets, end, mid, eps, meet2, angle_param);
  266. return inkUsed;
  267. }
  268. double ink1(const pedge &e) {
  269. double ink0 = 0;
  270. const std::vector<double> &x = e.x;
  271. const double xx = x[0] - x[e.dim * e.npoints - e.dim];
  272. const double yy = x[1] - x[e.dim * e.npoints - e.dim + 1];
  273. ink0 += hypot(xx, yy);
  274. return ink0;
  275. }