ellipse.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /// @file
  2. /// @ingroup common_render
  3. /*************************************************************************
  4. * Copyright (c) 2012 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. * https://www.eclipse.org/legal/epl-v10.html
  9. *
  10. * Contributors: Details at https://graphviz.org
  11. *************************************************************************/
  12. /* This code is derived from the Java implementation by Luc Maisonobe */
  13. /* Copyright (c) 2003-2004, Luc Maisonobe
  14. * All rights reserved.
  15. *
  16. * Redistribution and use in source and binary forms, with
  17. * or without modification, are permitted provided that
  18. * the following conditions are met:
  19. *
  20. * Redistributions of source code must retain the
  21. * above copyright notice, this list of conditions and
  22. * the following disclaimer.
  23. * Redistributions in binary form must reproduce the
  24. * above copyright notice, this list of conditions and
  25. * the following disclaimer in the documentation
  26. * and/or other materials provided with the
  27. * distribution.
  28. * Neither the names of spaceroots.org, spaceroots.com
  29. * nor the names of their contributors may be used to
  30. * endorse or promote products derived from this
  31. * software without specific prior written permission.
  32. *
  33. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  34. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
  35. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  36. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  37. * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
  38. * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
  39. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  40. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  41. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  44. * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  45. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  46. * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  47. * POSSIBILITY OF SUCH DAMAGE.
  48. */
  49. #include <assert.h>
  50. #include <cgraph/list.h>
  51. #include <limits.h>
  52. #include <math.h>
  53. #include <stdbool.h>
  54. #include <common/render.h>
  55. #include <pathplan/pathplan.h>
  56. #include <util/alloc.h>
  57. #define TWOPI (2*M_PI)
  58. typedef struct {
  59. double cx, cy; /* center */
  60. double a, b; /* semi-major and -minor axes */
  61. /* Start and end angles of the arc. */
  62. double eta1, eta2;
  63. } ellipse_t;
  64. static void initEllipse(ellipse_t * ep, double cx, double cy, double a,
  65. double b, double lambda1, double lambda2) {
  66. ep->cx = cx;
  67. ep->cy = cy;
  68. ep->a = a;
  69. ep->b = b;
  70. ep->eta1 = atan2(sin(lambda1) / b, cos(lambda1) / a);
  71. ep->eta2 = atan2(sin(lambda2) / b, cos(lambda2) / a);
  72. // make sure we have eta1 <= eta2 <= eta1 + 2*PI
  73. ep->eta2 -= TWOPI * floor((ep->eta2 - ep->eta1) / TWOPI);
  74. // the preceding correction fails if we have exactly eta2 - eta1 = 2*PI
  75. // it reduces the interval to zero length
  76. if (lambda2 - lambda1 > M_PI && ep->eta2 - ep->eta1 < M_PI) {
  77. ep->eta2 += TWOPI;
  78. }
  79. }
  80. typedef double erray_t[2][4][4];
  81. // coefficients for error estimation
  82. // while using cubic Bezier curves for approximation
  83. // 0 < b/a < 1/4
  84. static erray_t coeffs3Low = {
  85. {
  86. {3.85268, -21.229, -0.330434, 0.0127842},
  87. {-1.61486, 0.706564, 0.225945, 0.263682},
  88. {-0.910164, 0.388383, 0.00551445, 0.00671814},
  89. {-0.630184, 0.192402, 0.0098871, 0.0102527}
  90. },
  91. {
  92. {-0.162211, 9.94329, 0.13723, 0.0124084},
  93. {-0.253135, 0.00187735, 0.0230286, 0.01264},
  94. {-0.0695069, -0.0437594, 0.0120636, 0.0163087},
  95. {-0.0328856, -0.00926032, -0.00173573, 0.00527385}
  96. }
  97. };
  98. // coefficients for error estimation
  99. // while using cubic Bezier curves for approximation
  100. // 1/4 <= b/a <= 1
  101. static erray_t coeffs3High = {
  102. {
  103. {0.0899116, -19.2349, -4.11711, 0.183362},
  104. {0.138148, -1.45804, 1.32044, 1.38474},
  105. {0.230903, -0.450262, 0.219963, 0.414038},
  106. {0.0590565, -0.101062, 0.0430592, 0.0204699}
  107. },
  108. {
  109. {0.0164649, 9.89394, 0.0919496, 0.00760802},
  110. {0.0191603, -0.0322058, 0.0134667, -0.0825018},
  111. {0.0156192, -0.017535, 0.00326508, -0.228157},
  112. {-0.0236752, 0.0405821, -0.0173086, 0.176187}
  113. }
  114. };
  115. // safety factor to convert the "best" error approximation
  116. // into a "max bound" error
  117. static double safety3[] = {
  118. 0.001, 4.98, 0.207, 0.0067
  119. };
  120. /* Compute the value of a rational function.
  121. * This method handles rational functions where the numerator is
  122. * quadratic and the denominator is linear
  123. */
  124. #define RationalFunction(x,c) ((x * (x * c[0] + c[1]) + c[2]) / (x + c[3]))
  125. /* Estimate the approximation error for a sub-arc of the instance.
  126. * tA and tB give the start and end angle of the subarc
  127. * Returns upper bound of the approximation error between the Bezier
  128. * curve and the real ellipse
  129. */
  130. static double estimateError(ellipse_t *ep, double etaA, double etaB) {
  131. double c0, c1, eta = 0.5 * (etaA + etaB);
  132. double x = ep->b / ep->a;
  133. double dEta = etaB - etaA;
  134. double cos2 = cos(2 * eta);
  135. double cos4 = cos(4 * eta);
  136. double cos6 = cos(6 * eta);
  137. // select the right coefficient's set according to b/a
  138. double (*coeffs)[4][4];
  139. coeffs = x < 0.25 ? coeffs3Low : coeffs3High;
  140. c0 = RationalFunction(x, coeffs[0][0])
  141. + cos2 * RationalFunction(x, coeffs[0][1])
  142. + cos4 * RationalFunction(x, coeffs[0][2])
  143. + cos6 * RationalFunction(x, coeffs[0][3]);
  144. c1 = RationalFunction(x, coeffs[1][0])
  145. + cos2 * RationalFunction(x, coeffs[1][1])
  146. + cos4 * RationalFunction(x, coeffs[1][2])
  147. + cos6 * RationalFunction(x, coeffs[1][3]);
  148. return RationalFunction(x, safety3) * ep->a * exp(c0 + c1 * dEta);
  149. }
  150. DEFINE_LIST(bezier_path, pointf)
  151. /* append points to a Bezier path
  152. * Assume initial call to moveTo to initialize, followed by
  153. * calls to curveTo and lineTo, and finished with endPath.
  154. */
  155. static void moveTo(bezier_path_t *polypath, double x, double y) {
  156. bezier_path_append(polypath, (pointf){.x = x, .y = y});
  157. }
  158. static void curveTo(bezier_path_t *polypath, double x1, double y1, double x2,
  159. double y2, double x3, double y3) {
  160. bezier_path_append(polypath, (pointf){.x = x1, .y = y1});
  161. bezier_path_append(polypath, (pointf){.x = x2, .y = y2});
  162. bezier_path_append(polypath, (pointf){.x = x3, .y = y3});
  163. }
  164. static void lineTo(bezier_path_t *polypath, double x, double y) {
  165. const pointf curp = bezier_path_get(polypath, bezier_path_size(polypath) - 1);
  166. curveTo(polypath, curp.x, curp.y, x, y, x, y);
  167. }
  168. static void endPath(bezier_path_t *polypath) {
  169. const pointf p0 = bezier_path_get(polypath, 0);
  170. lineTo(polypath, p0.x, p0.y);
  171. }
  172. /* genEllipticPath:
  173. * Approximate an elliptical arc via Beziers of degree 3
  174. * The path begins and ends with line segments to the center of the ellipse.
  175. * Returned path must be freed by the caller.
  176. */
  177. static Ppolyline_t *genEllipticPath(ellipse_t * ep) {
  178. double dEta;
  179. double etaB;
  180. double cosEtaB;
  181. double sinEtaB;
  182. double aCosEtaB;
  183. double bSinEtaB;
  184. double aSinEtaB;
  185. double bCosEtaB;
  186. double xB;
  187. double yB;
  188. double xBDot;
  189. double yBDot;
  190. double t;
  191. double alpha;
  192. Ppolyline_t *polypath = gv_alloc(sizeof(Ppolyline_t));
  193. static const double THRESHOLD = 0.00001; // quality of approximation
  194. // find the number of Bezier curves needed
  195. bool found = false;
  196. int i, n = 1;
  197. while (!found && n < 1024) {
  198. double diffEta = (ep->eta2 - ep->eta1) / n;
  199. if (diffEta <= 0.5 * M_PI) {
  200. double etaOne = ep->eta1;
  201. found = true;
  202. for (i = 0; found && i < n; ++i) {
  203. double etaA = etaOne;
  204. etaOne += diffEta;
  205. found = estimateError(ep, etaA, etaOne) <= THRESHOLD;
  206. }
  207. }
  208. n = n << 1;
  209. }
  210. dEta = (ep->eta2 - ep->eta1) / n;
  211. etaB = ep->eta1;
  212. cosEtaB = cos(etaB);
  213. sinEtaB = sin(etaB);
  214. aCosEtaB = ep->a * cosEtaB;
  215. bSinEtaB = ep->b * sinEtaB;
  216. aSinEtaB = ep->a * sinEtaB;
  217. bCosEtaB = ep->b * cosEtaB;
  218. xB = ep->cx + aCosEtaB;
  219. yB = ep->cy + bSinEtaB;
  220. xBDot = -aSinEtaB;
  221. yBDot = bCosEtaB;
  222. bezier_path_t bezier_path = {0};
  223. moveTo(&bezier_path, ep->cx, ep->cy);
  224. lineTo(&bezier_path, xB, yB);
  225. t = tan(0.5 * dEta);
  226. alpha = sin(dEta) * (sqrt(4 + 3 * t * t) - 1) / 3;
  227. for (i = 0; i < n; ++i) {
  228. double xA = xB;
  229. double yA = yB;
  230. double xADot = xBDot;
  231. double yADot = yBDot;
  232. etaB += dEta;
  233. cosEtaB = cos(etaB);
  234. sinEtaB = sin(etaB);
  235. aCosEtaB = ep->a * cosEtaB;
  236. bSinEtaB = ep->b * sinEtaB;
  237. aSinEtaB = ep->a * sinEtaB;
  238. bCosEtaB = ep->b * cosEtaB;
  239. xB = ep->cx + aCosEtaB;
  240. yB = ep->cy + bSinEtaB;
  241. xBDot = -aSinEtaB;
  242. yBDot = bCosEtaB;
  243. curveTo(&bezier_path, xA + alpha * xADot, yA + alpha * yADot,
  244. xB - alpha * xBDot, yB - alpha * yBDot, xB, yB);
  245. }
  246. endPath(&bezier_path);
  247. polypath->pn = bezier_path_size(&bezier_path);
  248. polypath->ps = bezier_path_detach(&bezier_path);
  249. return polypath;
  250. }
  251. /* ellipticWedge:
  252. * Return a cubic Bezier for an elliptical wedge, with center ctr, x and y
  253. * semi-axes xsemi and ysemi, start angle angle0 and end angle angle1.
  254. * This includes beginning and ending line segments to the ellipse center.
  255. * Calling function must free storage of returned path.
  256. */
  257. Ppolyline_t *ellipticWedge(pointf ctr, double xsemi, double ysemi,
  258. double angle0, double angle1)
  259. {
  260. ellipse_t ell;
  261. Ppolyline_t *pp;
  262. initEllipse(&ell, ctr.x, ctr.y, xsemi, ysemi, angle0, angle1);
  263. pp = genEllipticPath(&ell);
  264. return pp;
  265. }