bezctx_hittest.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /*
  2. ppedit - A pattern plate editor for Spiro splines.
  3. Copyright (C) 2007 Raph Levien
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301, USA.
  16. */
  17. #include "zmisc.h"
  18. #include "bezctx.h"
  19. #include "bezctx_hittest.h"
  20. #include <math.h>
  21. typedef struct {
  22. bezctx base;
  23. double x, y;
  24. double x0, y0;
  25. int knot_idx;
  26. int knot_idx_min;
  27. double r_min;
  28. } bezctx_hittest;
  29. static void
  30. bezctx_hittest_moveto(bezctx *z, double x, double y, int is_open) {
  31. bezctx_hittest *bc = (bezctx_hittest *)z;
  32. bc->x0 = x;
  33. bc->y0 = y;
  34. }
  35. static void
  36. bezctx_hittest_lineto(bezctx *z, double x, double y) {
  37. bezctx_hittest *bc = (bezctx_hittest *)z;
  38. double x0 = bc->x0;
  39. double y0 = bc->y0;
  40. double dx = x - x0;
  41. double dy = y - y0;
  42. double dotp = (bc->x - x0) * dx + (bc->y - y0) * dy;
  43. double lin_dotp = dx * dx + dy * dy;
  44. double r_min, r;
  45. r = hypot(bc->x - x0, bc->y - y0);
  46. r_min = r;
  47. r = hypot(bc->x - x, bc->y - y);
  48. if (r < r_min) r_min = r;
  49. if (dotp >= 0 && dotp <= lin_dotp) {
  50. double norm = (bc->x - x0) * dy - (bc->y - y0) * dx;
  51. r = fabs(norm / sqrt(lin_dotp));
  52. if (r < r_min) r_min = r;
  53. }
  54. if (r_min < bc->r_min) {
  55. bc->r_min = r_min;
  56. bc->knot_idx_min = bc->knot_idx;
  57. }
  58. bc->x0 = x;
  59. bc->y0 = y;
  60. }
  61. #define cube(x) ((x) * (x) * (x))
  62. static double
  63. my_cbrt(double x)
  64. {
  65. if (x >= 0)
  66. return pow(x, 1.0 / 3.0);
  67. else
  68. return -pow(-x, 1.0 / 3.0);
  69. }
  70. /**
  71. * Give real roots to eqn c0 + c1 * x + c2 * x^2 + c3 * x^3 == 0.
  72. * Return value is number of roots found.
  73. **/
  74. static int
  75. solve_cubic(double c0, double c1, double c2, double c3, double root[3])
  76. {
  77. double p, q, r, a, b, Q, x0;
  78. p = c2 / c3;
  79. q = c1 / c3;
  80. r = c0 / c3;
  81. a = (3 * q - p * p) / 3;
  82. b = (2 * cube(p) - 9 * p * q + 27 * r) / 27;
  83. Q = b * b / 4 + cube(a) / 27;
  84. x0 = p / 3;
  85. if (Q > 0) {
  86. double sQ = sqrt(Q);
  87. double t1 = my_cbrt(-b/2 + sQ) + my_cbrt(-b/2 - sQ);
  88. root[0] = t1 - x0;
  89. return 1;
  90. } else if (Q == 0) {
  91. double t1 = my_cbrt(b / 2);
  92. double x1 = t1 - x0;
  93. root[0] = x1;
  94. root[1] = x1;
  95. root[2] = -2 * t1 - x0;
  96. return 3;
  97. } else {
  98. double sQ = sqrt(-Q);
  99. double rho = hypot(-b/2, sQ);
  100. double th = atan2(sQ, -b/2);
  101. double cbrho = my_cbrt(rho);
  102. double c = cos(th / 3);
  103. double s = sin(th / 3);
  104. double sqr3 = sqrt(3);
  105. root[0] = 2 * cbrho * c - x0;
  106. root[1] = -cbrho * (c + sqr3 * s) - x0;
  107. root[2] = -cbrho * (c - sqr3 * s) - x0;
  108. return 3;
  109. }
  110. }
  111. static double
  112. dist_to_quadratic(double x, double y,
  113. double x0, double y0,
  114. double x1, double y1,
  115. double x2, double y2)
  116. {
  117. double u0, u1, t0, t1, t2, c0, c1, c2, c3;
  118. double roots[3];
  119. int n_roots;
  120. double ts[4];
  121. int n_ts;
  122. int i;
  123. double minerr = 0;
  124. u0 = x1 - x0;
  125. u1 = x0 - 2 * x1 + x2;
  126. t0 = x0 - x;
  127. t1 = 2 * u0;
  128. t2 = u1;
  129. c0 = t0 * u0;
  130. c1 = t1 * u0 + t0 * u1;
  131. c2 = t2 * u0 + t1 * u1;
  132. c3 = t2 * u1;
  133. u0 = y1 - y0;
  134. u1 = y0 - 2 * y1 + y2;
  135. t0 = y0 - y;
  136. t1 = 2 * u0;
  137. t2 = u1;
  138. c0 += t0 * u0;
  139. c1 += t1 * u0 + t0 * u1;
  140. c2 += t2 * u0 + t1 * u1;
  141. c3 += t2 * u1;
  142. n_roots = solve_cubic(c0, c1, c2, c3, roots);
  143. n_ts = 0;
  144. for (i = 0; i < n_roots; i++) {
  145. double t = roots[i];
  146. if (t > 0 && t < 1)
  147. ts[n_ts++] = t;
  148. }
  149. if (n_ts < n_roots) {
  150. ts[n_ts++] = 0;
  151. ts[n_ts++] = 1;
  152. }
  153. for (i = 0; i < n_ts; i++) {
  154. double t = ts[i];
  155. double xa = x0 * (1 - t) * (1 - t) + 2 * x1 * (1 - t) * t + x2 * t * t;
  156. double ya = y0 * (1 - t) * (1 - t) + 2 * y1 * (1 - t) * t + y2 * t * t;
  157. double err = hypot(xa - x, ya - y);
  158. if (i == 0 || err < minerr) {
  159. minerr = err;
  160. }
  161. }
  162. return minerr;
  163. }
  164. static void
  165. bezctx_hittest_quadto(bezctx *z, double x1, double y1, double x2, double y2)
  166. {
  167. bezctx_hittest *bc = (bezctx_hittest *)z;
  168. double r = dist_to_quadratic(bc->x, bc->y,
  169. bc->x0, bc->y0, x1, y1, x2, y2);
  170. if (r < bc->r_min) {
  171. bc->r_min = r;
  172. bc->knot_idx_min = bc->knot_idx;
  173. }
  174. bc->x0 = x2;
  175. bc->y0 = y2;
  176. }
  177. static void
  178. bezctx_hittest_curveto(bezctx *z, double x1, double y1, double x2, double y2,
  179. double x3, double y3)
  180. {
  181. bezctx_hittest *bc = (bezctx_hittest *)z;
  182. double x0 = bc->x0;
  183. double y0 = bc->y0;
  184. int n_subdiv = 32;
  185. int i;
  186. double xq2, yq2;
  187. /* todo: subdivide to quadratics rather than lines */
  188. for (i = 0; i < n_subdiv; i++) {
  189. double t = (1. / n_subdiv) * (i + 1);
  190. double mt = 1 - t;
  191. xq2 = x0 * mt * mt * mt + 3 * x1 * mt * t * t + 3 * x2 * mt * mt * t +
  192. x3 * t * t * t;
  193. yq2 = y0 * mt * mt * mt + 3 * y1 * mt * t * t + 3 * y2 * mt * mt * t +
  194. y3 * t * t * t;
  195. bezctx_hittest_lineto(z, xq2, yq2);
  196. }
  197. }
  198. static void
  199. bezctx_hittest_mark_knot(bezctx *z, int knot_idx) {
  200. bezctx_hittest *bc = (bezctx_hittest *)z;
  201. bc->knot_idx = knot_idx;
  202. }
  203. bezctx *
  204. new_bezctx_hittest(double x, double y) {
  205. bezctx_hittest *result = znew(bezctx_hittest, 1);
  206. result->base.moveto = bezctx_hittest_moveto;
  207. result->base.lineto = bezctx_hittest_lineto;
  208. result->base.quadto = bezctx_hittest_quadto;
  209. result->base.curveto = bezctx_hittest_curveto;
  210. result->base.mark_knot = bezctx_hittest_mark_knot;
  211. result->x = x;
  212. result->y = y;
  213. result->knot_idx_min = -1;
  214. result->r_min = 1e12;
  215. return &result->base;
  216. }
  217. double
  218. bezctx_hittest_report(bezctx *z, int *p_knot_idx)
  219. {
  220. bezctx_hittest *bc = (bezctx_hittest *)z;
  221. double r_min = bc->r_min;
  222. if (p_knot_idx)
  223. *p_knot_idx = bc->knot_idx_min;
  224. zfree(z);
  225. return r_min;
  226. }