You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3481 lines
100 KiB

  1. #define TINYSPLINE_EXPORT
  2. #include "tinyspline.h"
  3. #include "parson.h" /* serialization */
  4. #include <stdlib.h> /* malloc, free */
  5. #include <math.h> /* fabs, sqrt, acos */
  6. #include <string.h> /* memcpy, memmove */
  7. #include <stdio.h> /* FILE, fopen */
  8. #include <stdarg.h> /* varargs */
  9. /* Suppress some useless MSVC warnings. */
  10. #ifdef _MSC_VER
  11. #pragma warning(push)
  12. /* address of dllimport */
  13. #pragma warning(disable:4232)
  14. /* function not inlined */
  15. #pragma warning(disable:4710)
  16. /* byte padding */
  17. #pragma warning(disable:4820)
  18. /* meaningless deprecation */
  19. #pragma warning(disable:4996)
  20. /* Spectre mitigation */
  21. #pragma warning(disable:5045)
  22. #endif
  23. #define INIT_OUT_BSPLINE(in, out) \
  24. if ((in) != (out)) \
  25. ts_int_bspline_init(out);
  26. /*! @name Internal Structs and Functions
  27. *
  28. * Internal functions are prefixed with \e ts_int (int for internal).
  29. *
  30. * @{
  31. */
  32. /**
  33. * Stores the private data of ::tsBSpline.
  34. */
  35. struct tsBSplineImpl
  36. {
  37. size_t deg; /**< Degree of B-Spline basis function. */
  38. size_t dim; /**< Dimensionality of the control points (2D => x, y). */
  39. size_t n_ctrlp; /**< Number of control points. */
  40. size_t n_knots; /**< Number of knots (n_ctrlp + deg + 1). */
  41. };
  42. /**
  43. * Stores the private data of ::tsDeBoorNet.
  44. */
  45. struct tsDeBoorNetImpl
  46. {
  47. tsReal u; /**< The evaluated knot. */
  48. size_t k; /**< The index [u_k, u_k+1) */
  49. size_t s; /**< Multiplicity of u_k. */
  50. size_t h; /**< Number of insertions required to obtain result. */
  51. size_t dim; /**< Dimensionality of the points (2D => x, y). */
  52. size_t n_points; /** Number of points in `points'. */
  53. };
  54. void
  55. ts_int_bspline_init(tsBSpline *spline)
  56. {
  57. spline->pImpl = NULL;
  58. }
  59. size_t
  60. ts_int_bspline_sof_state(const tsBSpline *spline)
  61. {
  62. return sizeof(struct tsBSplineImpl) +
  63. ts_bspline_sof_control_points(spline) +
  64. ts_bspline_sof_knots(spline);
  65. }
  66. tsReal *
  67. ts_int_bspline_access_ctrlp(const tsBSpline *spline)
  68. {
  69. return (tsReal *) (& spline->pImpl[1]);
  70. }
  71. tsReal *
  72. ts_int_bspline_access_knots(const tsBSpline *spline)
  73. {
  74. return ts_int_bspline_access_ctrlp(spline) +
  75. ts_bspline_len_control_points(spline);
  76. }
  77. tsError
  78. ts_int_bspline_access_ctrlp_at(const tsBSpline *spline,
  79. size_t index,
  80. tsReal **ctrlp,
  81. tsStatus *status)
  82. {
  83. const size_t num = ts_bspline_num_control_points(spline);
  84. if (index >= num) {
  85. TS_RETURN_2(status, TS_INDEX_ERROR,
  86. "index (%lu) >= num(control_points) (%lu)",
  87. (unsigned long) index,
  88. (unsigned long) num)
  89. }
  90. *ctrlp = ts_int_bspline_access_ctrlp(spline) +
  91. index * ts_bspline_dimension(spline);
  92. TS_RETURN_SUCCESS(status)
  93. }
  94. tsError
  95. ts_int_bspline_access_knot_at(const tsBSpline *spline,
  96. size_t index,
  97. tsReal *knot,
  98. tsStatus *status)
  99. {
  100. const size_t num = ts_bspline_num_knots(spline);
  101. if (index >= num) {
  102. TS_RETURN_2(status, TS_INDEX_ERROR,
  103. "index (%lu) >= num(knots) (%lu)",
  104. (unsigned long) index,
  105. (unsigned long) num)
  106. }
  107. *knot = ts_int_bspline_access_knots(spline)[index];
  108. TS_RETURN_SUCCESS(status)
  109. }
  110. void
  111. ts_int_deboornet_init(tsDeBoorNet *net)
  112. {
  113. net->pImpl = NULL;
  114. }
  115. size_t
  116. ts_int_deboornet_sof_state(const tsDeBoorNet *net)
  117. {
  118. return sizeof(struct tsDeBoorNetImpl) +
  119. ts_deboornet_sof_points(net) +
  120. ts_deboornet_sof_result(net);
  121. }
  122. tsReal *
  123. ts_int_deboornet_access_points(const tsDeBoorNet *net)
  124. {
  125. return (tsReal *) (& net->pImpl[1]);
  126. }
  127. tsReal *
  128. ts_int_deboornet_access_result(const tsDeBoorNet *net)
  129. {
  130. if (ts_deboornet_num_result(net) == 2) {
  131. return ts_int_deboornet_access_points(net);
  132. } else {
  133. return ts_int_deboornet_access_points(net) +
  134. /* Last point in `points`. */
  135. (ts_deboornet_len_points(net) -
  136. ts_deboornet_dimension(net));
  137. }
  138. }
  139. /*! @} */
  140. /*! @name B-Spline Data
  141. *
  142. * @{
  143. */
  144. size_t
  145. ts_bspline_degree(const tsBSpline *spline)
  146. {
  147. return spline->pImpl->deg;
  148. }
  149. size_t
  150. ts_bspline_order(const tsBSpline *spline)
  151. {
  152. return ts_bspline_degree(spline) + 1;
  153. }
  154. size_t
  155. ts_bspline_dimension(const tsBSpline *spline)
  156. {
  157. return spline->pImpl->dim;
  158. }
  159. size_t
  160. ts_bspline_len_control_points(const tsBSpline *spline)
  161. {
  162. return ts_bspline_num_control_points(spline) *
  163. ts_bspline_dimension(spline);
  164. }
  165. size_t
  166. ts_bspline_num_control_points(const tsBSpline *spline)
  167. {
  168. return spline->pImpl->n_ctrlp;
  169. }
  170. size_t
  171. ts_bspline_sof_control_points(const tsBSpline *spline)
  172. {
  173. return ts_bspline_len_control_points(spline) * sizeof(tsReal);
  174. }
  175. const tsReal *
  176. ts_bspline_control_points_ptr(const tsBSpline *spline)
  177. {
  178. return ts_int_bspline_access_ctrlp(spline);
  179. }
  180. tsError
  181. ts_bspline_control_points(const tsBSpline *spline,
  182. tsReal **ctrlp,
  183. tsStatus *status)
  184. {
  185. const size_t size = ts_bspline_sof_control_points(spline);
  186. *ctrlp = (tsReal*) malloc(size);
  187. if (!*ctrlp) TS_RETURN_0(status, TS_MALLOC, "out of memory")
  188. memcpy(*ctrlp, ts_int_bspline_access_ctrlp(spline), size);
  189. TS_RETURN_SUCCESS(status)
  190. }
  191. tsError
  192. ts_bspline_control_point_at_ptr(const tsBSpline *spline,
  193. size_t index,
  194. const tsReal **ctrlp,
  195. tsStatus *status)
  196. {
  197. tsReal *vals;
  198. tsError err;
  199. TS_TRY(try, err, status)
  200. TS_CALL(try, err, ts_int_bspline_access_ctrlp_at(
  201. spline, index, &vals, status))
  202. *ctrlp = vals;
  203. TS_CATCH(err)
  204. *ctrlp = NULL;
  205. TS_END_TRY_RETURN(err)
  206. }
  207. tsError
  208. ts_bspline_set_control_points(tsBSpline *spline,
  209. const tsReal *ctrlp,
  210. tsStatus *status)
  211. {
  212. const size_t size = ts_bspline_sof_control_points(spline);
  213. memmove(ts_int_bspline_access_ctrlp(spline), ctrlp, size);
  214. TS_RETURN_SUCCESS(status)
  215. }
  216. tsError
  217. ts_bspline_set_control_point_at(tsBSpline *spline,
  218. size_t index,
  219. const tsReal *ctrlp,
  220. tsStatus *status)
  221. {
  222. tsReal *to;
  223. size_t size;
  224. tsError err;
  225. TS_TRY(try, err, status)
  226. TS_CALL(try, err, ts_int_bspline_access_ctrlp_at(
  227. spline, index, &to, status))
  228. size = ts_bspline_dimension(spline) * sizeof(tsReal);
  229. memcpy(to, ctrlp, size);
  230. TS_END_TRY_RETURN(err)
  231. }
  232. size_t
  233. ts_bspline_num_knots(const tsBSpline *spline)
  234. {
  235. return spline->pImpl->n_knots;
  236. }
  237. size_t
  238. ts_bspline_sof_knots(const tsBSpline *spline)
  239. {
  240. return ts_bspline_num_knots(spline) * sizeof(tsReal);
  241. }
  242. const tsReal *
  243. ts_bspline_knots_ptr(const tsBSpline *spline)
  244. {
  245. return ts_int_bspline_access_knots(spline);
  246. }
  247. tsError
  248. ts_bspline_knots(const tsBSpline *spline,
  249. tsReal **knots,
  250. tsStatus *status)
  251. {
  252. const size_t size = ts_bspline_sof_knots(spline);
  253. *knots = (tsReal*) malloc(size);
  254. if (!*knots) TS_RETURN_0(status, TS_MALLOC, "out of memory")
  255. memcpy(*knots, ts_int_bspline_access_knots(spline), size);
  256. TS_RETURN_SUCCESS(status)
  257. }
  258. tsError
  259. ts_bspline_knot_at(const tsBSpline *spline,
  260. size_t index,
  261. tsReal *knot,
  262. tsStatus *status)
  263. {
  264. return ts_int_bspline_access_knot_at(spline, index, knot, status);
  265. }
  266. tsError
  267. ts_bspline_set_knots(tsBSpline *spline,
  268. const tsReal *knots,
  269. tsStatus *status)
  270. {
  271. const size_t size = ts_bspline_sof_knots(spline);
  272. const size_t num_knots = ts_bspline_num_knots(spline);
  273. const size_t order = ts_bspline_order(spline);
  274. size_t idx, mult;
  275. tsReal lst_knot, knot;
  276. lst_knot = knots[0];
  277. mult = 1;
  278. for (idx = 1; idx < num_knots; idx++) {
  279. knot = knots[idx];
  280. if (ts_knots_equal(lst_knot, knot)) {
  281. mult++;
  282. } else if (lst_knot > knot) {
  283. TS_RETURN_1(status, TS_KNOTS_DECR,
  284. "decreasing knot vector at index: %lu",
  285. (unsigned long) idx)
  286. } else {
  287. mult = 0;
  288. }
  289. if (mult > order) {
  290. TS_RETURN_3(status, TS_MULTIPLICITY,
  291. "mult(%f) (%lu) > order (%lu)",
  292. knot, (unsigned long) mult,
  293. (unsigned long) order)
  294. }
  295. lst_knot = knot;
  296. }
  297. memmove(ts_int_bspline_access_knots(spline), knots, size);
  298. TS_RETURN_SUCCESS(status)
  299. }
  300. tsError
  301. ts_bspline_set_knots_varargs(tsBSpline *spline,
  302. tsStatus *status,
  303. tsReal knot0,
  304. double knot1,
  305. ...)
  306. {
  307. tsReal *values = NULL;
  308. va_list argp;
  309. size_t idx;
  310. tsError err;
  311. TS_TRY(try, err, status)
  312. TS_CALL(try, err, ts_bspline_knots(
  313. spline, &values, status))
  314. values[0] = knot0;
  315. values[1] = (tsReal) knot1;
  316. va_start(argp, knot1);
  317. for (idx = 2; idx < ts_bspline_num_knots(spline); idx++)
  318. values[idx] = (tsReal) va_arg(argp, double);
  319. va_end(argp);
  320. TS_CALL(try, err, ts_bspline_set_knots(
  321. spline, values, status))
  322. TS_FINALLY
  323. if (values) free(values);
  324. TS_END_TRY_RETURN(err)
  325. }
  326. tsError
  327. ts_bspline_set_knot_at(tsBSpline *spline,
  328. size_t index,
  329. tsReal knot,
  330. tsStatus *status)
  331. {
  332. tsError err;
  333. tsReal *knots = NULL;
  334. /* This is only for initialization. */
  335. tsReal oldKnot = ts_int_bspline_access_knots(spline)[0];
  336. TS_TRY(try, err, status)
  337. TS_CALL(try, err, ts_int_bspline_access_knot_at(
  338. spline, index, &oldKnot, status))
  339. /* knots must be set after reading oldKnot because the catch
  340. * block assumes that oldKnot contains the correct value if
  341. * knots is not NULL. */
  342. knots = ts_int_bspline_access_knots(spline);
  343. knots[index] = knot;
  344. TS_CALL(try, err, ts_bspline_set_knots(
  345. spline, knots, status))
  346. TS_CATCH(err)
  347. /* If knots is not NULL, oldKnot contains the correct value. */
  348. if (knots) knots[index] = oldKnot;
  349. TS_END_TRY_RETURN(err)
  350. }
  351. /*! @} */
  352. /*! @name B-Spline Initialization
  353. *
  354. * @{
  355. */
  356. tsBSpline
  357. ts_bspline_init(void)
  358. {
  359. tsBSpline spline;
  360. ts_int_bspline_init(&spline);
  361. return spline;
  362. }
  363. tsError
  364. ts_int_bspline_generate_knots(const tsBSpline *spline,
  365. tsBSplineType type,
  366. tsStatus *status)
  367. {
  368. const size_t n_knots = ts_bspline_num_knots(spline);
  369. const size_t deg = ts_bspline_degree(spline);
  370. const size_t order = ts_bspline_order(spline);
  371. tsReal fac; /**< Factor used to calculate the knot values. */
  372. size_t i; /**< Used in for loops. */
  373. tsReal *knots; /**< Pointer to the knots of \p _result_. */
  374. /* order >= 1 implies 2*order >= 2 implies n_knots >= 2 */
  375. if (type == TS_BEZIERS && n_knots % order != 0) {
  376. TS_RETURN_2(status, TS_NUM_KNOTS,
  377. "num(knots) (%lu) %% order (%lu) != 0",
  378. (unsigned long) n_knots, (unsigned long) order)
  379. }
  380. knots = ts_int_bspline_access_knots(spline);
  381. if (type == TS_OPENED) {
  382. knots[0] = TS_DOMAIN_DEFAULT_MIN; /* n_knots >= 2 */
  383. fac = (TS_DOMAIN_DEFAULT_MAX - TS_DOMAIN_DEFAULT_MIN)
  384. / (n_knots - 1); /* n_knots >= 2 */
  385. for (i = 1; i < n_knots-1; i++)
  386. knots[i] = TS_DOMAIN_DEFAULT_MIN + i*fac;
  387. knots[i] = TS_DOMAIN_DEFAULT_MAX; /* n_knots >= 2 */
  388. } else if (type == TS_CLAMPED) {
  389. /* n_knots >= 2*order == 2*(deg+1) == 2*deg + 2 > 2*deg - 1 */
  390. fac = (TS_DOMAIN_DEFAULT_MAX - TS_DOMAIN_DEFAULT_MIN)
  391. / (n_knots - 2*deg - 1);
  392. ts_arr_fill(knots, order, TS_DOMAIN_DEFAULT_MIN);
  393. for (i = order ;i < n_knots-order; i++)
  394. knots[i] = TS_DOMAIN_DEFAULT_MIN + (i-deg)*fac;
  395. ts_arr_fill(knots + i, order, TS_DOMAIN_DEFAULT_MAX);
  396. } else if (type == TS_BEZIERS) {
  397. /* n_knots >= 2*order implies n_knots/order >= 2 */
  398. fac = (TS_DOMAIN_DEFAULT_MAX - TS_DOMAIN_DEFAULT_MIN)
  399. / (n_knots/order - 1);
  400. ts_arr_fill(knots, order, TS_DOMAIN_DEFAULT_MIN);
  401. for (i = order; i < n_knots-order; i += order)
  402. ts_arr_fill(knots + i,
  403. order,
  404. TS_DOMAIN_DEFAULT_MIN + (i/order)*fac);
  405. ts_arr_fill(knots + i, order, TS_DOMAIN_DEFAULT_MAX);
  406. }
  407. TS_RETURN_SUCCESS(status)
  408. }
  409. tsError
  410. ts_bspline_new(size_t num_control_points,
  411. size_t dimension,
  412. size_t degree,
  413. tsBSplineType type,
  414. tsBSpline *spline,
  415. tsStatus *status)
  416. {
  417. const size_t order = degree + 1;
  418. const size_t num_knots = num_control_points + order;
  419. const size_t len_ctrlp = num_control_points * dimension;
  420. const size_t sof_real = sizeof(tsReal);
  421. const size_t sof_impl = sizeof(struct tsBSplineImpl);
  422. const size_t sof_ctrlp_vec = len_ctrlp * sof_real;
  423. const size_t sof_knots_vec = num_knots * sof_real;
  424. const size_t sof_spline = sof_impl + sof_ctrlp_vec + sof_knots_vec;
  425. tsError err;
  426. ts_int_bspline_init(spline);
  427. if (dimension < 1) {
  428. TS_RETURN_0(status, TS_DIM_ZERO, "unsupported dimension: 0")
  429. }
  430. if (num_knots > TS_MAX_NUM_KNOTS) {
  431. TS_RETURN_2(status, TS_NUM_KNOTS,
  432. "unsupported number of knots: %lu > %i",
  433. (unsigned long) num_knots, TS_MAX_NUM_KNOTS)
  434. }
  435. if (degree >= num_control_points) {
  436. TS_RETURN_2(status, TS_DEG_GE_NCTRLP,
  437. "degree (%lu) >= num(control_points) (%lu)",
  438. (unsigned long) degree,
  439. (unsigned long) num_control_points)
  440. }
  441. spline->pImpl = (struct tsBSplineImpl *) malloc(sof_spline);
  442. if (!spline->pImpl) TS_RETURN_0(status, TS_MALLOC, "out of memory")
  443. spline->pImpl->deg = degree;
  444. spline->pImpl->dim = dimension;
  445. spline->pImpl->n_ctrlp = num_control_points;
  446. spline->pImpl->n_knots = num_knots;
  447. TS_TRY(try, err, status)
  448. TS_CALL(try, err, ts_int_bspline_generate_knots(
  449. spline, type, status))
  450. TS_CATCH(err)
  451. ts_bspline_free(spline);
  452. TS_END_TRY_RETURN(err)
  453. }
  454. tsError
  455. ts_bspline_new_with_control_points(size_t num_control_points,
  456. size_t dimension,
  457. size_t degree,
  458. tsBSplineType type,
  459. tsBSpline *spline,
  460. tsStatus *status,
  461. double first,
  462. ...)
  463. {
  464. tsReal *ctrlp = NULL;
  465. va_list argp;
  466. size_t i;
  467. tsError err;
  468. TS_TRY(try, err, status)
  469. TS_CALL(try, err, ts_bspline_new(
  470. num_control_points, dimension,
  471. degree, type, spline, status))
  472. TS_CATCH(err)
  473. ts_bspline_free(spline);
  474. TS_END_TRY_ROE(err)
  475. ctrlp = ts_int_bspline_access_ctrlp(spline);
  476. ctrlp[0] = (tsReal) first;
  477. va_start(argp, first);
  478. for (i = 1; i < ts_bspline_len_control_points(spline); i++)
  479. ctrlp[i] = (tsReal) va_arg(argp, double);
  480. va_end(argp);
  481. TS_RETURN_SUCCESS(status)
  482. }
  483. tsError
  484. ts_bspline_copy(const tsBSpline *src,
  485. tsBSpline *dest,
  486. tsStatus *status)
  487. {
  488. size_t size;
  489. if (src == dest) TS_RETURN_SUCCESS(status)
  490. ts_int_bspline_init(dest);
  491. size = ts_int_bspline_sof_state(src);
  492. dest->pImpl = (struct tsBSplineImpl *) malloc(size);
  493. if (!dest->pImpl) TS_RETURN_0(status, TS_MALLOC, "out of memory")
  494. memcpy(dest->pImpl, src->pImpl, size);
  495. TS_RETURN_SUCCESS(status)
  496. }
  497. void
  498. ts_bspline_move(tsBSpline *src,
  499. tsBSpline *dest)
  500. {
  501. if (src == dest) return;
  502. dest->pImpl = src->pImpl;
  503. ts_int_bspline_init(src);
  504. }
  505. void
  506. ts_bspline_free(tsBSpline *spline)
  507. {
  508. if (spline->pImpl) free(spline->pImpl);
  509. ts_int_bspline_init(spline);
  510. }
  511. /*! @} */
  512. /*! @name De Boor Net Data
  513. *
  514. * @{
  515. */
  516. tsReal
  517. ts_deboornet_knot(const tsDeBoorNet *net)
  518. {
  519. return net->pImpl->u;
  520. }
  521. size_t
  522. ts_deboornet_index(const tsDeBoorNet *net)
  523. {
  524. return net->pImpl->k;
  525. }
  526. size_t
  527. ts_deboornet_multiplicity(const tsDeBoorNet *net)
  528. {
  529. return net->pImpl->s;
  530. }
  531. size_t
  532. ts_deboornet_num_insertions(const tsDeBoorNet *net)
  533. {
  534. return net->pImpl->h;
  535. }
  536. size_t
  537. ts_deboornet_dimension(const tsDeBoorNet *net)
  538. {
  539. return net->pImpl->dim;
  540. }
  541. size_t
  542. ts_deboornet_len_points(const tsDeBoorNet *net)
  543. {
  544. return ts_deboornet_num_points(net) *
  545. ts_deboornet_dimension(net);
  546. }
  547. size_t
  548. ts_deboornet_num_points(const tsDeBoorNet *net)
  549. {
  550. return net->pImpl->n_points;
  551. }
  552. size_t
  553. ts_deboornet_sof_points(const tsDeBoorNet *net)
  554. {
  555. return ts_deboornet_len_points(net) * sizeof(tsReal);
  556. }
  557. const tsReal *
  558. ts_deboornet_points_ptr(const tsDeBoorNet *net)
  559. {
  560. return ts_int_deboornet_access_points(net);
  561. }
  562. tsError
  563. ts_deboornet_points(const tsDeBoorNet *net,
  564. tsReal **points,
  565. tsStatus *status)
  566. {
  567. const size_t size = ts_deboornet_sof_points(net);
  568. *points = (tsReal*) malloc(size);
  569. if (!*points) TS_RETURN_0(status, TS_MALLOC, "out of memory")
  570. memcpy(*points, ts_int_deboornet_access_points(net), size);
  571. TS_RETURN_SUCCESS(status)
  572. }
  573. size_t
  574. ts_deboornet_len_result(const tsDeBoorNet *net)
  575. {
  576. return ts_deboornet_num_result(net) *
  577. ts_deboornet_dimension(net);
  578. }
  579. size_t
  580. ts_deboornet_num_result(const tsDeBoorNet *net)
  581. {
  582. return ts_deboornet_num_points(net) == 2 ? 2 : 1;
  583. }
  584. size_t
  585. ts_deboornet_sof_result(const tsDeBoorNet *net)
  586. {
  587. return ts_deboornet_len_result(net) * sizeof(tsReal);
  588. }
  589. const tsReal *
  590. ts_deboornet_result_ptr(const tsDeBoorNet *net)
  591. {
  592. return ts_int_deboornet_access_result(net);
  593. }
  594. tsError
  595. ts_deboornet_result(const tsDeBoorNet *net,
  596. tsReal **result,
  597. tsStatus *status)
  598. {
  599. const size_t size = ts_deboornet_sof_result(net);
  600. *result = (tsReal*) malloc(size);
  601. if (!*result) TS_RETURN_0(status, TS_MALLOC, "out of memory")
  602. memcpy(*result, ts_int_deboornet_access_result(net), size);
  603. TS_RETURN_SUCCESS(status)
  604. }
  605. /*! @} */
  606. /*! @name De Boor Net Initialization
  607. *
  608. * @{
  609. */
  610. tsDeBoorNet
  611. ts_deboornet_init(void)
  612. {
  613. tsDeBoorNet net;
  614. ts_int_deboornet_init(&net);
  615. return net;
  616. }
  617. tsError
  618. ts_int_deboornet_new(const tsBSpline *spline,
  619. tsDeBoorNet *net,
  620. tsStatus *status)
  621. {
  622. const size_t dim = ts_bspline_dimension(spline);
  623. const size_t deg = ts_bspline_degree(spline);
  624. const size_t order = ts_bspline_order(spline);
  625. const size_t num_points = (size_t)(order * (order+1) * 0.5f);
  626. /* Handle `order == 1' which generates too few points. */
  627. const size_t fixed_num_points = num_points < 2 ? 2 : num_points;
  628. const size_t sof_real = sizeof(tsReal);
  629. const size_t sof_impl = sizeof(struct tsDeBoorNetImpl);
  630. const size_t sof_points_vec = fixed_num_points * dim * sof_real;
  631. const size_t sof_net = sof_impl * sof_points_vec;
  632. net->pImpl = (struct tsDeBoorNetImpl *) malloc(sof_net);
  633. if (!net->pImpl) TS_RETURN_0(status, TS_MALLOC, "out of memory")
  634. net->pImpl->u = 0.f;
  635. net->pImpl->k = 0;
  636. net->pImpl->s = 0;
  637. net->pImpl->h = deg;
  638. net->pImpl->dim = dim;
  639. net->pImpl->n_points = fixed_num_points;
  640. TS_RETURN_SUCCESS(status)
  641. }
  642. void
  643. ts_deboornet_free(tsDeBoorNet *net)
  644. {
  645. if (net->pImpl) free(net->pImpl);
  646. ts_int_deboornet_init(net);
  647. }
  648. tsError
  649. ts_deboornet_copy(const tsDeBoorNet *src,
  650. tsDeBoorNet *dest,
  651. tsStatus *status)
  652. {
  653. size_t size;
  654. if (src == dest) TS_RETURN_SUCCESS(status)
  655. ts_int_deboornet_init(dest);
  656. size = ts_int_deboornet_sof_state(src);
  657. dest->pImpl = (struct tsDeBoorNetImpl *) malloc(size);
  658. if (!dest->pImpl) TS_RETURN_0(status, TS_MALLOC, "out of memory")
  659. memcpy(dest->pImpl, src->pImpl, size);
  660. TS_RETURN_SUCCESS(status)
  661. }
  662. void
  663. ts_deboornet_move(tsDeBoorNet *src,
  664. tsDeBoorNet *dest)
  665. {
  666. if (src == dest) return;
  667. dest->pImpl = src->pImpl;
  668. ts_int_deboornet_init(src);
  669. }
  670. /*! @} */
  671. /*! @name Interpolation and Approximation Functions
  672. *
  673. * @{
  674. */
  675. tsError
  676. ts_int_cubic_point(const tsReal *point,
  677. size_t dim,
  678. tsBSpline *spline,
  679. tsStatus *status)
  680. {
  681. const size_t size = dim * sizeof(tsReal);
  682. tsReal *ctrlp = NULL;
  683. size_t i;
  684. tsError err;
  685. TS_CALL_ROE(err, ts_bspline_new(
  686. 4, dim, 3,
  687. TS_CLAMPED, spline, status))
  688. ctrlp = ts_int_bspline_access_ctrlp(spline);
  689. for (i = 0; i < 4; i++) {
  690. memcpy(ctrlp + i*dim,
  691. point,
  692. size);
  693. }
  694. TS_RETURN_SUCCESS(status)
  695. }
  696. tsError
  697. ts_int_thomas_algorithm(const tsReal *a,
  698. const tsReal *b,
  699. const tsReal *c,
  700. size_t num,
  701. size_t dim,
  702. tsReal *d,
  703. tsStatus *status)
  704. {
  705. size_t i, j, k, l;
  706. tsReal m, *cc = NULL;
  707. tsError err;
  708. if (dim == 0) {
  709. TS_RETURN_0(status, TS_DIM_ZERO,
  710. "unsupported dimension: 0")
  711. }
  712. if (num <= 1) {
  713. TS_RETURN_1(status, TS_NUM_POINTS,
  714. "num(points) (%lu) <= 1",
  715. (unsigned long) num)
  716. }
  717. cc = (tsReal *) malloc(num * sizeof(tsReal));
  718. if (!cc) TS_RETURN_0(status, TS_MALLOC, "out of memory")
  719. TS_TRY(try, err, status)
  720. /* Forward sweep. */
  721. if (fabs(b[0]) <= fabs(c[0])) {
  722. TS_THROW_2(try, err, status, TS_NO_RESULT,
  723. "error: |%f| <= |%f|", b[0], c[0])
  724. }
  725. /* |b[i]| > |c[i]| implies that |b[i]| > 0. Thus, the following
  726. * statements cannot evaluate to division by zero.*/
  727. cc[0] = c[0] / b[0];
  728. for (i = 0; i < dim; i++)
  729. d[i] = d[i] / b[0];
  730. for (i = 1; i < num; i++) {
  731. if (fabs(b[i]) <= fabs(a[i]) + fabs(c[i])) {
  732. TS_THROW_3(try, err, status, TS_NO_RESULT,
  733. "error: |%f| <= |%f| + |%f|",
  734. b[i], a[i], c[i])
  735. }
  736. /* |a[i]| < |b[i]| and cc[i - 1] < 1. Therefore, the
  737. * following statement cannot evaluate to division by
  738. * zero. */
  739. m = 1.f / (b[i] - a[i] * cc[i - 1]);
  740. /* |b[i]| > |a[i]| + |c[i]| implies that there must be
  741. * an eps > 0 such that |b[i]| = |a[i]| + |c[i]| + eps.
  742. * Even if |a[i]| is 0 (by which the result of the
  743. * following statement becomes maximum), |c[i]| is less
  744. * than |b[i]| by an amount of eps. By substituting the
  745. * previous and the following statements (under the
  746. * assumption that |a[i]| is 0), we obtain c[i] / b[i],
  747. * which must be less than 1. */
  748. cc[i] = c[i] * m;
  749. for (j = 0; j < dim; j++) {
  750. k = i * dim + j;
  751. l = (i-1) * dim + j;
  752. d[k] = (d[k] - a[i] * d[l]) * m;
  753. }
  754. }
  755. /* Back substitution. */
  756. for (i = num-1; i > 0; i--) {
  757. for (j = 0; j < dim; j++) {
  758. k = (i-1) * dim + j;
  759. l = i * dim + j;
  760. d[k] -= cc[i-1] * d[l];
  761. }
  762. }
  763. TS_FINALLY
  764. free(cc);
  765. TS_END_TRY_RETURN(err)
  766. }
  767. tsError
  768. ts_int_relaxed_uniform_cubic_bspline(const tsReal *points,
  769. size_t n,
  770. size_t dim,
  771. tsBSpline *spline,
  772. tsStatus *status)
  773. {
  774. const size_t order = 4; /**< Order of spline to interpolate. */
  775. const tsReal as = 1.f/6.f; /**< The value 'a sixth'. */
  776. const tsReal at = 1.f/3.f; /**< The value 'a third'. */
  777. const tsReal tt = 2.f/3.f; /**< The value 'two third'. */
  778. size_t sof_ctrlp; /**< Size of a single control point. */
  779. const tsReal* b = points; /**< Array of the b values. */
  780. tsReal* s; /**< Array of the s values. */
  781. size_t i, d; /**< Used in for loops */
  782. size_t j, k, l; /**< Used as temporary indices. */
  783. tsReal *ctrlp; /**< Pointer to the control points of \p _spline_. */
  784. tsError err;
  785. /* input validation */
  786. if (dim == 0)
  787. TS_RETURN_0(status, TS_DIM_ZERO, "unsupported dimension: 0")
  788. if (n <= 1) {
  789. TS_RETURN_1(status, TS_NUM_POINTS,
  790. "num(points) (%lu) <= 1",
  791. (unsigned long) n)
  792. }
  793. /* in the following n >= 2 applies */
  794. sof_ctrlp = dim * sizeof(tsReal); /* dim > 0 implies sof_ctrlp > 0 */
  795. s = NULL;
  796. TS_TRY(try, err, status)
  797. /* n >= 2 implies n-1 >= 1 implies (n-1)*4 >= 4 */
  798. TS_CALL(try, err, ts_bspline_new(
  799. (n-1) * 4, dim, order - 1,
  800. TS_BEZIERS, spline, status))
  801. ctrlp = ts_int_bspline_access_ctrlp(spline);
  802. s = (tsReal*) malloc(n * sof_ctrlp);
  803. if (!s) {
  804. TS_THROW_0(try, err, status, TS_MALLOC,
  805. "out of memory")
  806. }
  807. /* set s_0 to b_0 and s_n = b_n */
  808. memcpy(s, b, sof_ctrlp);
  809. memcpy(s + (n-1)*dim, b + (n-1)*dim, sof_ctrlp);
  810. /* set s_i = 1/6*b_i + 2/3*b_{i-1} + 1/6*b_{i+1}*/
  811. for (i = 1; i < n-1; i++) {
  812. for (d = 0; d < dim; d++) {
  813. j = (i-1)*dim+d;
  814. k = i*dim+d;
  815. l = (i+1)*dim+d;
  816. s[k] = as * b[j];
  817. s[k] += tt * b[k];
  818. s[k] += as * b[l];
  819. }
  820. }
  821. /* create beziers from b and s */
  822. for (i = 0; i < n-1; i++) {
  823. for (d = 0; d < dim; d++) {
  824. j = i*dim+d;
  825. k = i*4*dim+d;
  826. l = (i+1)*dim+d;
  827. ctrlp[k] = s[j];
  828. ctrlp[k+dim] = tt*b[j] + at*b[l];
  829. ctrlp[k+2*dim] = at*b[j] + tt*b[l];
  830. ctrlp[k+3*dim] = s[l];
  831. }
  832. }
  833. TS_CATCH(err)
  834. ts_bspline_free(spline);
  835. TS_FINALLY
  836. if (s)
  837. free(s);
  838. TS_END_TRY_RETURN(err)
  839. }
  840. tsError
  841. ts_bspline_interpolate_cubic_natural(const tsReal *points,
  842. size_t num_points,
  843. size_t dimension,
  844. tsBSpline *spline,
  845. tsStatus *status)
  846. {
  847. const size_t sof_ctrlp = dimension * sizeof(tsReal);
  848. const size_t len_points = num_points * dimension;
  849. const size_t num_int_points = num_points - 2;
  850. const size_t len_int_points = num_int_points * dimension;
  851. tsReal *thomas, *a, *b, *c, *d;
  852. size_t i, j, k, l;
  853. tsError err;
  854. ts_int_bspline_init(spline);
  855. if (num_points == 0)
  856. TS_RETURN_0(status, TS_NUM_POINTS, "num(points) == 0")
  857. if (num_points == 1) {
  858. TS_CALL_ROE(err, ts_int_cubic_point(
  859. points, dimension, spline, status))
  860. TS_RETURN_SUCCESS(status)
  861. }
  862. if (num_points == 2) {
  863. return ts_int_relaxed_uniform_cubic_bspline(
  864. points, num_points, dimension, spline, status);
  865. }
  866. /* `num_points` >= 3 */
  867. thomas = NULL;
  868. TS_TRY(try, err, status)
  869. thomas = (tsReal *) malloc(
  870. /* `a', `b', `c' (note that `c' is equal to `a') */
  871. 2 * num_int_points * sizeof(tsReal) +
  872. /* `d' and "result of the thomas algorithm" (which
  873. contains `num_points' points) */
  874. num_points * dimension * sizeof(tsReal));
  875. if (!thomas) {
  876. TS_THROW_0(try, err, status, TS_MALLOC,
  877. "out of memory")
  878. }
  879. /* The system of linear equations is taken from:
  880. * http://www.bakoma-tex.com/doc/generic/pst-bspline/
  881. * pst-bspline-doc.pdf */
  882. a = c = thomas;
  883. ts_arr_fill(a, num_int_points, 1);
  884. b = a + num_int_points;
  885. ts_arr_fill(b, num_int_points, 4);
  886. d = b + num_int_points;
  887. /* 6 * S_{i+1} */
  888. for (i = 0; i < num_int_points; i++) {
  889. for (j = 0; j < dimension; j++) {
  890. k = i * dimension + j;
  891. l = (i+1) * dimension + j;
  892. d[k] = 6 * points[l];
  893. }
  894. }
  895. for (i = 0; i < dimension; i++) {
  896. /* 6 * S_{1} - S_{0} */
  897. d[i] -= points[i];
  898. /* 6 * S_{n-1} - S_{n} */
  899. k = len_int_points - (i+1);
  900. l = len_points - (i+1);
  901. d[k] -= points[l];
  902. }
  903. /* The Thomas algorithm requires at least two points. Hence,
  904. * `num_int_points` == 1 must be handled separately (let's call
  905. * it "Mini Thomas"). */
  906. if (num_int_points == 1) {
  907. for (i = 0; i < dimension; i++)
  908. d[i] *= (tsReal) 0.25f;
  909. } else {
  910. TS_CALL(try, err, ts_int_thomas_algorithm(
  911. a, b, c, num_int_points, dimension, d,
  912. status))
  913. }
  914. memcpy(thomas, points, sof_ctrlp);
  915. memmove(thomas + dimension, d, num_int_points * sof_ctrlp);
  916. memcpy(thomas + (num_int_points+1) * dimension,
  917. points + (num_points-1) * dimension, sof_ctrlp);
  918. TS_CALL(try, err, ts_int_relaxed_uniform_cubic_bspline(
  919. thomas, num_points, dimension, spline, status))
  920. TS_CATCH(err)
  921. ts_bspline_free(spline);
  922. TS_FINALLY
  923. if (thomas)
  924. free(thomas);
  925. TS_END_TRY_RETURN(err)
  926. }
  927. tsError
  928. ts_bspline_interpolate_catmull_rom(const tsReal *points,
  929. size_t num_points,
  930. size_t dimension,
  931. tsReal alpha,
  932. const tsReal *first,
  933. const tsReal *last,
  934. tsReal epsilon,
  935. tsBSpline *spline,
  936. tsStatus *status)
  937. {
  938. const size_t sof_real = sizeof(tsReal);
  939. const size_t sof_ctrlp = dimension * sof_real;
  940. const tsReal eps = (tsReal) fabs(epsilon);
  941. tsReal *bs_ctrlp; /* Points to the control points of `spline`. */
  942. tsReal *cr_ctrlp; /**< The points to interpolate based on `points`. */
  943. size_t i, d; /**< Used in for loops. */
  944. tsError err; /**< Local error handling. */
  945. /* [https://en.wikipedia.org/wiki/
  946. * Centripetal_Catmull%E2%80%93Rom_spline] */
  947. tsReal t0, t1, t2, t3; /**< Catmull-Rom knots. */
  948. /* [https://stackoverflow.com/questions/30748316/
  949. * catmull-rom-interpolation-on-svg-paths/30826434#30826434] */
  950. tsReal c1, c2, d1, d2, m1, m2; /**< Used to calculate derivatives. */
  951. tsReal *p0, *p1, *p2, *p3; /**< Processed Catmull-Rom points. */
  952. ts_int_bspline_init(spline);
  953. if (dimension == 0)
  954. TS_RETURN_0(status, TS_DIM_ZERO, "unsupported dimension: 0")
  955. if (num_points == 0)
  956. TS_RETURN_0(status, TS_NUM_POINTS, "num(points) == 0")
  957. if (alpha < (tsReal) 0.0) alpha = (tsReal) 0.0;
  958. if (alpha > (tsReal) 1.0) alpha = (tsReal) 1.0;
  959. /* Copy `points` to `cr_ctrlp`. Add space for `first` and `last`. */
  960. cr_ctrlp = (tsReal *) malloc((num_points + 2) * sof_ctrlp);
  961. if (!cr_ctrlp)
  962. TS_RETURN_0(status, TS_MALLOC, "out of memory")
  963. memcpy(cr_ctrlp + dimension, points, num_points * sof_ctrlp);
  964. /* Remove redundant points from `cr_ctrlp`. Update `num_points`. */
  965. for (i = 1 /* 0 (`first`) is not assigned yet */;
  966. i < num_points /* skip last point (inclusive end) */;
  967. i++) {
  968. p0 = cr_ctrlp + (i * dimension);
  969. p1 = p0 + dimension;
  970. if (ts_distance(p0, p1, dimension) <= eps) {
  971. /* Are there any other points (after the one that is
  972. * to be removed) that need to be shifted? */
  973. if (i < num_points - 1) {
  974. memmove(p1, p1 + dimension,
  975. (num_points - (i + 1)) * sof_ctrlp);
  976. }
  977. num_points--;
  978. i--;
  979. }
  980. }
  981. /* Check if there are still enough points for interpolation. */
  982. if (num_points == 1) { /* `num_points` can't be 0 */
  983. free(cr_ctrlp); /* The point is copied from `points`. */
  984. TS_CALL_ROE(err, ts_int_cubic_point(
  985. points, dimension, spline, status))
  986. TS_RETURN_SUCCESS(status)
  987. }
  988. /* Add or generate `first` and `last`. Update `num_points`. */
  989. p0 = cr_ctrlp + dimension;
  990. if (first && ts_distance(first, p0, dimension) > eps) {
  991. memcpy(cr_ctrlp, first, sof_ctrlp);
  992. } else {
  993. p1 = p0 + dimension;
  994. for (d = 0; d < dimension; d++)
  995. cr_ctrlp[d] = p0[d] + (p0[d] - p1[d]);
  996. }
  997. p1 = cr_ctrlp + (num_points * dimension);
  998. if (last && ts_distance(p1, last, dimension) > eps) {
  999. memcpy(cr_ctrlp + ((num_points + 1) * dimension),
  1000. last, sof_ctrlp);
  1001. } else {
  1002. p0 = p1 - dimension;
  1003. for (d = 0; d < dimension; d++) {
  1004. cr_ctrlp[((num_points + 1) * dimension) + d] =
  1005. p1[d] + (p1[d] - p0[d]);
  1006. }
  1007. }
  1008. num_points = num_points + 2;
  1009. /* Transform the sequence of Catmull-Rom splines. */
  1010. bs_ctrlp = NULL;
  1011. TS_TRY(try, err, status)
  1012. TS_CALL(try, err, ts_bspline_new(
  1013. (num_points - 3) * 4, dimension, 3,
  1014. TS_BEZIERS, spline, status))
  1015. bs_ctrlp = ts_int_bspline_access_ctrlp(spline);
  1016. TS_CATCH(err)
  1017. free(cr_ctrlp);
  1018. TS_END_TRY_ROE(err)
  1019. for (i = 0; i < ts_bspline_num_control_points(spline) / 4; i++) {
  1020. p0 = cr_ctrlp + ((i+0) * dimension);
  1021. p1 = cr_ctrlp + ((i+1) * dimension);
  1022. p2 = cr_ctrlp + ((i+2) * dimension);
  1023. p3 = cr_ctrlp + ((i+3) * dimension);
  1024. t0 = (tsReal) 0.f;
  1025. t1 = t0 + (tsReal) pow(ts_distance(p0, p1, dimension), alpha);
  1026. t2 = t1 + (tsReal) pow(ts_distance(p1, p2, dimension), alpha);
  1027. t3 = t2 + (tsReal) pow(ts_distance(p2, p3, dimension), alpha);
  1028. c1 = (t2-t1) / (t2-t0);
  1029. c2 = (t1-t0) / (t2-t0);
  1030. d1 = (t3-t2) / (t3-t1);
  1031. d2 = (t2-t1) / (t3-t1);
  1032. for (d = 0; d < dimension; d++) {
  1033. m1 = (t2-t1)*(c1*(p1[d]-p0[d])/(t1-t0)
  1034. + c2*(p2[d]-p1[d])/(t2-t1));
  1035. m2 = (t2-t1)*(d1*(p2[d]-p1[d])/(t2-t1)
  1036. + d2*(p3[d]-p2[d])/(t3-t2));
  1037. bs_ctrlp[((i*4 + 0) * dimension) + d] = p1[d];
  1038. bs_ctrlp[((i*4 + 1) * dimension) + d] = p1[d] + m1/3;
  1039. bs_ctrlp[((i*4 + 2) * dimension) + d] = p2[d] - m2/3;
  1040. bs_ctrlp[((i*4 + 3) * dimension) + d] = p2[d];
  1041. }
  1042. }
  1043. free(cr_ctrlp);
  1044. TS_RETURN_SUCCESS(status)
  1045. }
  1046. /*! @} */
  1047. /*! @name Query Functions
  1048. *
  1049. * @{
  1050. */
  1051. tsError
  1052. ts_int_bspline_find_knot(const tsBSpline *spline,
  1053. tsReal *knot, /* in: knot; out: actual knot */
  1054. size_t *idx, /* out: index of `knot' */
  1055. size_t *mult, /* out: multiplicity of `knot' */
  1056. tsStatus *status)
  1057. {
  1058. const size_t deg = ts_bspline_degree(spline);
  1059. const size_t num_knots = ts_bspline_num_knots(spline);
  1060. const tsReal *knots = ts_int_bspline_access_knots(spline);
  1061. tsReal min, max;
  1062. size_t low, high;
  1063. ts_bspline_domain(spline, &min, &max);
  1064. if (*knot < min) {
  1065. /* Avoid infinite loop (issue #222) */
  1066. if (ts_knots_equal(*knot, min)) *knot = min;
  1067. else {
  1068. TS_RETURN_2(status, TS_U_UNDEFINED,
  1069. "knot (%f) < min(domain) (%f)",
  1070. *knot, min)
  1071. }
  1072. }
  1073. else if (*knot > max && !ts_knots_equal(*knot, max)) {
  1074. TS_RETURN_2(status, TS_U_UNDEFINED,
  1075. "knot (%f) > max(domain) (%f)",
  1076. *knot, max)
  1077. }
  1078. /* Based on 'The NURBS Book' (Les Piegl and Wayne Tiller). */
  1079. if (ts_knots_equal(*knot, knots[num_knots - 1])) {
  1080. *idx = num_knots - 1;
  1081. } else {
  1082. low = 0;
  1083. high = num_knots - 1;
  1084. *idx = (low+high) / 2;
  1085. while (*knot < knots[*idx] || *knot >= knots[*idx + 1]) {
  1086. if (*knot < knots[*idx])
  1087. high = *idx;
  1088. else
  1089. low = *idx;
  1090. *idx = (low+high) / 2;
  1091. }
  1092. }
  1093. /* Handle floating point errors. */
  1094. while (*idx < num_knots - 1 && /* there is a next knot */
  1095. ts_knots_equal(*knot, knots[*idx + 1])) {
  1096. (*idx)++;
  1097. }
  1098. if (ts_knots_equal(*knot, knots[*idx]))
  1099. *knot = knots[*idx]; /* set actual knot */
  1100. /* Calculate knot's multiplicity. */
  1101. for (*mult = deg + 1; *mult > 0 ; (*mult)--) {
  1102. if (ts_knots_equal(*knot, knots[*idx - (*mult-1)]))
  1103. break;
  1104. }
  1105. TS_RETURN_SUCCESS(status)
  1106. }
  1107. tsError
  1108. ts_int_bspline_eval_woa(const tsBSpline *spline,
  1109. tsReal u,
  1110. tsDeBoorNet *net,
  1111. tsStatus *status)
  1112. {
  1113. const size_t deg = ts_bspline_degree(spline);
  1114. const size_t order = ts_bspline_order(spline);
  1115. const size_t dim = ts_bspline_dimension(spline);
  1116. const size_t num_knots = ts_bspline_num_knots(spline);
  1117. const size_t sof_ctrlp = dim * sizeof(tsReal);
  1118. const tsReal *ctrlp = ts_int_bspline_access_ctrlp(spline);
  1119. const tsReal *knots = ts_int_bspline_access_knots(spline);
  1120. tsReal *points = NULL; /**< Pointer to the points of \p net. */
  1121. size_t k; /**< Index of \p u. */
  1122. size_t s; /**< Multiplicity of \p u. */
  1123. size_t from; /**< Offset used to copy values. */
  1124. size_t fst; /**< First affected control point, inclusive. */
  1125. size_t lst; /**< Last affected control point, inclusive. */
  1126. size_t N; /**< Number of affected control points. */
  1127. /* The following indices are used to create the DeBoor net. */
  1128. size_t lidx; /**< Current left index. */
  1129. size_t ridx; /**< Current right index. */
  1130. size_t tidx; /**< Current to index. */
  1131. size_t r, i, d; /**< Used in for loop. */
  1132. tsReal ui; /**< Knot value at index i. */
  1133. tsReal a, a_hat; /**< Weighting factors of control points. */
  1134. tsError err;
  1135. points = ts_int_deboornet_access_points(net);
  1136. /* 1. Find index k such that u is in between [u_k, u_k+1).
  1137. * 2. Setup already known values.
  1138. * 3. Decide by multiplicity of u how to calculate point P(u). */
  1139. /* 1. */
  1140. k = s = 0;
  1141. TS_CALL_ROE(err, ts_int_bspline_find_knot(
  1142. spline, &u, &k, &s, status))
  1143. /* 2. */
  1144. net->pImpl->u = u;
  1145. net->pImpl->k = k;
  1146. net->pImpl->s = s;
  1147. net->pImpl->h = deg < s ? 0 : deg-s; /* prevent underflow */
  1148. /* 3. (by 1. s <= order)
  1149. *
  1150. * 3a) Check for s = order.
  1151. * Take the two points k-s and k-s + 1. If one of
  1152. * them doesn't exist, take only the other.
  1153. * 3b) Use de boor algorithm to find point P(u). */
  1154. if (s == order) {
  1155. /* only one of the two control points exists */
  1156. if (k == deg || /* only the first */
  1157. k == num_knots - 1) { /* only the last */
  1158. from = k == deg ? 0 : (k-s) * dim;
  1159. net->pImpl->n_points = 1;
  1160. memcpy(points, ctrlp + from, sof_ctrlp);
  1161. } else {
  1162. from = (k-s) * dim;
  1163. net->pImpl->n_points = 2;
  1164. memcpy(points, ctrlp + from, 2 * sof_ctrlp);
  1165. }
  1166. } else { /* by 3a) s <= deg (order = deg+1) */
  1167. fst = k-deg; /* by 1. k >= deg */
  1168. lst = k-s; /* s <= deg <= k */
  1169. N = lst-fst + 1; /* lst <= fst implies N >= 1 */
  1170. net->pImpl->n_points = (size_t)(N * (N+1) * 0.5f);
  1171. /* copy initial values to output */
  1172. memcpy(points, ctrlp + fst*dim, N * sof_ctrlp);
  1173. lidx = 0;
  1174. ridx = dim;
  1175. tidx = N*dim; /* N >= 1 implies tidx > 0 */
  1176. r = 1;
  1177. for (;r <= ts_deboornet_num_insertions(net); r++) {
  1178. i = fst + r;
  1179. for (; i <= lst; i++) {
  1180. ui = knots[i];
  1181. a = (ts_deboornet_knot(net) - ui) /
  1182. (knots[i+deg-r+1] - ui);
  1183. a_hat = 1.f-a;
  1184. for (d = 0; d < dim; d++) {
  1185. points[tidx++] =
  1186. a_hat * points[lidx++] +
  1187. a * points[ridx++];
  1188. }
  1189. }
  1190. lidx += dim;
  1191. ridx += dim;
  1192. }
  1193. }
  1194. TS_RETURN_SUCCESS(status)
  1195. }
  1196. tsError
  1197. ts_bspline_eval(const tsBSpline *spline,
  1198. tsReal knot,
  1199. tsDeBoorNet *net,
  1200. tsStatus *status)
  1201. {
  1202. tsError err;
  1203. ts_int_deboornet_init(net);
  1204. TS_TRY(try, err, status)
  1205. TS_CALL(try, err, ts_int_deboornet_new(
  1206. spline, net, status))
  1207. TS_CALL(try, err, ts_int_bspline_eval_woa(
  1208. spline, knot, net, status))
  1209. TS_CATCH(err)
  1210. ts_deboornet_free(net);
  1211. TS_END_TRY_RETURN(err)
  1212. }
  1213. tsError
  1214. ts_bspline_eval_all(const tsBSpline *spline,
  1215. const tsReal *knots,
  1216. size_t num,
  1217. tsReal **points,
  1218. tsStatus *status)
  1219. {
  1220. const size_t dim = ts_bspline_dimension(spline);
  1221. const size_t sof_point = dim * sizeof(tsReal);
  1222. const size_t sof_points = num * sof_point;
  1223. tsDeBoorNet net = ts_deboornet_init();
  1224. tsReal *result;
  1225. size_t i;
  1226. tsError err;
  1227. TS_TRY(try, err, status)
  1228. *points = (tsReal *) malloc(sof_points);
  1229. if (!*points) {
  1230. TS_THROW_0(try, err, status, TS_MALLOC,
  1231. "out of memory")
  1232. }
  1233. TS_CALL(try, err, ts_int_deboornet_new(
  1234. spline,&net, status))
  1235. for (i = 0; i < num; i++) {
  1236. TS_CALL(try, err, ts_int_bspline_eval_woa(
  1237. spline, knots[i], &net, status))
  1238. result = ts_int_deboornet_access_result(&net);
  1239. memcpy((*points) + i * dim, result, sof_point);
  1240. }
  1241. TS_CATCH(err)
  1242. if (*points)
  1243. free(*points);
  1244. *points = NULL;
  1245. TS_FINALLY
  1246. ts_deboornet_free(&net);
  1247. TS_END_TRY_RETURN(err)
  1248. }
  1249. tsError
  1250. ts_bspline_sample(const tsBSpline *spline,
  1251. size_t num,
  1252. tsReal **points,
  1253. size_t *actual_num,
  1254. tsStatus *status)
  1255. {
  1256. tsError err;
  1257. tsReal *knots;
  1258. num = num == 0 ? 100 : num;
  1259. *actual_num = num;
  1260. knots = (tsReal *) malloc(num * sizeof(tsReal));
  1261. if (!knots) {
  1262. *points = NULL;
  1263. TS_RETURN_0(status, TS_MALLOC, "out of memory")
  1264. }
  1265. ts_bspline_uniform_knot_seq(spline, num, knots);
  1266. TS_TRY(try, err, status)
  1267. TS_CALL(try, err, ts_bspline_eval_all(
  1268. spline, knots, num, points, status))
  1269. TS_FINALLY
  1270. free(knots);
  1271. TS_END_TRY_RETURN(err)
  1272. }
  1273. tsError
  1274. ts_bspline_bisect(const tsBSpline *spline,
  1275. tsReal value,
  1276. tsReal epsilon,
  1277. int persnickety,
  1278. size_t index,
  1279. int ascending,
  1280. size_t max_iter,
  1281. tsDeBoorNet *net,
  1282. tsStatus *status)
  1283. {
  1284. tsError err;
  1285. const size_t dim = ts_bspline_dimension(spline);
  1286. const tsReal eps = (tsReal) fabs(epsilon);
  1287. size_t i = 0;
  1288. tsReal dist = 0;
  1289. tsReal min, max, mid;
  1290. tsReal *P;
  1291. ts_int_deboornet_init(net);
  1292. if (dim < index) {
  1293. TS_RETURN_2(status, TS_INDEX_ERROR,
  1294. "dimension (%lu) <= index (%lu)",
  1295. (unsigned long) dim,
  1296. (unsigned long) index)
  1297. }
  1298. if(max_iter == 0)
  1299. TS_RETURN_0(status, TS_NO_RESULT, "0 iterations")
  1300. ts_bspline_domain(spline, &min, &max);
  1301. TS_TRY(try, err, status)
  1302. TS_CALL(try, err, ts_int_deboornet_new(
  1303. spline, net, status))
  1304. do {
  1305. mid = (tsReal) ((min + max) / 2.0);
  1306. TS_CALL(try, err, ts_int_bspline_eval_woa(
  1307. spline, mid, net, status))
  1308. P = ts_int_deboornet_access_result(net);
  1309. dist = ts_distance(&P[index], &value, 1);
  1310. if (dist <= eps)
  1311. TS_RETURN_SUCCESS(status)
  1312. if (ascending) {
  1313. if (P[index] < value)
  1314. min = mid;
  1315. else
  1316. max = mid;
  1317. } else {
  1318. if (P[index] < value)
  1319. max = mid;
  1320. else
  1321. min = mid;
  1322. }
  1323. } while (i++ < max_iter);
  1324. if (persnickety) {
  1325. TS_THROW_1(try, err, status, TS_NO_RESULT,
  1326. "maximum iterations (%lu) exceeded",
  1327. (unsigned long) max_iter)
  1328. }
  1329. TS_CATCH(err)
  1330. ts_deboornet_free(net);
  1331. TS_END_TRY_RETURN(err)
  1332. }
  1333. void ts_bspline_domain(const tsBSpline *spline,
  1334. tsReal *min,
  1335. tsReal *max)
  1336. {
  1337. *min = ts_int_bspline_access_knots(spline)
  1338. [ts_bspline_degree(spline)];
  1339. *max = ts_int_bspline_access_knots(spline)
  1340. [ts_bspline_num_knots(spline) - ts_bspline_order(spline)];
  1341. }
  1342. tsError
  1343. ts_bspline_is_closed(const tsBSpline *spline,
  1344. tsReal epsilon,
  1345. int *closed,
  1346. tsStatus *status)
  1347. {
  1348. const size_t deg = ts_bspline_degree(spline);
  1349. const size_t dim = ts_bspline_dimension(spline);
  1350. tsBSpline derivative;
  1351. tsReal min, max;
  1352. tsDeBoorNet first, last;
  1353. size_t i;
  1354. tsError err;
  1355. ts_int_bspline_init(&derivative);
  1356. ts_int_deboornet_init(&first);
  1357. ts_int_deboornet_init(&last);
  1358. TS_TRY(try, err, status)
  1359. for (i = 0; i < deg; i++) {
  1360. TS_CALL(try, err, ts_bspline_derive(
  1361. spline, i, -1.f, &derivative, status))
  1362. ts_bspline_domain(&derivative, &min, &max);
  1363. TS_CALL(try, err, ts_bspline_eval(
  1364. &derivative, min, &first, status))
  1365. TS_CALL(try, err, ts_bspline_eval(
  1366. &derivative, max, &last, status))
  1367. *closed = ts_distance(
  1368. ts_int_deboornet_access_result(&first),
  1369. ts_int_deboornet_access_result(&last),
  1370. dim) <= epsilon ? 1 : 0;
  1371. ts_bspline_free(&derivative);
  1372. ts_deboornet_free(&first);
  1373. ts_deboornet_free(&last);
  1374. if (!*closed)
  1375. TS_RETURN_SUCCESS(status)
  1376. }
  1377. TS_FINALLY
  1378. ts_bspline_free(&derivative);
  1379. ts_deboornet_free(&first);
  1380. ts_deboornet_free(&last);
  1381. TS_END_TRY_RETURN(err)
  1382. }
  1383. tsError
  1384. ts_bspline_compute_rmf(const tsBSpline *spline,
  1385. const tsReal *knots,
  1386. size_t num,
  1387. int has_first_normal,
  1388. tsFrame *frames,
  1389. tsStatus *status)
  1390. {
  1391. tsError err;
  1392. size_t i;
  1393. tsReal fx, fy, fz, fmin;
  1394. tsReal xc[3], xn[3], v1[3], c1, v2[3], c2, rL[3], tL[3];
  1395. tsBSpline deriv = ts_bspline_init();
  1396. tsDeBoorNet curr = ts_deboornet_init();
  1397. tsDeBoorNet next = ts_deboornet_init();
  1398. if (num < 1)
  1399. TS_RETURN_SUCCESS(status);
  1400. TS_TRY(try, err, status)
  1401. TS_CALL(try, err, ts_int_deboornet_new(
  1402. spline, &curr, status))
  1403. TS_CALL(try, err, ts_int_deboornet_new(
  1404. spline, &next, status))
  1405. TS_CALL(try, err, ts_bspline_derive(
  1406. spline, 1, (tsReal) -1.0, &deriv, status))
  1407. /* Set position. */
  1408. TS_CALL(try, err, ts_int_bspline_eval_woa(
  1409. spline, knots[0], &curr, status))
  1410. ts_vec3_set(frames[0].position,
  1411. ts_int_deboornet_access_result(&curr),
  1412. ts_bspline_dimension(spline));
  1413. /* Set tangent. */
  1414. TS_CALL(try, err, ts_int_bspline_eval_woa(
  1415. &deriv, knots[0], &curr, status))
  1416. ts_vec3_set(frames[0].tangent,
  1417. ts_int_deboornet_access_result(&curr),
  1418. ts_bspline_dimension(&deriv));
  1419. ts_vec_norm(frames[0].tangent, 3, frames[0].tangent);
  1420. /* Set normal. */
  1421. if (!has_first_normal) {
  1422. fx = (tsReal) fabs(frames[0].tangent[0]);
  1423. fy = (tsReal) fabs(frames[0].tangent[1]);
  1424. fz = (tsReal) fabs(frames[0].tangent[2]);
  1425. fmin = fx; /* x is min => 1, 0, 0 */
  1426. ts_vec3_init(frames[0].normal,
  1427. (tsReal) 1.0,
  1428. (tsReal) 0.0,
  1429. (tsReal) 0.0);
  1430. if (fy < fmin) { /* y is min => 0, 1, 0 */
  1431. fmin = fy;
  1432. ts_vec3_init(frames[0].normal,
  1433. (tsReal) 0.0,
  1434. (tsReal) 1.0,
  1435. (tsReal) 0.0);
  1436. }
  1437. if (fz < fmin) { /* z is min => 0, 0, 1 */
  1438. ts_vec3_init(frames[0].normal,
  1439. (tsReal) 0.0,
  1440. (tsReal) 0.0,
  1441. (tsReal) 1.0);
  1442. }
  1443. ts_vec3_cross(frames[0].tangent,
  1444. frames[0].normal,
  1445. frames[0].normal);
  1446. ts_vec_norm(frames[0].normal, 3, frames[0].normal);
  1447. if (ts_bspline_dimension(spline) >= 3) {
  1448. /* In 3D (and higher) an additional rotation of
  1449. the normal along the tangent is needed in
  1450. order to let the normal extend sideways (as
  1451. it does in 2D and lower). */
  1452. ts_vec3_cross(frames[0].tangent,
  1453. frames[0].normal,
  1454. frames[0].normal);
  1455. }
  1456. } else {
  1457. /* Never trust user input! */
  1458. ts_vec_norm(frames[0].normal, 3, frames[0].normal);
  1459. }
  1460. /* Set binormal. */
  1461. ts_vec3_cross(frames[0].tangent,
  1462. frames[0].normal,
  1463. frames[0].binormal);
  1464. for (i = 0; i < num - 1; i++) {
  1465. /* Eval current and next point. */
  1466. TS_CALL(try, err, ts_int_bspline_eval_woa(
  1467. spline, knots[i], &curr, status))
  1468. TS_CALL(try, err, ts_int_bspline_eval_woa(
  1469. spline, knots[i+1], &next, status))
  1470. ts_vec3_set(xc, /* xc is now the current point */
  1471. ts_int_deboornet_access_result(&curr),
  1472. ts_bspline_dimension(spline));
  1473. ts_vec3_set(xn, /* xn is now the next point */
  1474. ts_int_deboornet_access_result(&next),
  1475. ts_bspline_dimension(spline));
  1476. /* Set position of U_{i+1}. */
  1477. ts_vec3_set(frames[i+1].position, xn, 3);
  1478. /* Compute reflection vector of R_{1}. */
  1479. ts_vec_sub(xn, xc, 3, v1);
  1480. c1 = ts_vec_dot(v1, v1, 3);
  1481. /* Compute r_{i}^{L} = R_{1} * r_{i}. */
  1482. rL[0] = (tsReal) 2.0 / c1;
  1483. rL[1] = ts_vec_dot(v1, frames[i].normal, 3);
  1484. rL[2] = rL[0] * rL[1];
  1485. ts_vec_mul(v1, 3, rL[2], rL);
  1486. ts_vec_sub(frames[i].normal, rL, 3, rL);
  1487. /* Compute t_{i}^{L} = R_{1} * t_{i}. */
  1488. tL[0] = (tsReal) 2.0 / c1;
  1489. tL[1] = ts_vec_dot(v1, frames[i].tangent, 3);
  1490. tL[2] = tL[0] * tL[1];
  1491. ts_vec_mul(v1, 3, tL[2], tL);
  1492. ts_vec_sub(frames[i].tangent, tL, 3, tL);
  1493. /* Compute reflection vector of R_{2}. */
  1494. TS_CALL(try, err, ts_int_bspline_eval_woa(
  1495. &deriv, knots[i+1], &next, status))
  1496. ts_vec3_set(xn, /* xn is now the next tangent */
  1497. ts_int_deboornet_access_result(&next),
  1498. ts_bspline_dimension(&deriv));
  1499. ts_vec_norm(xn, 3, xn);
  1500. ts_vec_sub(xn, tL, 3, v2);
  1501. c2 = ts_vec_dot(v2, v2, 3);
  1502. /* Compute r_{i+1} = R_{2} * r_{i}^{L}. */
  1503. ts_vec3_set(xc, /* xc is now the next normal */
  1504. frames[i+1].normal, 3);
  1505. xc[0] = (tsReal) 2.0 / c2;
  1506. xc[1] = ts_vec_dot(v2, rL, 3);
  1507. xc[2] = xc[0] * xc[1];
  1508. ts_vec_mul(v2, 3, xc[2], xc);
  1509. ts_vec_sub(rL, xc, 3, xc);
  1510. ts_vec_norm(xc, 3, xc);
  1511. /* Compute vector s_{i+1} of U_{i+1}. */
  1512. ts_vec3_cross(xn, xc, frames[i+1].binormal);
  1513. /* Set vectors t_{i+1} and r_{i+1} of U_{i+1}. */
  1514. ts_vec3_set(frames[i+1].tangent, xn, 3);
  1515. ts_vec3_set(frames[i+1].normal, xc, 3);
  1516. }
  1517. TS_FINALLY
  1518. ts_bspline_free(&deriv);
  1519. ts_deboornet_free(&curr);
  1520. ts_deboornet_free(&next);
  1521. TS_END_TRY_RETURN(err)
  1522. }
  1523. tsError
  1524. ts_bspline_chord_lengths(const tsBSpline *spline,
  1525. const tsReal *knots,
  1526. size_t num,
  1527. tsReal *lengths,
  1528. tsStatus *status)
  1529. {
  1530. tsError err;
  1531. tsReal dist, lst_knot, cur_knot;
  1532. size_t i, dim = ts_bspline_dimension(spline);
  1533. tsDeBoorNet lst = ts_deboornet_init();
  1534. tsDeBoorNet cur = ts_deboornet_init();
  1535. tsDeBoorNet tmp = ts_deboornet_init();
  1536. if (num == 0) TS_RETURN_SUCCESS(status);
  1537. TS_TRY(try, err, status)
  1538. TS_CALL(try, err, ts_int_deboornet_new(
  1539. spline, &lst, status))
  1540. TS_CALL(try, err, ts_int_deboornet_new(
  1541. spline, &cur, status))
  1542. /* num >= 1 */
  1543. TS_CALL(try, err, ts_int_bspline_eval_woa(
  1544. spline, knots[0], &lst, status));
  1545. lengths[0] = (tsReal) 0.0;
  1546. for (i = 1; i < num; i++) {
  1547. TS_CALL(try, err, ts_int_bspline_eval_woa(
  1548. spline, knots[i], &cur, status));
  1549. lst_knot = ts_deboornet_knot(&lst);
  1550. cur_knot = ts_deboornet_knot(&cur);
  1551. if (cur_knot < lst_knot) {
  1552. TS_THROW_1(try, err, status, TS_KNOTS_DECR,
  1553. "decreasing knot at index: %lu",
  1554. (unsigned long) i)
  1555. }
  1556. dist = ts_distance(ts_deboornet_result_ptr(&lst),
  1557. ts_deboornet_result_ptr(&cur),
  1558. dim);
  1559. lengths[i] = lengths[i-1] + dist;
  1560. ts_deboornet_move(&lst, &tmp);
  1561. ts_deboornet_move(&cur, &lst);
  1562. ts_deboornet_move(&tmp, &cur);
  1563. }
  1564. TS_FINALLY
  1565. ts_deboornet_free(&lst);
  1566. ts_deboornet_free(&cur);
  1567. TS_END_TRY_RETURN(err)
  1568. }
  1569. tsError
  1570. ts_bspline_sub_spline(const tsBSpline *spline,
  1571. tsReal knot0,
  1572. tsReal knot1,
  1573. tsBSpline *sub,
  1574. tsStatus *status)
  1575. {
  1576. int reverse; /* reverse `spline`? (if `knot0 > knot1`) */
  1577. tsReal *tmp = NULL; /* a buffer to swap control points */
  1578. tsReal min, max; /* domain of `spline` */
  1579. size_t dim, deg, order; /* properties of `spline` (and `sub`) */
  1580. tsBSpline worker; /* stores the result of the `split' operations */
  1581. tsReal *ctrlp, *knots; /* control points and knots of `worker` */
  1582. size_t k0, k1; /* indices returned by the `split' operations */
  1583. size_t c0, c1; /* indices of the control points to be moved */
  1584. size_t nc, nk; /* number of control points and knots of `sub` */
  1585. size_t i; /* for various needs */
  1586. tsError err; /* for local try-catch block */
  1587. /* Make sure that `worker` points to `NULL'. This allows us to call
  1588. * `ts_bspline_free` in `TS_CATCH` without further checks. Also, `NULL'
  1589. * serves as an indicator of whether `ts_bspline_split` has been called
  1590. * on `spline` at least once (if not, `worker` needs to be initialized
  1591. * manually). */
  1592. ts_int_bspline_init(&worker);
  1593. INIT_OUT_BSPLINE(spline, sub)
  1594. ts_bspline_domain(spline, &min, &max);
  1595. dim = ts_bspline_dimension(spline);
  1596. deg = ts_bspline_degree(spline);
  1597. order = ts_bspline_order(spline);
  1598. /* Cannot create valid knot vector from empty domain. */
  1599. if (ts_knots_equal(knot0, knot1)) {
  1600. TS_RETURN_0(status,
  1601. TS_NO_RESULT,
  1602. "empty domain")
  1603. }
  1604. /* Check for `reverse mode'. Reverse mode means that the copied sequence
  1605. * of (sub) control points need to be reversed, forming a `backwards'
  1606. * spline. */
  1607. reverse = knot0 > knot1;
  1608. if (reverse) { /* swap `knot0` and `knot1` */
  1609. tmp = (tsReal *) malloc(dim * sizeof(tsReal));
  1610. if (!tmp) TS_RETURN_0(status, TS_MALLOC, "out of memory");
  1611. *tmp = knot0; /* `tmp` can hold at least one value */
  1612. knot0 = knot1;
  1613. knot1 = *tmp;
  1614. }
  1615. TS_TRY(try, err, status)
  1616. if (!ts_knots_equal(knot0 , min)) {
  1617. TS_CALL(try , err, ts_bspline_split(
  1618. spline, knot0, &worker, &k0, status))
  1619. } else { k0 = deg; }
  1620. if (!ts_knots_equal(knot1, max)) {
  1621. TS_CALL(try , err, ts_bspline_split(
  1622. /* If `NULL', the split operation
  1623. above was not called. */
  1624. !worker.pImpl ? spline : &worker,
  1625. knot1, &worker, &k1, status))
  1626. } else {
  1627. k1 = ts_bspline_num_knots(
  1628. /* If `NULL', the split operation
  1629. above was not called. */
  1630. !worker.pImpl ? spline : &worker) - 1;
  1631. }
  1632. /* Set up `worker`. */
  1633. if (!worker.pImpl) { /* => no split applied */
  1634. TS_CALL(try, err, ts_bspline_copy(
  1635. spline, &worker, status))
  1636. /* Needed in `reverse mode'. */
  1637. ctrlp = ts_int_bspline_access_ctrlp(&worker);
  1638. knots = ts_int_bspline_access_knots(&worker);
  1639. nc = ts_bspline_num_control_points(&worker);
  1640. } else {
  1641. c0 = (k0-deg) * dim;
  1642. c1 = (k1-order) * dim;
  1643. nc = ((c1-c0) / dim) + 1;
  1644. nk = (k1-k0) + order;
  1645. /* Also needed in `reverse mode'. */
  1646. ctrlp = ts_int_bspline_access_ctrlp(&worker);
  1647. knots = ts_int_bspline_access_knots(&worker);
  1648. /* Move control points. */
  1649. memmove(ctrlp,
  1650. ctrlp + c0,
  1651. nc * dim * sizeof(tsReal));
  1652. /* Move knots. */
  1653. memmove(ctrlp + nc * dim,
  1654. knots + (k0-deg),
  1655. nk * sizeof(tsReal));
  1656. /* Remove superfluous control points and knots from
  1657. * the memory of `worker`. */
  1658. worker.pImpl->n_knots = nk;
  1659. worker.pImpl->n_ctrlp = nc;
  1660. i = ts_int_bspline_sof_state(&worker);
  1661. worker.pImpl = realloc(worker.pImpl, i);
  1662. if (worker.pImpl == NULL) { /* unlikely to fail */
  1663. TS_THROW_0(try, err, status, TS_MALLOC,
  1664. "out of memory")
  1665. }
  1666. }
  1667. /* Reverse control points (if necessary). */
  1668. if (reverse) {
  1669. for (i = 0; i < nc / 2; i++) {
  1670. memcpy(tmp,
  1671. ctrlp + i * dim,
  1672. dim * sizeof(tsReal));
  1673. memmove(ctrlp + i * dim,
  1674. ctrlp + (nc-1 - i) * dim,
  1675. dim * sizeof(tsReal));
  1676. memcpy(ctrlp + (nc-1 - i) * dim,
  1677. tmp,
  1678. dim * sizeof(tsReal));
  1679. }
  1680. }
  1681. /* Move `worker' to output parameter. */
  1682. if (spline == sub) ts_bspline_free(sub);
  1683. ts_bspline_move(&worker, sub);
  1684. TS_CATCH(err)
  1685. ts_bspline_free(&worker);
  1686. TS_FINALLY
  1687. if (tmp) free(tmp);
  1688. TS_END_TRY_RETURN(err)
  1689. }
  1690. void
  1691. ts_bspline_uniform_knot_seq(const tsBSpline *spline,
  1692. size_t num,
  1693. tsReal *knots)
  1694. {
  1695. size_t i;
  1696. tsReal min, max;
  1697. if (num == 0) return;
  1698. ts_bspline_domain(spline, &min, &max);
  1699. for (i = 0; i < num; i++) {
  1700. knots[i] = max - min;
  1701. knots[i] *= (tsReal) i / (num - 1);
  1702. knots[i] += min;
  1703. }
  1704. /* Set `knots[0]` after `knots[num - 1]` to ensure
  1705. that `knots[0] = min` if `num` is `1'. */
  1706. knots[num - 1] = max;
  1707. knots[0] = min;
  1708. }
  1709. tsError
  1710. ts_bspline_equidistant_knot_seq(const tsBSpline *spline,
  1711. size_t num,
  1712. tsReal *knots,
  1713. size_t num_samples,
  1714. tsStatus *status)
  1715. {
  1716. tsError err;
  1717. tsReal *samples = NULL, *lengths = NULL;
  1718. if (num == 0) TS_RETURN_SUCCESS(status);
  1719. if (num_samples == 0) num_samples = 200;
  1720. samples = (tsReal *) malloc(2 * num_samples * sizeof(tsReal));
  1721. if (!samples) TS_RETURN_0(status, TS_MALLOC, "out of memory");
  1722. ts_bspline_uniform_knot_seq(spline, num_samples, samples);
  1723. lengths = samples + num_samples;
  1724. TS_TRY(try, err, status)
  1725. TS_CALL(try, err, ts_bspline_chord_lengths(
  1726. spline, samples, num_samples, lengths, status))
  1727. TS_CALL(try, err, ts_chord_lengths_equidistant_knot_seq(
  1728. samples, lengths, num_samples, num, knots, status))
  1729. TS_FINALLY
  1730. free(samples); /* cannot be NULL */
  1731. /* free(lengths); NO! */
  1732. TS_END_TRY_RETURN(err)
  1733. }
  1734. /*! @} */
  1735. /*! @name Transformation Functions
  1736. *
  1737. * @{
  1738. */
  1739. tsError
  1740. ts_int_bspline_resize(const tsBSpline *spline,
  1741. int n,
  1742. int back,
  1743. tsBSpline *resized,
  1744. tsStatus *status)
  1745. {
  1746. const size_t deg = ts_bspline_degree(spline);
  1747. const size_t dim = ts_bspline_dimension(spline);
  1748. const size_t sof_real = sizeof(tsReal);
  1749. const size_t num_ctrlp = ts_bspline_num_control_points(spline);
  1750. const size_t num_knots = ts_bspline_num_knots(spline);
  1751. const size_t nnum_ctrlp = num_ctrlp + n; /**< New length of ctrlp. */
  1752. const size_t nnum_knots = num_knots + n; /**< New length of knots. */
  1753. const size_t min_num_ctrlp_vec = n < 0 ? nnum_ctrlp : num_ctrlp;
  1754. const size_t min_num_knots_vec = n < 0 ? nnum_knots : num_knots;
  1755. const size_t sof_min_num_ctrlp = min_num_ctrlp_vec * dim * sof_real;
  1756. const size_t sof_min_num_knots = min_num_knots_vec * sof_real;
  1757. tsBSpline tmp; /**< Temporarily stores the result. */
  1758. const tsReal* from_ctrlp = ts_int_bspline_access_ctrlp(spline);
  1759. const tsReal* from_knots = ts_int_bspline_access_knots(spline);
  1760. tsReal* to_ctrlp = NULL; /**< Pointer to the control points of tmp. */
  1761. tsReal* to_knots = NULL; /**< Pointer to the knots of tmp. */
  1762. tsError err;
  1763. if (n == 0) return ts_bspline_copy(spline, resized, status);
  1764. INIT_OUT_BSPLINE(spline, resized)
  1765. TS_CALL_ROE(err, ts_bspline_new(
  1766. nnum_ctrlp, dim, deg, TS_OPENED,
  1767. &tmp, status))
  1768. to_ctrlp = ts_int_bspline_access_ctrlp(&tmp);
  1769. to_knots = ts_int_bspline_access_knots(&tmp);
  1770. /* Copy control points and knots. */
  1771. if (!back && n < 0) {
  1772. memcpy(to_ctrlp, from_ctrlp - n*dim, sof_min_num_ctrlp);
  1773. memcpy(to_knots, from_knots - n , sof_min_num_knots);
  1774. } else if (!back && n > 0) {
  1775. memcpy(to_ctrlp + n*dim, from_ctrlp, sof_min_num_ctrlp);
  1776. memcpy(to_knots + n , from_knots, sof_min_num_knots);
  1777. } else {
  1778. /* n != 0 implies back == true */
  1779. memcpy(to_ctrlp, from_ctrlp, sof_min_num_ctrlp);
  1780. memcpy(to_knots, from_knots, sof_min_num_knots);
  1781. }
  1782. if (spline == resized)
  1783. ts_bspline_free(resized);
  1784. ts_bspline_move(&tmp, resized);
  1785. TS_RETURN_SUCCESS(status)
  1786. }
  1787. tsError
  1788. ts_bspline_derive(const tsBSpline *spline,
  1789. size_t n,
  1790. tsReal epsilon,
  1791. tsBSpline *deriv,
  1792. tsStatus *status)
  1793. {
  1794. const size_t sof_real = sizeof(tsReal);
  1795. const size_t dim = ts_bspline_dimension(spline);
  1796. const size_t sof_ctrlp = dim * sof_real;
  1797. size_t deg = ts_bspline_degree(spline);
  1798. size_t num_ctrlp = ts_bspline_num_control_points(spline);
  1799. size_t num_knots = ts_bspline_num_knots(spline);
  1800. tsBSpline worker; /**< Stores the intermediate result. */
  1801. tsReal* ctrlp; /**< Pointer to the control points of worker. */
  1802. tsReal* knots; /**< Pointer to the knots of worker. */
  1803. size_t m, i, j, k, l; /**< Used in for loops. */
  1804. tsReal *fst, *snd; /**< Pointer to first and second control point. */
  1805. tsReal dist; /**< Distance between fst and snd. */
  1806. tsReal kid1, ki1; /**< Knots at i+deg+1 and i+1. */
  1807. tsReal span; /**< Distance between kid1 and ki1. */
  1808. tsBSpline swap; /**< Used to swap worker and derivative. */
  1809. tsError err;
  1810. INIT_OUT_BSPLINE(spline, deriv)
  1811. TS_CALL_ROE(err, ts_bspline_copy(spline, &worker, status))
  1812. ctrlp = ts_int_bspline_access_ctrlp(&worker);
  1813. knots = ts_int_bspline_access_knots(&worker);
  1814. TS_TRY(try, err, status)
  1815. for (m = 1; m <= n; m++) { /* from 1st to n'th derivative */
  1816. if (deg == 0) {
  1817. ts_arr_fill(ctrlp, dim, 0.f);
  1818. ts_bspline_domain(spline,
  1819. &knots[0],
  1820. &knots[1]);
  1821. num_ctrlp = 1;
  1822. num_knots = 2;
  1823. break;
  1824. }
  1825. /* Check and, if possible, fix discontinuity. */
  1826. for (i = 2*deg + 1; i < num_knots - (deg+1); i++) {
  1827. if (!ts_knots_equal(knots[i], knots[i-deg]))
  1828. continue;
  1829. fst = ctrlp + (i - (deg+1)) * dim;
  1830. snd = fst + dim;
  1831. dist = ts_distance(fst, snd, dim);
  1832. if (epsilon >= 0.f && dist > epsilon) {
  1833. TS_THROW_1(try, err, status,
  1834. TS_UNDERIVABLE,
  1835. "discontinuity at knot: %f",
  1836. knots[i])
  1837. }
  1838. memmove(snd,
  1839. snd + dim,
  1840. (num_ctrlp - (i+1-deg)) * sof_ctrlp);
  1841. memmove(&knots[i],
  1842. &knots[i+1],
  1843. (num_knots - (i+1)) * sof_real);
  1844. num_ctrlp--;
  1845. num_knots--;
  1846. i += deg-1;
  1847. }
  1848. /* Derive continuous worker. */
  1849. for (i = 0; i < num_ctrlp-1; i++) {
  1850. for (j = 0; j < dim; j++) {
  1851. k = i *dim + j;
  1852. l = (i+1)*dim + j;
  1853. ctrlp[k] = ctrlp[l] - ctrlp[k];
  1854. kid1 = knots[i+deg+1];
  1855. ki1 = knots[i+1];
  1856. span = kid1 - ki1;
  1857. if (span < TS_KNOT_EPSILON)
  1858. span = TS_KNOT_EPSILON;
  1859. ctrlp[k] *= deg;
  1860. ctrlp[k] /= span;
  1861. }
  1862. }
  1863. deg -= 1;
  1864. num_ctrlp -= 1;
  1865. num_knots -= 2;
  1866. knots += 1;
  1867. }
  1868. TS_CALL(try, err, ts_bspline_new(
  1869. num_ctrlp, dim, deg, TS_OPENED,
  1870. &swap, status))
  1871. memcpy(ts_int_bspline_access_ctrlp(&swap),
  1872. ctrlp,
  1873. num_ctrlp * sof_ctrlp);
  1874. memcpy(ts_int_bspline_access_knots(&swap),
  1875. knots,
  1876. num_knots * sof_real);
  1877. if (spline == deriv)
  1878. ts_bspline_free(deriv);
  1879. ts_bspline_move(&swap, deriv);
  1880. TS_FINALLY
  1881. ts_bspline_free(&worker);
  1882. TS_END_TRY_RETURN(err)
  1883. }
  1884. tsError
  1885. ts_int_bspline_insert_knot(const tsBSpline *spline,
  1886. const tsDeBoorNet *net,
  1887. size_t n,
  1888. tsBSpline *result,
  1889. tsStatus *status)
  1890. {
  1891. const size_t deg = ts_bspline_degree(spline);
  1892. const size_t order = ts_bspline_order(spline);
  1893. const size_t dim = ts_bspline_dimension(spline);
  1894. const tsReal knot = ts_deboornet_knot(net);
  1895. const size_t k = ts_deboornet_index(net);
  1896. const size_t mult = ts_deboornet_multiplicity(net);
  1897. const size_t sof_real = sizeof(tsReal);
  1898. const size_t sof_ctrlp = dim * sof_real;
  1899. size_t N; /**< Number of affected control points. */
  1900. tsReal* from; /**< Pointer to copy the values from. */
  1901. tsReal* to; /**< Pointer to copy the values to. */
  1902. int stride; /**< Stride of the next pointer to copy. */
  1903. size_t i; /**< Used in for loops. */
  1904. tsReal *ctrlp_spline, *ctrlp_result;
  1905. tsReal *knots_spline, *knots_result;
  1906. size_t num_ctrlp_result;
  1907. size_t num_knots_result;
  1908. tsError err;
  1909. INIT_OUT_BSPLINE(spline, result)
  1910. if (n == 0)
  1911. return ts_bspline_copy(spline, result, status);
  1912. if (mult + n > order) {
  1913. TS_RETURN_4(status, TS_MULTIPLICITY,
  1914. "multiplicity(%f) (%lu) + %lu > order (%lu)",
  1915. knot, (unsigned long) mult, (unsigned long) n,
  1916. (unsigned long) order)
  1917. }
  1918. TS_CALL_ROE(err, ts_int_bspline_resize(
  1919. spline, (int)n, 1, result, status))
  1920. ctrlp_spline = ts_int_bspline_access_ctrlp(spline);
  1921. knots_spline = ts_int_bspline_access_knots(spline);
  1922. ctrlp_result = ts_int_bspline_access_ctrlp(result);
  1923. knots_result = ts_int_bspline_access_knots(result);
  1924. num_ctrlp_result = ts_bspline_num_control_points(result);
  1925. num_knots_result = ts_bspline_num_knots(result);
  1926. /* mult + n <= deg + 1 (order) with n >= 1
  1927. * => mult <= deg
  1928. * => regular evaluation
  1929. * => N = h + 1 is valid */
  1930. N = ts_deboornet_num_insertions(net) + 1;
  1931. /* 1. Copy the relevant control points and knots from `spline'.
  1932. * 2. Copy the relevant control points and knots from `net'. */
  1933. /* 1.
  1934. *
  1935. * a) Copy left hand side control points from `spline'.
  1936. * b) Copy right hand side control points from `spline'.
  1937. * c) Copy left hand side knots from `spline'.
  1938. * d) Copy right hand side knots form `spline'. */
  1939. /*** Copy Control Points ***/
  1940. memmove(ctrlp_result, ctrlp_spline, (k-deg) * sof_ctrlp); /* a) */
  1941. from = (tsReal *) ctrlp_spline + dim*(k-deg+N);
  1942. /* n >= 0 implies to >= from */
  1943. to = ctrlp_result + dim*(k-deg+N+n);
  1944. memmove(to, from, (num_ctrlp_result-n-(k-deg+N)) * sof_ctrlp); /* b) */
  1945. /*** Copy Knots ***/
  1946. memmove(knots_result, knots_spline, (k+1) * sof_real); /* c) */
  1947. from = (tsReal *) knots_spline + k+1;
  1948. /* n >= 0 implies to >= from */
  1949. to = knots_result + k+1+n;
  1950. memmove(to, from, (num_knots_result-n-(k+1)) * sof_real); /* d) */
  1951. /* 2.
  1952. *
  1953. * a) Copy left hand side control points from `net'.
  1954. * b) Copy middle part control points from `net'.
  1955. * c) Copy right hand side control points from `net'.
  1956. * d) Copy knot from `net' (`knot'). */
  1957. from = ts_int_deboornet_access_points(net);
  1958. to = ctrlp_result + (k-deg)*dim;
  1959. stride = (int)(N*dim);
  1960. /*** Copy Control Points ***/
  1961. for (i = 0; i < n; i++) { /* a) */
  1962. memcpy(to, from, sof_ctrlp);
  1963. from += stride;
  1964. to += dim;
  1965. stride -= (int)dim;
  1966. }
  1967. memcpy(to, from, (N-n) * sof_ctrlp); /* b) */
  1968. from -= dim;
  1969. to += (N-n)*dim;
  1970. /* N = h+1 with h = deg-mult (ts_int_bspline_eval)
  1971. * => N = deg-mult+1 = order-mult.
  1972. *
  1973. * n <= order-mult
  1974. * => N-n+1 >= order-mult - order-mult + 1 = 1
  1975. * => -(int)(N-n+1) <= -1. */
  1976. stride = -(int)(N-n+1) * (int)dim;
  1977. for (i = 0; i < n; i++) { /* c) */
  1978. memcpy(to, from, sof_ctrlp);
  1979. from += stride;
  1980. stride -= (int)dim;
  1981. to += dim;
  1982. }
  1983. /*** Copy Knot ***/
  1984. to = knots_result + k+1;
  1985. for (i = 0; i < n; i++) { /* d) */
  1986. *to = knot;
  1987. to++;
  1988. }
  1989. TS_RETURN_SUCCESS(status)
  1990. }
  1991. tsError
  1992. ts_bspline_insert_knot(const tsBSpline *spline,
  1993. tsReal knot,
  1994. size_t num,
  1995. tsBSpline *result,
  1996. size_t* k,
  1997. tsStatus *status)
  1998. {
  1999. tsDeBoorNet net;
  2000. tsError err;
  2001. INIT_OUT_BSPLINE(spline, result)
  2002. ts_int_deboornet_init(&net);
  2003. TS_TRY(try, err, status)
  2004. TS_CALL(try, err, ts_bspline_eval(
  2005. spline, knot, &net, status))
  2006. TS_CALL(try, err, ts_int_bspline_insert_knot(
  2007. spline, &net, num, result, status))
  2008. ts_deboornet_free(&net);
  2009. TS_CALL(try, err, ts_bspline_eval(
  2010. result, knot, &net, status))
  2011. *k = ts_deboornet_index(&net);
  2012. TS_CATCH(err)
  2013. *k = 0;
  2014. TS_FINALLY
  2015. ts_deboornet_free(&net);
  2016. TS_END_TRY_RETURN(err)
  2017. }
  2018. tsError
  2019. ts_bspline_split(const tsBSpline *spline,
  2020. tsReal knot,
  2021. tsBSpline *split,
  2022. size_t* k,
  2023. tsStatus *status)
  2024. {
  2025. tsDeBoorNet net;
  2026. tsError err;
  2027. INIT_OUT_BSPLINE(spline, split)
  2028. ts_int_deboornet_init(&net);
  2029. TS_TRY(try, err, status)
  2030. TS_CALL(try, err, ts_bspline_eval(
  2031. spline, knot, &net, status))
  2032. if (ts_deboornet_multiplicity(&net)
  2033. == ts_bspline_order(spline)) {
  2034. TS_CALL(try, err, ts_bspline_copy(
  2035. spline, split, status))
  2036. *k = ts_deboornet_index(&net);
  2037. } else {
  2038. TS_CALL(try, err, ts_int_bspline_insert_knot(
  2039. spline, &net,
  2040. ts_deboornet_num_insertions(&net) + 1,
  2041. split, status))
  2042. *k = ts_deboornet_index(&net) +
  2043. ts_deboornet_num_insertions(&net) + 1;
  2044. }
  2045. TS_CATCH(err)
  2046. *k = 0;
  2047. TS_FINALLY
  2048. ts_deboornet_free(&net);
  2049. TS_END_TRY_RETURN(err)
  2050. }
  2051. tsError
  2052. ts_bspline_tension(const tsBSpline *spline,
  2053. tsReal beta,
  2054. tsBSpline *out,
  2055. tsStatus *status)
  2056. {
  2057. const size_t dim = ts_bspline_dimension(spline);
  2058. const size_t N = ts_bspline_num_control_points(spline);
  2059. const tsReal* p0 = ts_int_bspline_access_ctrlp(spline);
  2060. const tsReal* pn_1 = p0 + (N-1)*dim;
  2061. tsReal s; /**< The straightening factor. */
  2062. tsReal *ctrlp; /**< Pointer to the control points of `out'. */
  2063. size_t i, d; /**< Used in for loops. */
  2064. tsReal vec; /**< Straightening vector. */
  2065. tsError err;
  2066. TS_CALL_ROE(err, ts_bspline_copy(spline, out, status))
  2067. ctrlp = ts_int_bspline_access_ctrlp(out);
  2068. if (beta < (tsReal) 0.0) beta = (tsReal) 0.0;
  2069. if (beta > (tsReal) 1.0) beta = (tsReal) 1.0;
  2070. s = 1.f - beta;
  2071. for (i = 0; i < N; i++) {
  2072. for (d = 0; d < dim; d++) {
  2073. vec = ((tsReal)i / (N-1)) * (pn_1[d] - p0[d]);
  2074. ctrlp[i*dim + d] = beta * ctrlp[i*dim + d] +
  2075. s * (p0[d] + vec);
  2076. }
  2077. }
  2078. TS_RETURN_SUCCESS(status)
  2079. }
  2080. tsError
  2081. ts_bspline_to_beziers(const tsBSpline *spline,
  2082. tsBSpline *beziers,
  2083. tsStatus *status)
  2084. {
  2085. const size_t deg = ts_bspline_degree(spline);
  2086. const size_t order = ts_bspline_order(spline);
  2087. int resize; /**< Number of control points to add/remove. */
  2088. size_t k; /**< Index of the split knot value. */
  2089. tsReal u_min; /**< Minimum of the knot values. */
  2090. tsReal u_max; /**< Maximum of the knot values. */
  2091. tsBSpline tmp; /**< Temporarily stores the result. */
  2092. tsReal *knots; /**< Pointer to the knots of tmp. */
  2093. size_t num_knots; /**< Number of knots in tmp. */
  2094. tsError err;
  2095. INIT_OUT_BSPLINE(spline, beziers)
  2096. TS_CALL_ROE(err, ts_bspline_copy(spline, &tmp, status))
  2097. knots = ts_int_bspline_access_knots(&tmp);
  2098. num_knots = ts_bspline_num_knots(&tmp);
  2099. TS_TRY(try, err, status)
  2100. /* DO NOT FORGET TO UPDATE knots AND num_knots AFTER EACH
  2101. * TRANSFORMATION OF tmp! */
  2102. /* Fix first control point if necessary. */
  2103. u_min = knots[deg];
  2104. if (!ts_knots_equal(knots[0], u_min)) {
  2105. TS_CALL(try, err, ts_bspline_split(
  2106. &tmp, u_min, &tmp, &k, status))
  2107. resize = (int)(-1*deg + (deg*2 - k));
  2108. TS_CALL(try, err, ts_int_bspline_resize(
  2109. &tmp, resize, 0, &tmp, status))
  2110. knots = ts_int_bspline_access_knots(&tmp);
  2111. num_knots = ts_bspline_num_knots(&tmp);
  2112. }
  2113. /* Fix last control point if necessary. */
  2114. u_max = knots[num_knots - order];
  2115. if (!ts_knots_equal(knots[num_knots - 1], u_max)) {
  2116. TS_CALL(try, err, ts_bspline_split(
  2117. &tmp, u_max, &tmp, &k, status))
  2118. num_knots = ts_bspline_num_knots(&tmp);
  2119. resize = (int)(-1*deg + (k - (num_knots - order)));
  2120. TS_CALL(try, err, ts_int_bspline_resize(
  2121. &tmp, resize, 1, &tmp, status))
  2122. knots = ts_int_bspline_access_knots(&tmp);
  2123. num_knots = ts_bspline_num_knots(&tmp);
  2124. }
  2125. /* Split internal knots. */
  2126. k = order;
  2127. while (k < num_knots - order) {
  2128. TS_CALL(try, err, ts_bspline_split(
  2129. &tmp, knots[k], &tmp, &k, status))
  2130. knots = ts_int_bspline_access_knots(&tmp);
  2131. num_knots = ts_bspline_num_knots(&tmp);
  2132. k++;
  2133. }
  2134. if (spline == beziers)
  2135. ts_bspline_free(beziers);
  2136. ts_bspline_move(&tmp, beziers);
  2137. TS_FINALLY
  2138. ts_bspline_free(&tmp);
  2139. TS_END_TRY_RETURN(err)
  2140. }
  2141. tsError
  2142. ts_bspline_elevate_degree(const tsBSpline *spline,
  2143. size_t amount,
  2144. tsReal epsilon,
  2145. tsBSpline *elevated,
  2146. tsStatus * status)
  2147. {
  2148. tsBSpline worker;
  2149. size_t dim, order;
  2150. tsReal *ctrlp, *knots;
  2151. size_t num_beziers, i, a, c, d, offset, idx;
  2152. tsReal f, f_hat, *first, *last;
  2153. tsError err;
  2154. /* Trivial case. */
  2155. if (amount == 0)
  2156. return ts_bspline_copy(spline, elevated, status);
  2157. /* An overview of this algorithm can be found at:
  2158. * https://pages.mtu.edu/~shene/COURSES/cs3621/LAB/curve/elevation.html */
  2159. INIT_OUT_BSPLINE(spline, elevated);
  2160. worker = ts_bspline_init();
  2161. TS_TRY(try, err, status)
  2162. /* Decompose `spline' into a sequence of bezier curves and make
  2163. * space for the additional control points and knots that are
  2164. * to be inserted. Results are stored in `worker'. */
  2165. TS_CALL(try, err, ts_bspline_to_beziers(
  2166. spline, &worker, status));
  2167. num_beziers = ts_bspline_num_control_points(&worker) /
  2168. ts_bspline_order(&worker);
  2169. TS_CALL(try, err, ts_int_bspline_resize(
  2170. /* Resize by the number of knots to be inserted. Note
  2171. * that this creates too many control points (due to
  2172. * increasing the degree), which are removed at the end
  2173. * of this function. */
  2174. &worker, (int) ((num_beziers+1) * amount), 1, &worker,
  2175. status));
  2176. dim = ts_bspline_dimension(&worker);
  2177. order = ts_bspline_order(&worker);
  2178. ctrlp = ts_int_bspline_access_ctrlp(&worker);
  2179. knots = ts_int_bspline_access_knots(&worker);
  2180. /* Move all but the first bezier curve to their new location in
  2181. * the control point array so that the additional control
  2182. * points can be inserted without overwriting the others. Note
  2183. * that iteration must run backwards. Otherwise, the moved
  2184. * values overwrite each other. */
  2185. for (i = num_beziers - 1; i > 0; i--) {
  2186. /* `i' can be interpreted as the number of bezier
  2187. * curves before the current bezier curve. */
  2188. /* Location of current bezier curve. */
  2189. offset = i * order * dim;
  2190. /* Each elevation inserts an additional control point
  2191. * into every bezier curve. `i * amount' is the total
  2192. * number of control points to be inserted before the
  2193. * current bezier curve. */
  2194. memmove(ctrlp + offset + (i * amount * dim),
  2195. ctrlp + offset,
  2196. dim * order * sizeof(tsReal));
  2197. }
  2198. /* Move all but the first group of knots to their new location
  2199. * in the knot vector so that the additional knots can be
  2200. * inserted without overwriting the others. Note that iteration
  2201. * must run backwards. Otherwise, the moved values overwrite
  2202. * each other. */
  2203. for (i = num_beziers; i > 0; i--) {
  2204. /* Note that the number of knot groups is one more than
  2205. * the number of bezier curves. `i' can be interpreted
  2206. * as the number of knot groups before the current
  2207. * group. */
  2208. /* Location of current knot group. */
  2209. offset = i * order;
  2210. /* Each elevation inserts an additional knot into every
  2211. * group of knots. `i * amount' is the total number of
  2212. * knots to be inserted before the current knot
  2213. * group. */
  2214. memmove(knots + offset + (i * amount),
  2215. knots + offset,
  2216. order * sizeof(tsReal));
  2217. }
  2218. /* `worker' is now fully set up.
  2219. * The following formulas are based on:
  2220. * https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-elev.html */
  2221. for (a = 0; a < amount; a++) {
  2222. /* For each bezier curve... */
  2223. for (i = 0; i < num_beziers; i++) {
  2224. /* ... 1) Insert and update control points. */
  2225. /* Location of current bezier curve. Each
  2226. * elevation (`a') inserts an additional
  2227. * control point into every bezier curve and
  2228. * increases the degree (`order') by one. The
  2229. * location is thus made up of two parts:
  2230. *
  2231. * i) `i * order', which is the location taking
  2232. * into account the increasing order but
  2233. * neglecting the control points that are to be
  2234. * inserted before the current bezier curve. It
  2235. * can be seen as some sort of base location:
  2236. * Where would the bezier curve be (with
  2237. * respect to the current value of `order') if
  2238. * no additional control points had to be
  2239. * inserted?
  2240. *
  2241. * ii) `i * (amount - a)', which is the total
  2242. * number of control points to be inserted
  2243. * before the current bezier curve
  2244. * (`i * amount') taking into account the
  2245. * increasing order (`order' and `a' are
  2246. * increased equally, thus, `a' compensates for
  2247. * the increasing value of `order'). This part
  2248. * adds the necessary offset to the base
  2249. * location (`i * order'). */
  2250. offset = (i * order + i * (amount - a)) * dim;
  2251. /* Duplicate last control point to the new end
  2252. * position (next control point). */
  2253. memmove(ctrlp + offset + ((order) * dim),
  2254. ctrlp + offset + ((order-1) * dim),
  2255. dim * sizeof(tsReal));
  2256. /* All but the outer control points must be
  2257. * recalculated (domain: [1, order - 1]). By
  2258. * traversing backwards, control points can be
  2259. * modified in-place. */
  2260. for (c = order - 1; c > 0; c--) {
  2261. /* Location of current control point
  2262. * within current bezier curve. */
  2263. idx = offset + c * dim;
  2264. f = (tsReal) c / (tsReal) (order);
  2265. f_hat = 1 - f;
  2266. for (d = 0; d < dim; d++) {
  2267. /* For the sake of space, we
  2268. * increment idx by d and
  2269. * decrement it at the end of
  2270. * this loop. */
  2271. idx += d;
  2272. ctrlp[idx] =
  2273. f * ctrlp[idx - dim] +
  2274. f_hat * ctrlp[idx];
  2275. /* Reset idx. */
  2276. idx -= d;
  2277. }
  2278. }
  2279. /* ...2) Increase the multiplicity of the
  2280. * second knot group (maximum of the domain of
  2281. * the current bezier curve) by one. Note that
  2282. * this loop misses the last knot group (the
  2283. * group of the last bezier curve) as there is
  2284. * one more knot group than bezier curves to
  2285. * process. Thus, the last group must be
  2286. * increased separately after this loop. */
  2287. /* Location of current knot group. Each
  2288. * elevation (`a') inserts an additional
  2289. * knot into the knot vector of every bezier
  2290. * curve and increases the degree (`order') by
  2291. * one. The location is thus made up of two
  2292. * parts:
  2293. *
  2294. * i) `i * order', which is the location taking
  2295. * into account the increasing order but
  2296. * neglecting the knots that are to be inserted
  2297. * before the current knot group. It can be
  2298. * seen as some sort of base location: Where
  2299. * would the knot group be (with respect to the
  2300. * current value of `order') if no additional
  2301. * knots had to be inserted?
  2302. *
  2303. * ii) `i * (amount - a)', which is the total
  2304. * number of knots to be inserted before the
  2305. * current knot group (`i * amount') taking
  2306. * into account the increasing order (`order'
  2307. * and `a' are increased equally, thus, `a'
  2308. * compensates for the increasing value of
  2309. * `order'). This part adds the necessary
  2310. * offset to the base location
  2311. * (`i * order'). */
  2312. offset = i * order + i * (amount - a);
  2313. /* Duplicate knot. */
  2314. knots[offset + order] = knots[offset];
  2315. }
  2316. /* Increase the multiplicity of the very last knot
  2317. * group (the second group of the last bezier curve)
  2318. * by one. For more details, see knot duplication in
  2319. * previous loop. */
  2320. offset = num_beziers * order +
  2321. num_beziers * (amount - a);
  2322. knots[offset + order] = knots[offset];
  2323. /* Elevated by one. */
  2324. order++;
  2325. }
  2326. /* Combine bezier curves. */
  2327. d = 0; /* Number of removed knots/control points. */
  2328. for (i = 0; i < num_beziers - 1; i++) {
  2329. /* Is the last control point of bezier curve `i' equal
  2330. * to the first control point of bezier curve `i+1'? */
  2331. last = ctrlp + (
  2332. i * order /* base location of `i' */
  2333. - d /* minus the number of removed values */
  2334. + (order - 1) /* jump to last control point */
  2335. ) * dim;
  2336. first = last + dim; /* next control point */
  2337. if (ts_distance(last, first, dim) <= epsilon) {
  2338. /* Move control points. */
  2339. memmove(last, first, (num_beziers - 1 - i) *
  2340. order * dim * sizeof(tsReal));
  2341. /* Move knots. `last' is the last knot of the
  2342. * second knot group of bezier curve `i'.
  2343. * `first' is the first knot of the first knot
  2344. * group of bezier curve `i+1'. The
  2345. * calculations are quite similar to those for
  2346. * the control points `last' and `first' (see
  2347. * above). */
  2348. last = knots + i * order - d + (2 * order - 1);
  2349. first = last + 1;
  2350. memmove(last, first, (num_beziers - 1 - i) *
  2351. order * sizeof(tsReal));
  2352. /* Removed one knot/control point. */
  2353. d++;
  2354. }
  2355. }
  2356. /* Repair internal state. */
  2357. worker.pImpl->deg = order - 1;
  2358. worker.pImpl->n_knots -= d;
  2359. worker.pImpl->n_ctrlp = ts_bspline_num_knots(&worker) - order;
  2360. memmove(ts_int_bspline_access_knots(&worker),
  2361. knots, ts_bspline_sof_knots(&worker));
  2362. worker.pImpl = realloc(worker.pImpl,
  2363. ts_int_bspline_sof_state(&worker));
  2364. if (worker.pImpl == NULL) {
  2365. TS_THROW_0(try, err, status, TS_MALLOC,
  2366. "out of memory")
  2367. }
  2368. /* Move `worker' to output parameter. */
  2369. if (spline == elevated)
  2370. ts_bspline_free(elevated);
  2371. ts_bspline_move(&worker, elevated);
  2372. TS_FINALLY
  2373. ts_bspline_free(&worker);
  2374. TS_END_TRY_RETURN(err)
  2375. }
  2376. tsError
  2377. ts_bspline_align(const tsBSpline *s1,
  2378. const tsBSpline *s2,
  2379. tsReal epsilon,
  2380. tsBSpline *s1_out,
  2381. tsBSpline *s2_out,
  2382. tsStatus *status)
  2383. {
  2384. tsBSpline s1_worker, s2_worker, *smaller, *larger;
  2385. tsDeBoorNet net; /* the net of `smaller'. */
  2386. size_t i, missing, remaining;
  2387. tsReal min, max, shift, nextKnot;
  2388. tsError err;
  2389. INIT_OUT_BSPLINE(s1, s1_out)
  2390. INIT_OUT_BSPLINE(s2, s2_out)
  2391. s1_worker = ts_bspline_init();
  2392. s2_worker = ts_bspline_init();
  2393. smaller = larger = NULL;
  2394. TS_TRY(try, err, status)
  2395. /* Set up `s1_worker' and `s2_worker'. After this
  2396. * if-elseif-else-block, `s1_worker' and `s2_worker' have same
  2397. * degree. */
  2398. if (ts_bspline_degree(s1) < ts_bspline_degree(s2)) {
  2399. TS_CALL(try, err, ts_bspline_elevate_degree(s1,
  2400. ts_bspline_degree(s2) - ts_bspline_degree(s1),
  2401. epsilon, &s1_worker, status))
  2402. TS_CALL(try, err, ts_bspline_copy(
  2403. s2, &s2_worker, status))
  2404. } else if (ts_bspline_degree(s2) < ts_bspline_degree(s1)) {
  2405. TS_CALL(try, err, ts_bspline_elevate_degree(s2,
  2406. ts_bspline_degree(s1) - ts_bspline_degree(s2),
  2407. epsilon, &s2_worker, status))
  2408. TS_CALL(try, err, ts_bspline_copy(
  2409. s1, &s1_worker, status))
  2410. } else {
  2411. TS_CALL(try, err, ts_bspline_copy(
  2412. s1, &s1_worker, status))
  2413. TS_CALL(try, err, ts_bspline_copy(
  2414. s2, &s2_worker, status))
  2415. }
  2416. /* Set up `smaller', `larger', and `net'. */
  2417. if (ts_bspline_num_knots(&s1_worker) <
  2418. ts_bspline_num_knots(&s2_worker)) {
  2419. smaller = &s1_worker;
  2420. larger = &s2_worker;
  2421. } else {
  2422. smaller = &s2_worker;
  2423. larger = &s1_worker;
  2424. }
  2425. TS_CALL(try, err, ts_int_deboornet_new(
  2426. smaller, &net, status))
  2427. /* Insert knots into `smaller' until it has the same number of
  2428. * knots (and therefore the same number of control points) as
  2429. * `larger'. */
  2430. ts_bspline_domain(smaller, &min, &max);
  2431. missing = remaining = ts_bspline_num_knots(larger) -
  2432. ts_bspline_num_knots(smaller);
  2433. shift = (tsReal) 0.0;
  2434. if (missing > 0)
  2435. shift = ( (tsReal) 1.0 / missing ) * (tsReal) 0.5;
  2436. for (i = 0; remaining > 0; i++, remaining--) {
  2437. nextKnot = (max - min) * ((tsReal)i / missing) + min;
  2438. nextKnot += shift;
  2439. TS_CALL(try, err, ts_int_bspline_eval_woa(
  2440. smaller, nextKnot, &net, status))
  2441. while (!ts_deboornet_num_insertions(&net)) {
  2442. /* Linear exploration for next knot. */
  2443. nextKnot += 5 * TS_KNOT_EPSILON;
  2444. if (nextKnot > max) {
  2445. TS_THROW_0(try, err, status,
  2446. TS_NO_RESULT,
  2447. "no more knots for insertion")
  2448. }
  2449. TS_CALL(try, err, ts_int_bspline_eval_woa(
  2450. smaller, nextKnot, &net, status))
  2451. }
  2452. TS_CALL(try, err, ts_int_bspline_insert_knot(
  2453. smaller, &net, 1, smaller, status))
  2454. }
  2455. if (s1 == s1_out)
  2456. ts_bspline_free(s1_out);
  2457. if (s2 == s2_out)
  2458. ts_bspline_free(s2_out);
  2459. ts_bspline_move(&s1_worker, s1_out);
  2460. /* if `s1_out' == `s2_out', `s2_worker' must not be moved
  2461. * because otherwise the memory of `s1_worker' is leaked
  2462. * (`s2_worker' overrides `s1_worker'). */
  2463. if (s1_out != s2_out)
  2464. ts_bspline_move(&s2_worker, s2_out);
  2465. TS_FINALLY
  2466. ts_bspline_free(&s1_worker);
  2467. ts_bspline_free(&s2_worker);
  2468. ts_deboornet_free(&net);
  2469. TS_END_TRY_RETURN(err)
  2470. }
  2471. tsError
  2472. ts_bspline_morph(const tsBSpline *origin,
  2473. const tsBSpline *target,
  2474. tsReal t,
  2475. tsReal epsilon,
  2476. tsBSpline *out,
  2477. tsStatus *status)
  2478. {
  2479. tsBSpline origin_al, target_al; /* aligned origin and target */
  2480. tsReal *origin_al_c, *origin_al_k; /* control points and knots */
  2481. tsReal *target_al_c, *target_al_k; /* control points and knots */
  2482. /* Properties of `out'. */
  2483. size_t deg, dim, num_ctrlp, num_knots;
  2484. tsReal *ctrlp, *knots;
  2485. tsBSpline tmp; /* temporary buffer if `out' must be resized */
  2486. tsReal t_hat;
  2487. size_t i, offset, d;
  2488. tsError err;
  2489. origin_al = ts_bspline_init();
  2490. target_al = ts_bspline_init();
  2491. TS_TRY(try, err, status)
  2492. /* Clamp `t' to domain [0, 1] and set up `t_hat'. */
  2493. if (t < (tsReal) 0.0) t = (tsReal) 0.0;
  2494. if (t > (tsReal) 1.0) t = (tsReal) 1.0;
  2495. t_hat = (tsReal) 1.0 - t;
  2496. /* Set up `origin_al' and `target_al'. */
  2497. /* Degree must be elevated... */
  2498. if (ts_bspline_degree(origin) != ts_bspline_degree(target) ||
  2499. /* .. or knots (and thus control points) must be inserted. */
  2500. ts_bspline_num_knots(origin) != ts_bspline_num_knots(target)) {
  2501. TS_CALL(try, err, ts_bspline_align(
  2502. origin, target, epsilon, &origin_al, &target_al,
  2503. status));
  2504. } else {
  2505. /* Flat copy. */
  2506. origin_al = *origin;
  2507. target_al = *target;
  2508. }
  2509. origin_al_c = ts_int_bspline_access_ctrlp(&origin_al);
  2510. origin_al_k = ts_int_bspline_access_knots(&origin_al);
  2511. target_al_c = ts_int_bspline_access_ctrlp(&target_al);
  2512. target_al_k = ts_int_bspline_access_knots(&target_al);
  2513. /* Set up `out'. */
  2514. deg = ts_bspline_degree(&origin_al);
  2515. num_ctrlp = ts_bspline_num_control_points(&origin_al);
  2516. dim = ts_bspline_dimension(&origin_al);
  2517. if (ts_bspline_dimension(&target_al) < dim)
  2518. dim = ts_bspline_dimension(&target_al);
  2519. if (out->pImpl == NULL) {
  2520. TS_CALL(try, err, ts_bspline_new(num_ctrlp, dim, deg,
  2521. TS_OPENED /* doesn't matter */, out, status))
  2522. } else if (ts_bspline_degree(out) != deg ||
  2523. ts_bspline_num_control_points(out) != num_ctrlp ||
  2524. ts_bspline_dimension(out) != dim) {
  2525. TS_CALL(try, err, ts_bspline_new(num_ctrlp, dim, deg,
  2526. TS_OPENED /* doesn't matter */, &tmp, status))
  2527. ts_bspline_free(out);
  2528. ts_bspline_move(&tmp, out);
  2529. }
  2530. num_knots = ts_bspline_num_knots(out);
  2531. ctrlp = ts_int_bspline_access_ctrlp(out);
  2532. knots = ts_int_bspline_access_knots(out);
  2533. /* Interpolate control points. */
  2534. for (i = 0; i < num_ctrlp; i++) {
  2535. for (d = 0; d < dim; d++) {
  2536. offset = i * dim + d;
  2537. ctrlp[offset] = t * target_al_c[offset] +
  2538. t_hat * origin_al_c[offset];
  2539. }
  2540. }
  2541. /* Interpolate knots. */
  2542. for (i = 0; i < num_knots; i++) {
  2543. knots[i] = t * target_al_k[i] +
  2544. t_hat * origin_al_k[i];
  2545. }
  2546. TS_FINALLY
  2547. if (origin->pImpl != origin_al.pImpl)
  2548. ts_bspline_free(&origin_al);
  2549. if (target->pImpl != target_al.pImpl)
  2550. ts_bspline_free(&target_al);
  2551. TS_END_TRY_RETURN(err)
  2552. }
  2553. /*! @} */
  2554. /*! @name Serialization and Persistence
  2555. *
  2556. * @{
  2557. */
  2558. tsError
  2559. ts_int_bspline_to_json(const tsBSpline *spline,
  2560. JSON_Value **value,
  2561. tsStatus *status)
  2562. {
  2563. const size_t deg = ts_bspline_degree(spline);
  2564. const size_t dim = ts_bspline_dimension(spline);
  2565. const size_t len_ctrlp = ts_bspline_len_control_points(spline);
  2566. const size_t len_knots = ts_bspline_num_knots(spline);
  2567. const tsReal *ctrlp = ts_int_bspline_access_ctrlp(spline);
  2568. const tsReal *knots = ts_int_bspline_access_knots(spline);
  2569. size_t i; /**< Used in loops */
  2570. tsError err;
  2571. JSON_Value *ctrlp_value;
  2572. JSON_Value *knots_value;
  2573. JSON_Object *spline_object;
  2574. JSON_Array *ctrlp_array;
  2575. JSON_Array *knots_array;
  2576. *value = ctrlp_value = knots_value = NULL;
  2577. TS_TRY(values, err, status)
  2578. /* Init memory. */
  2579. *value = json_value_init_object();
  2580. if (!*value) {
  2581. TS_THROW_0(values, err, status, TS_MALLOC,
  2582. "out of memory")
  2583. }
  2584. ctrlp_value = json_value_init_array();
  2585. if (!ctrlp_value) {
  2586. TS_THROW_0(values, err, status, TS_MALLOC,
  2587. "out of memory")
  2588. }
  2589. knots_value = json_value_init_array();
  2590. if (!knots_value) {
  2591. TS_THROW_0(values, err, status, TS_MALLOC,
  2592. "out of memory")
  2593. }
  2594. /* Although the following functions cannot fail, that is, they
  2595. * won't return NULL or JSONFailure, we nevertheless handle
  2596. * unexpected return values. */
  2597. /* Init output. */
  2598. spline_object = json_value_get_object(*value);
  2599. if (!spline_object) {
  2600. TS_THROW_0(values, err, status, TS_MALLOC,
  2601. "out of memory")
  2602. }
  2603. /* Set degree and dimension. */
  2604. if (JSONSuccess != json_object_set_number(spline_object,
  2605. "degree",
  2606. (double) deg)) {
  2607. TS_THROW_0(values, err, status, TS_MALLOC,
  2608. "out of memory")
  2609. }
  2610. if (JSONSuccess != json_object_set_number(spline_object,
  2611. "dimension",
  2612. (double) dim)) {
  2613. TS_THROW_0(values, err, status, TS_MALLOC,
  2614. "out of memory")
  2615. }
  2616. /* Set control points. */
  2617. if (JSONSuccess != json_object_set_value(spline_object,
  2618. "control_points",
  2619. ctrlp_value)) {
  2620. TS_THROW_0(values, err, status, TS_MALLOC,
  2621. "out of memory")
  2622. }
  2623. ctrlp_array = json_array(ctrlp_value);
  2624. if (!ctrlp_array) {
  2625. TS_THROW_0(values, err, status, TS_MALLOC,
  2626. "out of memory")
  2627. }
  2628. for (i = 0; i < len_ctrlp; i++) {
  2629. if (JSONSuccess != json_array_append_number(
  2630. ctrlp_array, (double) ctrlp[i])) {
  2631. TS_THROW_0(values, err, status, TS_MALLOC,
  2632. "out of memory")
  2633. }
  2634. }
  2635. /* Set knots. */
  2636. if (JSONSuccess != json_object_set_value(spline_object,
  2637. "knots",
  2638. knots_value)) {
  2639. TS_THROW_0(values, err, status, TS_MALLOC,
  2640. "out of memory")
  2641. }
  2642. knots_array = json_array(knots_value);
  2643. if (!knots_array) {
  2644. TS_THROW_0(values, err, status, TS_MALLOC,
  2645. "out of memory")
  2646. }
  2647. for (i = 0; i < len_knots; i++) {
  2648. if (JSONSuccess != json_array_append_number(
  2649. knots_array, (double) knots[i])) {
  2650. TS_THROW_0(values, err, status, TS_MALLOC,
  2651. "out of memory")
  2652. }
  2653. }
  2654. TS_CATCH(err)
  2655. if (*value)
  2656. json_value_free(*value);
  2657. if (ctrlp_value && !json_value_get_parent(ctrlp_value))
  2658. json_value_free(ctrlp_value);
  2659. if (knots_value && !json_value_get_parent(knots_value))
  2660. json_value_free(knots_value);
  2661. *value = NULL;
  2662. TS_END_TRY_RETURN(err)
  2663. }
  2664. tsError
  2665. ts_int_bspline_parse_json(const JSON_Value *spline_value,
  2666. tsBSpline *spline,
  2667. tsStatus *status)
  2668. {
  2669. size_t deg, dim, len_ctrlp, num_knots;
  2670. tsReal *ctrlp, *knots;
  2671. JSON_Object *spline_object;
  2672. JSON_Value *deg_value;
  2673. JSON_Value *dim_value;
  2674. JSON_Value *ctrlp_value;
  2675. JSON_Array *ctrlp_array;
  2676. JSON_Value *knots_value;
  2677. JSON_Array *knots_array;
  2678. JSON_Value *real_value;
  2679. size_t i;
  2680. tsError err;
  2681. ts_int_bspline_init(spline);
  2682. /* Read spline object. */
  2683. if (json_value_get_type(spline_value) != JSONObject)
  2684. TS_RETURN_0(status, TS_PARSE_ERROR, "invalid json input")
  2685. spline_object = json_value_get_object(spline_value);
  2686. if (!spline_object)
  2687. TS_RETURN_0(status, TS_PARSE_ERROR, "invalid json input")
  2688. /* Read degree. */
  2689. deg_value = json_object_get_value(spline_object, "degree");
  2690. if (json_value_get_type(deg_value) != JSONNumber)
  2691. TS_RETURN_0(status, TS_PARSE_ERROR, "degree is not a number")
  2692. if (json_value_get_number(deg_value) < -0.01f) {
  2693. TS_RETURN_1(status, TS_PARSE_ERROR,
  2694. "degree (%f) < 0",
  2695. json_value_get_number(deg_value))
  2696. }
  2697. deg = (size_t) json_value_get_number(deg_value);
  2698. /* Read dimension. */
  2699. dim_value = json_object_get_value(spline_object, "dimension");
  2700. if (json_value_get_type(dim_value) != JSONNumber) {
  2701. TS_RETURN_0(status, TS_PARSE_ERROR,
  2702. "dimension is not a number")
  2703. }
  2704. if (json_value_get_number(dim_value) < 0.99f) {
  2705. TS_RETURN_1(status, TS_PARSE_ERROR,
  2706. "dimension (%f) < 1",
  2707. json_value_get_number(deg_value))
  2708. }
  2709. dim = (size_t) json_value_get_number(dim_value);
  2710. /* Read length of control point vector. */
  2711. ctrlp_value = json_object_get_value(spline_object, "control_points");
  2712. if (json_value_get_type(ctrlp_value) != JSONArray) {
  2713. TS_RETURN_0(status, TS_PARSE_ERROR,
  2714. "control_points is not an array")
  2715. }
  2716. ctrlp_array = json_value_get_array(ctrlp_value);
  2717. len_ctrlp = json_array_get_count(ctrlp_array);
  2718. if (len_ctrlp % dim != 0) {
  2719. TS_RETURN_2(status, TS_PARSE_ERROR,
  2720. "len(control_points) (%lu) %% dimension (%lu) != 0",
  2721. (unsigned long) len_ctrlp, (unsigned long) dim)
  2722. }
  2723. /* Read number of knots. */
  2724. knots_value = json_object_get_value(spline_object, "knots");
  2725. if (json_value_get_type(knots_value) != JSONArray) {
  2726. TS_RETURN_0(status, TS_PARSE_ERROR,
  2727. "knots is not an array")
  2728. }
  2729. knots_array = json_value_get_array(knots_value);
  2730. num_knots = json_array_get_count(knots_array);
  2731. /* Create spline. */
  2732. TS_TRY(try, err, status)
  2733. TS_CALL(try, err, ts_bspline_new(
  2734. len_ctrlp/dim, dim, deg,
  2735. TS_CLAMPED, spline, status))
  2736. if (num_knots != ts_bspline_num_knots(spline))
  2737. TS_THROW_2(try, err, status, TS_NUM_KNOTS,
  2738. "unexpected num(knots): (%lu) != (%lu)",
  2739. (unsigned long) num_knots,
  2740. (unsigned long) ts_bspline_num_knots(spline))
  2741. /* Set control points. */
  2742. ctrlp = ts_int_bspline_access_ctrlp(spline);
  2743. for (i = 0; i < len_ctrlp; i++) {
  2744. real_value = json_array_get_value(ctrlp_array, i);
  2745. if (json_value_get_type(real_value) != JSONNumber)
  2746. TS_THROW_1(try, err, status, TS_PARSE_ERROR,
  2747. "control_points: value at index %lu is not a number",
  2748. (unsigned long) i)
  2749. ctrlp[i] = (tsReal) json_value_get_number(real_value);
  2750. }
  2751. TS_CALL(try, err, ts_bspline_set_control_points(
  2752. spline, ctrlp, status))
  2753. /* Set knots. */
  2754. knots = ts_int_bspline_access_knots(spline);
  2755. for (i = 0; i < num_knots; i++) {
  2756. real_value = json_array_get_value(knots_array, i);
  2757. if (json_value_get_type(real_value) != JSONNumber)
  2758. TS_THROW_1(try, err, status, TS_PARSE_ERROR,
  2759. "knots: value at index %lu is not a number",
  2760. (unsigned long) i)
  2761. knots[i] = (tsReal) json_value_get_number(real_value);
  2762. }
  2763. TS_CALL(try, err, ts_bspline_set_knots(
  2764. spline, knots, status))
  2765. TS_CATCH(err)
  2766. ts_bspline_free(spline);
  2767. TS_END_TRY_RETURN(err)
  2768. }
  2769. tsError
  2770. ts_bspline_to_json(const tsBSpline *spline,
  2771. char **json,
  2772. tsStatus *status)
  2773. {
  2774. tsError err;
  2775. JSON_Value *value = NULL;
  2776. *json = NULL;
  2777. TS_CALL_ROE(err, ts_int_bspline_to_json(spline, &value, status))
  2778. *json = json_serialize_to_string_pretty(value);
  2779. json_value_free(value);
  2780. if (!*json)
  2781. TS_RETURN_0(status, TS_MALLOC, "out of memory")
  2782. TS_RETURN_SUCCESS(status)
  2783. }
  2784. tsError
  2785. ts_bspline_parse_json(const char *json,
  2786. tsBSpline *spline,
  2787. tsStatus *status)
  2788. {
  2789. tsError err;
  2790. JSON_Value *value = NULL;
  2791. ts_int_bspline_init(spline);
  2792. TS_TRY(try, err, status)
  2793. value = json_parse_string(json);
  2794. if (!value) {
  2795. TS_RETURN_0(status, TS_PARSE_ERROR,
  2796. "invalid json input")
  2797. }
  2798. TS_CALL(try, err, ts_int_bspline_parse_json(
  2799. value, spline, status))
  2800. TS_FINALLY
  2801. if (value)
  2802. json_value_free(value);
  2803. TS_END_TRY_RETURN(err)
  2804. }
  2805. tsError
  2806. ts_bspline_save(const tsBSpline *spline,
  2807. const char *path,
  2808. tsStatus *status)
  2809. {
  2810. tsError err;
  2811. JSON_Status json_status;
  2812. JSON_Value *value = NULL;
  2813. TS_CALL_ROE(err, ts_int_bspline_to_json(spline, &value, status))
  2814. json_status = json_serialize_to_file_pretty(value, path);
  2815. json_value_free(value);
  2816. if (json_status != JSONSuccess)
  2817. TS_RETURN_0(status, TS_IO_ERROR, "unexpected io error")
  2818. TS_RETURN_SUCCESS(status)
  2819. }
  2820. tsError
  2821. ts_bspline_load(const char *path,
  2822. tsBSpline *spline,
  2823. tsStatus *status)
  2824. {
  2825. tsError err;
  2826. FILE *file = NULL;
  2827. JSON_Value *value = NULL;
  2828. ts_int_bspline_init(spline);
  2829. TS_TRY(try, err, status)
  2830. file = fopen(path, "r");
  2831. if (!file) {
  2832. TS_THROW_0(try, err, status, TS_IO_ERROR,
  2833. "unable to open file")
  2834. }
  2835. value = json_parse_file(path);
  2836. if (!value) {
  2837. TS_RETURN_0(status, TS_PARSE_ERROR,
  2838. "invalid json input")
  2839. }
  2840. TS_CALL(try, err, ts_int_bspline_parse_json(
  2841. value, spline, status))
  2842. TS_FINALLY
  2843. if (file)
  2844. fclose(file);
  2845. if (value)
  2846. json_value_free(value);
  2847. TS_CATCH(err)
  2848. ts_bspline_free(spline);
  2849. TS_END_TRY_RETURN(err)
  2850. }
  2851. /*! @} */
  2852. /*! @name Vector Math
  2853. * @{
  2854. */
  2855. void
  2856. ts_vec2_init(tsReal *out,
  2857. tsReal x,
  2858. tsReal y)
  2859. {
  2860. out[0] = x;
  2861. out[1] = y;
  2862. }
  2863. void
  2864. ts_vec3_init(tsReal *out,
  2865. tsReal x,
  2866. tsReal y,
  2867. tsReal z)
  2868. {
  2869. out[0] = x;
  2870. out[1] = y;
  2871. out[2] = z;
  2872. }
  2873. void
  2874. ts_vec4_init(tsReal *out,
  2875. tsReal x,
  2876. tsReal y,
  2877. tsReal z,
  2878. tsReal w)
  2879. {
  2880. out[0] = x;
  2881. out[1] = y;
  2882. out[2] = z;
  2883. out[3] = w;
  2884. }
  2885. void
  2886. ts_vec2_set(tsReal *out,
  2887. const tsReal *x,
  2888. size_t dim)
  2889. {
  2890. const size_t n = dim > 2 ? 2 : dim;
  2891. memmove(out, x, n * sizeof(tsReal));
  2892. if (dim < 2)
  2893. ts_arr_fill(out + dim, 2 - dim, (tsReal) 0.0);
  2894. }
  2895. void
  2896. ts_vec3_set(tsReal *out,
  2897. const tsReal *x,
  2898. size_t dim)
  2899. {
  2900. const size_t n = dim > 3 ? 3 : dim;
  2901. memmove(out, x, n * sizeof(tsReal));
  2902. if (dim < 3)
  2903. ts_arr_fill(out + dim, 3 - dim, (tsReal) 0.0);
  2904. }
  2905. void
  2906. ts_vec4_set(tsReal *out,
  2907. const tsReal *x,
  2908. size_t dim)
  2909. {
  2910. const size_t n = dim > 4 ? 4 : dim;
  2911. memmove(out, x, n * sizeof(tsReal));
  2912. if (dim < 4)
  2913. ts_arr_fill(out + dim, 4 - dim, (tsReal) 0.0);
  2914. }
  2915. void
  2916. ts_vec_add(const tsReal *x,
  2917. const tsReal *y,
  2918. size_t dim,
  2919. tsReal *out)
  2920. {
  2921. size_t i;
  2922. for (i = 0; i < dim; i++)
  2923. out[i] = x[i] + y[i];
  2924. }
  2925. void
  2926. ts_vec_sub(const tsReal *x,
  2927. const tsReal *y,
  2928. size_t dim,
  2929. tsReal *out)
  2930. {
  2931. size_t i;
  2932. if (x == y) {
  2933. /* More stable version. */
  2934. ts_arr_fill(out, dim, (tsReal) 0.0);
  2935. return;
  2936. }
  2937. for (i = 0; i < dim; i++)
  2938. out[i] = x[i] - y[i];
  2939. }
  2940. tsReal
  2941. ts_vec_dot(const tsReal *x,
  2942. const tsReal *y,
  2943. size_t dim)
  2944. {
  2945. size_t i;
  2946. tsReal dot = 0;
  2947. for (i = 0; i < dim; i++)
  2948. dot += x[i] * y[i];
  2949. return dot;
  2950. }
  2951. tsReal
  2952. ts_vec_angle(const tsReal *x,
  2953. const tsReal *y,
  2954. tsReal *buf,
  2955. size_t dim)
  2956. {
  2957. const tsReal *x_norm, *y_norm;
  2958. if (buf) {
  2959. ts_vec_norm(x, dim, buf);
  2960. ts_vec_norm(y, dim, buf + dim);
  2961. x_norm = buf;
  2962. y_norm = buf + dim;
  2963. } else {
  2964. x_norm = x;
  2965. y_norm = y;
  2966. }
  2967. return (tsReal) (
  2968. /* Use doubles as long as possible. */
  2969. acos(ts_vec_dot(x_norm,
  2970. y_norm,
  2971. dim))
  2972. * (180.0 / TS_PI) /* radiant to degree */
  2973. );
  2974. }
  2975. void
  2976. ts_vec3_cross(const tsReal *x,
  2977. const tsReal *y,
  2978. tsReal *out)
  2979. {
  2980. tsReal a, b, c;
  2981. a = x[1] * y[2] - x[2] * y[1];
  2982. b = x[2] * y[0] - x[0] * y[2];
  2983. c = x[0] * y[1] - x[1] * y[0];
  2984. out[0] = a;
  2985. out[1] = b;
  2986. out[2] = c;
  2987. }
  2988. void
  2989. ts_vec_norm(const tsReal *x,
  2990. size_t dim,
  2991. tsReal *out)
  2992. {
  2993. size_t i;
  2994. const tsReal m = ts_vec_mag(x, dim);
  2995. if (m < TS_LENGTH_ZERO) {
  2996. ts_arr_fill(out, dim, (tsReal) 0.0);
  2997. return;
  2998. }
  2999. for (i = 0; i < dim; i++)
  3000. out[i] = x[i] / m;
  3001. }
  3002. tsReal
  3003. ts_vec_mag(const tsReal *x,
  3004. size_t dim)
  3005. {
  3006. size_t i;
  3007. tsReal sum = 0;
  3008. for (i = 0; i < dim; i++)
  3009. sum += (x[i] * x[i]);
  3010. return (tsReal) sqrt(sum);
  3011. }
  3012. void
  3013. ts_vec_mul(const tsReal *x,
  3014. size_t dim,
  3015. tsReal val,
  3016. tsReal *out)
  3017. {
  3018. size_t i;
  3019. for (i = 0; i < dim; i++)
  3020. out[i] = x[i] * val;
  3021. }
  3022. /*! @} */
  3023. /*! @name Chord Length Method
  3024. *
  3025. * @{
  3026. */
  3027. tsError
  3028. ts_chord_lengths_length_to_knot(const tsReal *knots,
  3029. const tsReal *lengths,
  3030. size_t num,
  3031. tsReal len,
  3032. tsReal *knot,
  3033. tsStatus *status)
  3034. {
  3035. tsReal numer, denom, r, r_hat;
  3036. size_t idx, low, high;
  3037. /* Handle spacial cases. */
  3038. if (num == 0) { /* well... */
  3039. TS_RETURN_0(status, TS_NO_RESULT, "empty chord lengths")
  3040. }
  3041. if (num == 1) { /* no computation needed */
  3042. *knot = knots[0];
  3043. TS_RETURN_SUCCESS(status)
  3044. }
  3045. if (lengths[num - 1] < TS_LENGTH_ZERO) { /* spline is too short */
  3046. *knot = knots[0];
  3047. TS_RETURN_SUCCESS(status)
  3048. }
  3049. if (len <= lengths[0]) { /* clamp `len' to lower bound */
  3050. *knot = knots[0];
  3051. TS_RETURN_SUCCESS(status)
  3052. }
  3053. if (len >= lengths[num - 1]) { /* clamp `len' to upper bound */
  3054. *knot = knots[num - 1];
  3055. TS_RETURN_SUCCESS(status)
  3056. }
  3057. /* From now on: i) `len' is less than the last chord length in
  3058. `lengths' and ii) `lengths' contains at least two values. Hence, the
  3059. index (`idx') determined by the following binary search cannot be
  3060. the last index in `knots' and `lengths', respectively (i.e., `idx <=
  3061. num - 2`). It is therefore safe to access `knots' and `lengths' at
  3062. index `idx + 1`. */
  3063. /* Binary search. Similar to how locating a knot within a knot vector
  3064. is implemented in ::ts_int_bspline_find_knot. */
  3065. low = 0;
  3066. high = num - 1;
  3067. idx = (low+high) / 2;
  3068. while (len < lengths[idx] || len >= lengths[(idx) + 1]) {
  3069. if (len < lengths[idx]) high = idx;
  3070. else low = idx;
  3071. idx = (low+high) / 2;
  3072. }
  3073. /* Determine `knot' by linear interpolation. */
  3074. denom = lengths[(idx) + 1] - lengths[idx];
  3075. if (denom < TS_LENGTH_ZERO) { /* segment is too short */
  3076. *knot = knots[idx];
  3077. TS_RETURN_SUCCESS(status)
  3078. }
  3079. numer = len - lengths[idx];
  3080. r = numer / denom; /* denom >= TS_LENGTH_ZERO */
  3081. r_hat = (tsReal) 1.0 - r;
  3082. *knot = r * knots[(idx) + 1] + r_hat * knots[idx];
  3083. TS_RETURN_SUCCESS(status)
  3084. }
  3085. tsError
  3086. ts_chord_lengths_t_to_knot(const tsReal *knots,
  3087. const tsReal *lengths,
  3088. size_t num,
  3089. tsReal t,
  3090. tsReal *knot,
  3091. tsStatus *status)
  3092. {
  3093. /* Delegate error handling. If `num' is `0`,
  3094. `ts_chord_lengths_length_to_knot' doesn't read `len' at all. */
  3095. tsReal len = num == 0 ? 0 : t * lengths[num - 1];
  3096. return ts_chord_lengths_length_to_knot(knots,
  3097. lengths,
  3098. num,
  3099. len,
  3100. knot,
  3101. status);
  3102. }
  3103. tsError
  3104. ts_chord_lengths_equidistant_knot_seq(const tsReal *knots,
  3105. const tsReal *lengths,
  3106. size_t num,
  3107. size_t num_knot_seq,
  3108. tsReal *knot_seq,
  3109. tsStatus *status)
  3110. {
  3111. tsError err;
  3112. size_t i;
  3113. tsReal t, knot;
  3114. if (num_knot_seq == 0) TS_RETURN_SUCCESS(status)
  3115. TS_TRY(try, err, status)
  3116. for (i = 0; i < num_knot_seq; i++) {
  3117. t = (tsReal) i / (num_knot_seq - 1);
  3118. TS_CALL(try, err, ts_chord_lengths_t_to_knot(
  3119. knots, lengths, num, t, &knot, status))
  3120. knot_seq[i] = knot;
  3121. }
  3122. /* Set `knot_seq[0]` after `knot_seq[num_knot_seq - 1]` to
  3123. ensure that `knot_seq[0] = min` if `num_knot_seq` is
  3124. `1'. Note that `num_knot_seq` and `num` can't be `0'. */
  3125. knot_seq[num_knot_seq - 1] = knots[num - 1];
  3126. knot_seq[0] = knots[0];
  3127. TS_END_TRY_RETURN(err)
  3128. }
  3129. /*! @} */
  3130. /*! @name Utility Functions
  3131. *
  3132. * @{
  3133. */
  3134. int ts_knots_equal(tsReal x,
  3135. tsReal y)
  3136. {
  3137. return fabs(x-y) < TS_KNOT_EPSILON ? 1 : 0;
  3138. }
  3139. void ts_arr_fill(tsReal *arr,
  3140. size_t num,
  3141. tsReal val)
  3142. {
  3143. size_t i;
  3144. for (i = 0; i < num; i++)
  3145. arr[i] = val;
  3146. }
  3147. tsReal ts_distance(const tsReal *x,
  3148. const tsReal *y,
  3149. size_t dim)
  3150. {
  3151. size_t i;
  3152. tsReal sum = 0;
  3153. for (i = 0; i < dim; i++)
  3154. sum += (x[i] - y[i]) * (x[i] - y[i]);
  3155. return (tsReal) sqrt(sum);
  3156. }
  3157. /*! @} */
  3158. #ifdef _MSC_VER
  3159. #pragma warning(pop)
  3160. #endif