/test/c/derive.c

https://github.com/retuxx/tinyspline · C · 491 lines · 389 code · 76 blank · 26 comment · 5 complexity · 0c20329d44fc8ed6799e462b15b8907d MD5 · raw file

  1. #include <testutils.h>
  2. void derive_sequence_of_four_points(CuTest *tc)
  3. {
  4. ___SETUP___
  5. tsBSpline spline = ts_bspline_init();
  6. tsReal dist, *ctrlp = NULL, *knots = NULL;
  7. ___GIVEN___
  8. C(ts_bspline_new(4, 2, 0, TS_CLAMPED, &spline, &status))
  9. ___WHEN___
  10. C(ts_bspline_derive(&spline, 1, POINT_EPSILON, &spline, &status))
  11. ___THEN___
  12. /* Check control points of derivative. */
  13. CuAssertIntEquals(tc, 1, (int) ts_bspline_num_control_points(&spline));
  14. C(ts_bspline_control_points( &spline, &ctrlp, &status))
  15. dist = ts_distance_varargs(tc, 2, ctrlp, 0.0, 0.0);
  16. CuAssertDblEquals(tc, 0, dist, POINT_EPSILON);
  17. /* Check knots of derivative. */
  18. CuAssertIntEquals(tc, 2, (int) ts_bspline_num_knots(&spline));
  19. C(ts_bspline_knots(&spline, &knots, &status))
  20. CuAssertDblEquals(tc,
  21. TS_DOMAIN_DEFAULT_MIN, knots[0],
  22. TS_KNOT_EPSILON);
  23. CuAssertDblEquals(tc,
  24. TS_DOMAIN_DEFAULT_MAX, knots[1],
  25. TS_KNOT_EPSILON);
  26. ___TEARDOWN___
  27. ts_bspline_free(&spline);
  28. free(ctrlp);
  29. free(knots);
  30. }
  31. void derive_sequence_of_two_point_with_custom_knots(CuTest *tc)
  32. {
  33. ___SETUP___
  34. tsBSpline spline = ts_bspline_init();
  35. tsReal dist, *ctrlp = NULL, *knots = NULL;
  36. ___GIVEN___
  37. C(ts_bspline_new(2, 3, 0, TS_CLAMPED, &spline, &status))
  38. C(ts_bspline_set_knots_varargs(&spline, &status,
  39. (tsReal) -10.0, 1.0, 10.0))
  40. ___WHEN___
  41. C(ts_bspline_derive(&spline, 1, POINT_EPSILON, &spline, &status))
  42. ___THEN___
  43. /* Check control points of derivative. */
  44. CuAssertIntEquals(tc, 1, (int) ts_bspline_num_control_points(&spline));
  45. C(ts_bspline_control_points(&spline, &ctrlp, &status))
  46. dist = ts_distance_varargs(tc, 3, ctrlp, 0.0, 0.0, 0.0);
  47. CuAssertDblEquals(tc, 0, dist, POINT_EPSILON);
  48. /* Check knots of derivative. */
  49. CuAssertIntEquals(tc, 2, (int) ts_bspline_num_knots(&spline));
  50. C(ts_bspline_knots(&spline, &knots, &status))
  51. CuAssertDblEquals(tc, -10.0, knots[0], TS_KNOT_EPSILON);
  52. CuAssertDblEquals(tc, 10.0, knots[1], TS_KNOT_EPSILON);
  53. ___TEARDOWN___
  54. ts_bspline_free(&spline);
  55. free(ctrlp);
  56. free(knots);
  57. }
  58. void derive_single_line(CuTest *tc)
  59. {
  60. ___SETUP___
  61. tsBSpline spline = ts_bspline_init();
  62. tsReal dist, *ctrlp = NULL, *knots = NULL;
  63. ___GIVEN___
  64. C(ts_bspline_new_with_control_points(
  65. 2, 2, 1, TS_CLAMPED, &spline, &status,
  66. 1.0, -2.0, /* P1 */
  67. 3.0, 6.0)) /* P2 */
  68. ___WHEN___
  69. C(ts_bspline_derive(&spline, 1, POINT_EPSILON, &spline, &status))
  70. ___THEN___
  71. /* Check control points of derivative. */
  72. CuAssertIntEquals(tc, 1, (int) ts_bspline_num_control_points(&spline));
  73. C(ts_bspline_control_points(&spline, &ctrlp, &status))
  74. dist = ts_distance_varargs(tc, 2, ctrlp, 2.0, 8.0);
  75. CuAssertDblEquals(tc, 0, dist, POINT_EPSILON);
  76. /* Check knots of derivative. */
  77. CuAssertIntEquals(tc, 2, (int) ts_bspline_num_knots(&spline));
  78. C(ts_bspline_knots(&spline, &knots, &status))
  79. CuAssertDblEquals(tc,
  80. TS_DOMAIN_DEFAULT_MIN, knots[0],
  81. TS_KNOT_EPSILON);
  82. CuAssertDblEquals(tc,
  83. TS_DOMAIN_DEFAULT_MAX, knots[1],
  84. TS_KNOT_EPSILON);
  85. ___TEARDOWN___
  86. ts_bspline_free(&spline);
  87. free(ctrlp);
  88. free(knots);
  89. }
  90. void derive_single_line_with_custom_knots(CuTest *tc)
  91. {
  92. ___SETUP___
  93. tsBSpline spline = ts_bspline_init();
  94. tsDeBoorNet net = ts_deboornet_init();
  95. tsReal *ctrlp = NULL, *knots = NULL, *result = NULL;
  96. tsReal min, max, span, step, dist, slope[2];
  97. size_t i, num_samples = 3;
  98. ___GIVEN___
  99. C(ts_bspline_new(2, 2, 1, TS_CLAMPED, &spline, &status))
  100. /* Set up control points. */
  101. C(ts_bspline_control_points(&spline, &ctrlp, &status))
  102. ctrlp[0] = 1.0; ctrlp[1] = -2.0; /* P1 */
  103. ctrlp[2] = 3.0; ctrlp[3] = 6.0; /* P2 */
  104. C(ts_bspline_set_control_points(&spline, ctrlp, &status))
  105. /* Set up knots. */
  106. C(ts_bspline_set_knots_varargs(&spline, &status,
  107. (tsReal) -2.0, -1.0, 1.0, 2.0))
  108. /* Confirm that the spline is a line with desired slope. */
  109. ts_bspline_domain(&spline, &min, &max);
  110. span = max - min;
  111. slope[0] = (ctrlp[2] - ctrlp[0]) / span;
  112. slope[1] = (ctrlp[3] - ctrlp[1]) / span;
  113. step = span / (tsReal) (num_samples - 1);
  114. for(i = 0; i < num_samples; ++i) {
  115. C(ts_bspline_eval(&spline, min + step * i, &net, &status))
  116. C(ts_deboornet_result(&net, &result, &status))
  117. dist = ts_distance_varargs(tc, 2, result,
  118. ctrlp[0] + slope[0] * (step * i),
  119. ctrlp[1] + slope[1] * (step * i));
  120. CuAssertDblEquals(tc, 0, dist, POINT_EPSILON);
  121. ts_deboornet_free(&net);
  122. free(result);
  123. result = NULL;
  124. }
  125. free(ctrlp);
  126. ctrlp = NULL;
  127. ___WHEN___
  128. C(ts_bspline_derive(&spline, 1, POINT_EPSILON, &spline, &status))
  129. ___THEN___
  130. /* Check control points of derivative. */
  131. CuAssertIntEquals(tc, 1, (int) ts_bspline_num_control_points(&spline));
  132. C(ts_bspline_control_points(&spline, &ctrlp, &status))
  133. /* Scaled with domain [-1, 1]. */
  134. dist = ts_distance_varargs(tc, 2, ctrlp, 2.0 / 2.0, 8.0 / 2.0);
  135. CuAssertDblEquals(tc, 0, dist, POINT_EPSILON);
  136. /* Check knots of derivative. */
  137. CuAssertIntEquals(tc, 2, (int) ts_bspline_num_knots(&spline));
  138. C(ts_bspline_knots(&spline, &knots, &status))
  139. CuAssertDblEquals(tc, -1.0, knots[0], TS_KNOT_EPSILON);
  140. CuAssertDblEquals(tc, 1.0, knots[1], TS_KNOT_EPSILON);
  141. /* Evaluate derivative at mid-span. */
  142. C(ts_bspline_eval(&spline, (tsReal) (min + span / 2.0), &net, &status))
  143. C(ts_deboornet_result(&net, &result, &status))
  144. dist = ts_distance(result, slope, ts_bspline_dimension(&spline));
  145. CuAssertDblEquals(tc, 0, dist, POINT_EPSILON);
  146. ___TEARDOWN___
  147. ts_bspline_free(&spline);
  148. ts_deboornet_free(&net);
  149. free(result);
  150. free(ctrlp);
  151. free(knots);
  152. }
  153. void derive_single_parabola_with_custom_knots(CuTest *tc)
  154. {
  155. ___SETUP___
  156. tsBSpline spline = ts_bspline_init();
  157. tsDeBoorNet net = ts_deboornet_init();
  158. size_t degree, i, num_samples = 10;
  159. tsReal slope, span, step, ctrlp[3], knots[6], *result = NULL;
  160. ___GIVEN___
  161. /* Set the initial position, slope, and final position. Note that this
  162. * defines a parabola by degree, but a line by shape. */
  163. degree = 2;
  164. slope = (tsReal) 3.0;
  165. span = (tsReal) 5.0;
  166. ctrlp[0] = (tsReal) -7.0;
  167. ctrlp[1] = ctrlp[0] + slope * span / degree;
  168. ctrlp[2] = ctrlp[0] + slope * span;
  169. knots[0] = (tsReal) -1.0;
  170. knots[1] = knots[0];
  171. knots[2] = knots[0];
  172. knots[3] = knots[0] + span;
  173. knots[4] = knots[0] + span;
  174. knots[5] = knots[0] + span;
  175. /* Create spline with control points and custom knots. */
  176. C(ts_bspline_new(3, 1, degree, TS_CLAMPED, &spline, &status))
  177. C(ts_bspline_set_control_points(&spline, ctrlp, &status))
  178. C(ts_bspline_set_knots(&spline, knots, &status))
  179. /* Confirm that the spline is a line with desired slope. */
  180. step = (span - knots[0]) / (tsReal) (num_samples + 1);
  181. for(i = 0; i < num_samples; ++i) {
  182. C(ts_bspline_eval(&spline, knots[0] + (step * i),
  183. &net, &status))
  184. C(ts_deboornet_result(&net, &result, &status))
  185. CuAssertDblEquals(tc, /* One-dimensional spline. */
  186. ctrlp[0] + slope * (step * i), result[0],
  187. POINT_EPSILON);
  188. ts_deboornet_free(&net);
  189. free(result);
  190. result = NULL;
  191. }
  192. ___WHEN___
  193. C(ts_bspline_derive(&spline, 1, POINT_EPSILON, &spline, &status))
  194. ___THEN___
  195. for(i = 0; i < num_samples; ++i) {
  196. C(ts_bspline_eval(&spline, knots[0] + (step * i),
  197. &net, &status))
  198. C(ts_deboornet_result(&net, &result, &status))
  199. /* One-dimensional spline. */
  200. CuAssertDblEquals(tc, slope, result[0], POINT_EPSILON);
  201. ts_deboornet_free(&net);
  202. free(result);
  203. result = NULL;
  204. }
  205. ___TEARDOWN___
  206. ts_bspline_free(&spline);
  207. ts_deboornet_free(&net);
  208. free(result);
  209. }
  210. void derive_discontinuous_and_compare_with_continuous(CuTest *tc)
  211. {
  212. ___SETUP___
  213. tsBSpline spline = ts_bspline_init();
  214. tsBSpline beziers = ts_bspline_init();
  215. tsBSpline derivative = ts_bspline_init();
  216. tsDeBoorNet net = ts_deboornet_init();
  217. tsReal dist, *result = NULL;
  218. ___GIVEN___
  219. C(ts_bspline_new_with_control_points(
  220. 7, 2, 3, TS_CLAMPED, &spline, &status,
  221. -1.75, -1.0, /* P1 */
  222. -1.5, -0.5, /* P2 */
  223. -1.5, 0.0, /* P3 */
  224. -1.25, 0.5, /* P4 */
  225. -0.75, 0.75, /* P5 */
  226. 0.0, 0.5, /* P6 */
  227. 0.5, 0.0)) /* P7 */
  228. ___WHEN___
  229. /* Create beziers (to_beziers => derive). */
  230. C(ts_bspline_to_beziers(&spline, &beziers, &status))
  231. C(ts_bspline_derive(&beziers, 1, POINT_EPSILON, &beziers, &status))
  232. /* Create derivative (derive => to_beziers). */
  233. C(ts_bspline_derive(&spline, 1, POINT_EPSILON, &derivative, &status))
  234. C(ts_bspline_to_beziers(&derivative, &derivative, &status))
  235. ___THEN___
  236. /* Check that beziers and derivative are different splines. */
  237. CuAssertTrue(tc, beziers.pImpl != derivative.pImpl);
  238. assert_equal_shape(tc, &beziers, &derivative);
  239. /* Eval known values. */
  240. C(ts_bspline_eval(&beziers, (tsReal) 0.3, &net, &status))
  241. CuAssertIntEquals(tc, 1, (int) ts_deboornet_num_result(&net));
  242. C(ts_deboornet_result(&net, &result, &status))
  243. dist = ts_distance_varargs(tc, 2, result, 0.7, 2.3);
  244. CuAssertDblEquals(tc, 0, dist, POINT_EPSILON);
  245. ___TEARDOWN___
  246. ts_bspline_free(&spline);
  247. ts_bspline_free(&beziers);
  248. ts_bspline_free(&derivative);
  249. ts_deboornet_free(&net);
  250. free(result);
  251. }
  252. void derive_discontinuous_lines_exceeding_epsilon(CuTest *tc)
  253. {
  254. ___SETUP___
  255. tsBSpline spline = ts_bspline_init();
  256. tsError err;
  257. ___GIVEN___
  258. C(ts_bspline_new_with_control_points(
  259. 4, 2, 1, TS_CLAMPED, &spline, &status,
  260. 0.0, 0.0, /* P1 */
  261. 1.0, 1.0, /* P2 */
  262. 1.0, 1.5, /* P3 */
  263. 2.0, 2.0)) /* P4 */
  264. C(ts_bspline_set_knots_varargs(&spline, &status,
  265. (tsReal) 0.0, 0.0, 0.4, 0.4, 1.0, 1.0))
  266. ___WHEN___
  267. err = ts_bspline_derive(&spline, 1, POINT_EPSILON, &spline, NULL);
  268. ___THEN___
  269. CuAssertIntEquals(tc, TS_UNDERIVABLE, err);
  270. ___TEARDOWN___
  271. ts_bspline_free(&spline);
  272. }
  273. void derive_discontinuous_lines_ignoring_epsilon(CuTest *tc)
  274. {
  275. ___SETUP___
  276. tsBSpline spline = ts_bspline_init();
  277. tsDeBoorNet net = ts_deboornet_init();
  278. tsError err;
  279. tsReal dist, *result = NULL;
  280. ___GIVEN___
  281. C(ts_bspline_new_with_control_points(
  282. 4, 2, 1, TS_CLAMPED, &spline, &status,
  283. 2.0, 2.0, /* P1 */
  284. 3.0, 3.0, /* P2 */
  285. 4.0, 4.0, /* P3 */ /* <-- Removed by derivation. */
  286. 1.0, 1.0)) /* P4 */
  287. C(ts_bspline_set_knots_varargs(&spline, &status,
  288. (tsReal) 0.0, 0.0, 0.7, 0.7, 1.0, 1.0))
  289. ___WHEN___
  290. err = ts_bspline_derive(&spline, 1, (tsReal) -1.0, &spline, NULL);
  291. ___THEN___
  292. CuAssertIntEquals(tc, TS_SUCCESS, err);
  293. /* Eval derivative. */
  294. C(ts_bspline_eval(&spline, (tsReal) 0.7, &net, &status))
  295. CuAssertIntEquals(tc, 2, (int)ts_deboornet_num_result(&net));
  296. C(ts_deboornet_result(&net, &result, &status))
  297. dist = ts_distance_varargs(tc, 2, result, 1.0 / 0.7, 1.0 / 0.7);
  298. CuAssertDblEquals(tc, 0, dist, POINT_EPSILON);
  299. dist = ts_distance_varargs(tc, 2, result + 2, -2.0 / 0.3, -2.0 / 0.3);
  300. CuAssertDblEquals(tc, 0, dist, POINT_EPSILON);
  301. ___TEARDOWN___
  302. ts_bspline_free(&spline);
  303. ts_deboornet_free(&net);
  304. free(result);
  305. }
  306. void derive_continuous_spline(CuTest *tc)
  307. {
  308. ___SETUP___
  309. tsBSpline spline = ts_bspline_init();
  310. tsDeBoorNet net = ts_deboornet_init();
  311. tsReal dist, *result = NULL;
  312. ___GIVEN___
  313. C(ts_bspline_new_with_control_points(
  314. 10, 2, 2, TS_CLAMPED, &spline, &status,
  315. -1.75, -1.0, /* P1 */
  316. -1.5, -0.5, /* P2 */
  317. -1.5, 0.0, /* P3 */
  318. -1.25, 0.5, /* P4 */
  319. -0.75, 0.75, /* P5 */
  320. 0.0, 0.5, /* P6 */
  321. 0.5, 0.0, /* P7 */
  322. 1.5, 0.5, /* P8 */
  323. 2.0, 1.5, /* P9 */
  324. 2.5, 1.0)) /* P10 */
  325. ___WHEN___
  326. C(ts_bspline_derive(&spline, 1, POINT_EPSILON, &spline, &status))
  327. ___THEN___
  328. C(ts_bspline_eval(&spline, (tsReal) 0.8, &net, &status))
  329. C(ts_deboornet_result(&net, &result, &status))
  330. dist = ts_distance_varargs(tc, 2, result, 6.4, 5.6);
  331. CuAssertDblEquals(tc, 0, dist, POINT_EPSILON);
  332. ___TEARDOWN___
  333. ts_bspline_free(&spline);
  334. ts_deboornet_free(&net);
  335. free(result);
  336. }
  337. void derive_continuous_spline_with_custom_knots(CuTest *tc)
  338. {
  339. ___SETUP___
  340. tsBSpline spline = ts_bspline_init();
  341. tsDeBoorNet net = ts_deboornet_init();
  342. tsReal dist, *result = NULL;
  343. ___GIVEN___
  344. C(ts_bspline_new_with_control_points(
  345. 10, 2, 2, TS_CLAMPED, &spline, &status,
  346. -1.75, -1.0, /* P1 */
  347. -1.5, -0.5, /* P2 */
  348. -1.5, 0.0, /* P3 */
  349. -1.25, 0.5, /* P4 */
  350. -0.75, 0.75, /* P5 */
  351. 0.0, 0.5, /* P6 */
  352. 0.5, 0.0, /* P7 */
  353. 1.5, 0.5, /* P8 */
  354. 2.0, 1.5, /* P9 */
  355. 2.5, 1.0)) /* P10 */
  356. C(ts_bspline_set_knots_varargs(&spline, &status,
  357. 2.0, 2.0, 2.0, 3.0, 4.0, 5.0,
  358. 6.0, 7.0, 8.0, 9.0, 10.0, 10.0, 10.0));
  359. ___WHEN___
  360. C(ts_bspline_derive(&spline, 1, POINT_EPSILON, &spline, &status))
  361. ___THEN___
  362. C(ts_bspline_eval(&spline, (tsReal) 8.4, &net, &status))
  363. C(ts_deboornet_result(&net, &result, &status))
  364. dist = ts_distance_varargs(tc, 2, result, 0.8, 0.7);
  365. CuAssertDblEquals(tc, 0, dist, POINT_EPSILON);
  366. ___TEARDOWN___
  367. ts_bspline_free(&spline);
  368. ts_deboornet_free(&net);
  369. free(result);
  370. }
  371. void derive_compare_third_derivative_with_three_times(CuTest *tc)
  372. {
  373. ___SETUP___
  374. tsBSpline spline = ts_bspline_init();
  375. tsBSpline third = ts_bspline_init();
  376. tsBSpline three = ts_bspline_init();
  377. ___GIVEN___
  378. C(ts_bspline_new_with_control_points(
  379. 4, 2, 3, TS_OPENED, &spline, &status,
  380. 1.0, 1.0, /* P1 */
  381. 2.0, 4.0, /* P2 */
  382. 3.0, 3.0, /* P3 */
  383. 4.0, 0.0)) /* P4 */
  384. ___WHEN___
  385. /* Create third (derive with n = 3). */
  386. C(ts_bspline_derive(&spline, 3, POINT_EPSILON, &third, &status))
  387. /* Create three (derive three times). */
  388. C(ts_bspline_derive(&spline, 1, POINT_EPSILON, &three, &status))
  389. C(ts_bspline_derive(&three, 1, POINT_EPSILON, &three, &status))
  390. C(ts_bspline_derive(&three, 1, POINT_EPSILON, &three, &status))
  391. ___THEN___
  392. /* Check that third and three are different splines. */
  393. CuAssertTrue(tc, third.pImpl != three.pImpl);
  394. assert_equal_shape(tc, &third, &three);
  395. ___TEARDOWN___
  396. ts_bspline_free(&spline);
  397. ts_bspline_free(&third);
  398. ts_bspline_free(&three);
  399. }
  400. CuSuite* get_derive_suite()
  401. {
  402. CuSuite* suite = CuSuiteNew();
  403. SUITE_ADD_TEST(suite, derive_sequence_of_four_points);
  404. SUITE_ADD_TEST(suite, derive_sequence_of_two_point_with_custom_knots);
  405. SUITE_ADD_TEST(suite, derive_single_line);
  406. SUITE_ADD_TEST(suite, derive_single_line_with_custom_knots);
  407. SUITE_ADD_TEST(suite, derive_single_parabola_with_custom_knots);
  408. SUITE_ADD_TEST(suite, derive_discontinuous_and_compare_with_continuous);
  409. SUITE_ADD_TEST(suite, derive_discontinuous_lines_exceeding_epsilon);
  410. SUITE_ADD_TEST(suite, derive_discontinuous_lines_ignoring_epsilon);
  411. SUITE_ADD_TEST(suite, derive_continuous_spline);
  412. SUITE_ADD_TEST(suite, derive_continuous_spline_with_custom_knots);
  413. SUITE_ADD_TEST(suite, derive_compare_third_derivative_with_three_times);
  414. return suite;
  415. }