smoothing.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. /****************************************************************
  2. *
  3. * MODULE: v.generalize
  4. *
  5. * AUTHOR(S): Daniel Bundala
  6. *
  7. * PURPOSE: Module for line simplification and smoothing
  8. *
  9. * COPYRIGHT: (C) 2002-2005 by the GRASS Development Team
  10. *
  11. * This program is free software under the
  12. * GNU General Public License (>=v2).
  13. * Read the file COPYING that comes with GRASS
  14. * for details.
  15. *
  16. ****************************************************************/
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <math.h>
  20. #include <grass/gis.h>
  21. #include <grass/vector.h>
  22. #include <grass/glocale.h>
  23. #include "point.h"
  24. #include "matrix.h"
  25. /* TODO: add loop support where possible */
  26. /* boyle's forward looking algorithm
  27. * return the number of points in the result = Points->n_points
  28. */
  29. int boyle(struct line_pnts *Points, int look_ahead, int loop_support, int with_z)
  30. {
  31. POINT last, ppoint;
  32. POINT *res;
  33. int next, n, i, p;
  34. double c1, c2;
  35. int is_loop, count;
  36. n = Points->n_points;
  37. /* if look_ahead is too small or line too short, there's nothing
  38. * to smooth */
  39. if (look_ahead < 2 || look_ahead >= n) {
  40. return n;
  41. }
  42. count = n - 2;
  43. is_loop = 0;
  44. /* is it loop ? */
  45. if (Points->x[0] == Points->x[n - 1] &&
  46. Points->y[0] == Points->y[n - 1] &&
  47. (Points->z[0] == Points->z[n - 1] || with_z == 0) && loop_support){
  48. is_loop = 1;
  49. count = n;
  50. }
  51. res = G_malloc(sizeof(POINT) * n);
  52. point_assign(Points, 0, with_z, &last, 0);
  53. res[0] = last;
  54. c1 = (double)1 / (double)(look_ahead - 1);
  55. c2 = (double)1 - c1;
  56. next = 1;
  57. for (i = 0; i < count; i++) {
  58. p = i + look_ahead;
  59. if (!is_loop && p >= n)
  60. p = n - 1;
  61. point_assign(Points, p, with_z, &ppoint, is_loop);
  62. point_scalar(ppoint, c1, &ppoint);
  63. point_scalar(last, c2, &last);
  64. point_add(last, ppoint, &res[next]);
  65. /* original: use smoothed point as new last point */
  66. /* last = res[next]; */
  67. next++;
  68. if (is_loop) {
  69. while (next >= n - 1)
  70. next -= n - 1;
  71. }
  72. /* new: smooth with original points */
  73. point_assign(Points, p, with_z, &last, is_loop);
  74. }
  75. for (i = 1; i < n - 1; i++) {
  76. Points->x[i] = res[i].x;
  77. Points->y[i] = res[i].y;
  78. Points->z[i] = res[i].z;
  79. }
  80. if (is_loop) {
  81. Points->x[0] = res[0].x;
  82. Points->y[0] = res[0].y;
  83. Points->z[0] = res[0].z;
  84. Points->x[n - 1] = res[0].x;
  85. Points->y[n - 1] = res[0].y;
  86. Points->z[n - 1] = res[0].z;
  87. }
  88. G_free(res);
  89. return Points->n_points;
  90. }
  91. /* mcmaster's sliding averaging algorithm. Return the number of points
  92. * in the output line. This equals to the number of points in the
  93. * input line */
  94. int sliding_averaging(struct line_pnts *Points, double slide, int look_ahead,
  95. int loop_support, int with_z)
  96. {
  97. int n, half, i;
  98. double sc;
  99. POINT p, tmp, s;
  100. POINT *res;
  101. int is_loop, count;
  102. is_loop = 0;
  103. n = Points->n_points;
  104. half = look_ahead / 2;
  105. count = n - half;
  106. /* is it loop ? */
  107. if (Points->x[0] == Points->x[n - 1] &&
  108. Points->y[0] == Points->y[n - 1] &&
  109. (Points->z[0] == Points->z[n - 1] || with_z == 0) && loop_support){
  110. is_loop = 1;
  111. count = n + half;
  112. }
  113. if (look_ahead % 2 == 0) {
  114. G_fatal_error(_("Look ahead parameter must be odd"));
  115. return n;
  116. }
  117. if (look_ahead >= n || look_ahead < 2)
  118. return n;
  119. res = G_malloc(sizeof(POINT) * (n + half));
  120. if (!res) {
  121. G_fatal_error(_("Out of memory"));
  122. return n;
  123. }
  124. sc = (double)1.0 / (double)look_ahead;
  125. point_assign(Points, 0, with_z, &p, 0);
  126. for (i = 1; i < look_ahead; i++) {
  127. point_assign(Points, i, with_z, &tmp, 0);
  128. point_add(p, tmp, &p);
  129. }
  130. /* and calculate the average of remaining points */
  131. for (i = half; i < count; i++) {
  132. point_assign(Points, i, with_z, &s, is_loop);
  133. point_scalar(s, 1.0 - slide, &s);
  134. point_scalar(p, sc * slide, &tmp);
  135. point_add(tmp, s, &res[i]);
  136. if ((i + half + 1 < n) || is_loop) {
  137. point_assign(Points, i - half, with_z, &tmp, is_loop);
  138. point_subtract(p, tmp, &p);
  139. point_assign(Points, i + half + 1, with_z, &tmp, is_loop);
  140. point_add(p, tmp, &p);
  141. }
  142. }
  143. if (is_loop) {
  144. for (i = 0; i < half; i++) {
  145. Points->x[i] = res[n + i - 1].x;
  146. Points->y[i] = res[n + i - 1].y;
  147. Points->z[i] = res[n + i - 1].z;
  148. }
  149. for (i = half; i < n; i++) {
  150. Points->x[i] = res[i].x;
  151. Points->y[i] = res[i].y;
  152. Points->z[i] = res[i].z;
  153. }
  154. }
  155. else {
  156. for (i = half; i < n - half; i++) {
  157. Points->x[i] = res[i].x;
  158. Points->y[i] = res[i].y;
  159. Points->z[i] = res[i].z;
  160. }
  161. }
  162. G_free(res);
  163. return Points->n_points;
  164. }
  165. /* mcmaster's distance weighting algorithm. Return the number
  166. * of points in the output line which equals to Points->n_points */
  167. int distance_weighting(struct line_pnts *Points, double slide, int look_ahead,
  168. int loop_support, int with_z)
  169. {
  170. POINT p, c, s, tmp;
  171. int n, i, half, j;
  172. double dists, d;
  173. POINT *res;
  174. int is_loop, count;
  175. n = Points->n_points;
  176. if (look_ahead >= n || look_ahead < 1)
  177. return n;
  178. half = look_ahead / 2;
  179. count = n - half;
  180. /* is it loop ? */
  181. is_loop = 0;
  182. if (Points->x[0] == Points->x[n - 1] &&
  183. Points->y[0] == Points->y[n - 1] &&
  184. (Points->z[0] == Points->z[n - 1] || with_z == 0) && loop_support){
  185. is_loop = 1;
  186. count = n + half - 1;
  187. }
  188. if ((look_ahead & 1) == 0) {
  189. G_fatal_error(_("Look ahead parameter must be odd"));
  190. return n;
  191. }
  192. res = (POINT *) G_malloc(sizeof(POINT) * (n + half));
  193. if (!res) {
  194. G_fatal_error(_("Out of memory"));
  195. return n;
  196. }
  197. point_assign(Points, 0, with_z, &res[0], 0);
  198. for (i = half; i < count; i++) {
  199. point_assign(Points, i, with_z, &c, is_loop);
  200. s.x = s.y = s.z = 0;
  201. dists = 0;
  202. for (j = i - half; j <= i + half; j++) {
  203. if (j == i)
  204. continue;
  205. point_assign(Points, j, with_z, &p, is_loop);
  206. d = point_dist(p, c);
  207. if (d < GRASS_EPSILON)
  208. continue;
  209. d = (double)1.0 / d;
  210. dists += d;
  211. point_scalar(p, d, &tmp);
  212. s.x += tmp.x;
  213. s.y += tmp.y;
  214. s.z += tmp.z;
  215. }
  216. if (dists < GRASS_EPSILON) {
  217. point_add(c, s, &res[i]);
  218. }
  219. else {
  220. point_scalar(s, slide / dists, &tmp);
  221. point_scalar(c, (double)1.0 - slide, &s);
  222. point_add(s, tmp, &res[i]);
  223. }
  224. }
  225. if (is_loop) {
  226. for (i = 0; i < half; i++) {
  227. Points->x[i] = res[n + i - 1].x;
  228. Points->y[i] = res[n + i - 1].y;
  229. Points->z[i] = res[n + i - 1].z;
  230. }
  231. for (i = half; i < n; i++) {
  232. Points->x[i] = res[i].x;
  233. Points->y[i] = res[i].y;
  234. Points->z[i] = res[i].z;
  235. }
  236. }
  237. else {
  238. for (i = half; i < n - half; i++) {
  239. Points->x[i] = res[i].x;
  240. Points->y[i] = res[i].y;
  241. Points->z[i] = res[i].z;
  242. }
  243. }
  244. G_free(res);
  245. return Points->n_points;
  246. }
  247. /* Chaiken's algorithm. Return the number of points in smoothed line
  248. */
  249. int chaiken(struct line_pnts *Points, double thresh, int loop_support, int with_z)
  250. {
  251. int n, i;
  252. POINT_LIST head, *cur;
  253. POINT p0, p1, p2, m1, tmp;
  254. int is_loop;
  255. n = Points->n_points;
  256. /* line is too short */
  257. if (n < 3)
  258. return n;
  259. is_loop = 0;
  260. /* is it loop ? */
  261. if (Points->x[0] == Points->x[n - 1] &&
  262. Points->y[0] == Points->y[n - 1] &&
  263. (Points->z[0] == Points->z[n - 1] || with_z == 0) && loop_support){
  264. is_loop = 1;
  265. }
  266. thresh *= thresh;
  267. head.next = NULL;
  268. cur = &head;
  269. if (!is_loop) {
  270. /* always keep first point */
  271. point_assign(Points, 0, with_z, &p0, 0);
  272. }
  273. else {
  274. point_assign(Points, 0, with_z, &p1, 0);
  275. point_assign(Points, 1, with_z, &p2, 0);
  276. point_add(p1, p2, &tmp);
  277. point_scalar(tmp, 0.5, &p0);
  278. }
  279. point_list_add(cur, p0);
  280. cur = cur->next;
  281. for (i = 2; i <= n; i++) {
  282. if (!is_loop) {
  283. if (i == n)
  284. point_assign(Points, i - 1, with_z, &p2, 0);
  285. else
  286. point_assign(Points, i, with_z, &p2, 0);
  287. }
  288. else
  289. point_assign(Points, i, with_z, &p2, 1);
  290. point_assign(Points, i - 1, with_z, &p1, 0);
  291. while (1) {
  292. point_add(p1, p2, &tmp);
  293. point_scalar(tmp, 0.5, &m1);
  294. point_list_add(cur, m1);
  295. if (point_dist_square(p0, m1) > thresh) {
  296. point_add(p1, m1, &tmp); /* need to refine the partition */
  297. point_scalar(tmp, 0.5, &p2);
  298. point_add(p1, p0, &tmp);
  299. point_scalar(tmp, 0.5, &p1);
  300. }
  301. else {
  302. break; /* good approximation */
  303. }
  304. }
  305. while (cur->next != NULL)
  306. cur = cur->next;
  307. p0 = cur->p;
  308. }
  309. if (!is_loop) {
  310. point_assign(Points, n - 1, with_z, &p0, 0);
  311. point_list_add(cur, p0); /* always keep last point */
  312. }
  313. if (point_list_copy_to_line_pnts(head, Points) == -1) {
  314. G_fatal_error(_("Out of memory"));
  315. }
  316. point_list_free(head);
  317. return Points->n_points;
  318. }
  319. /* use for refining tangent in hermite interpolation */
  320. void refine_tangent(POINT * p)
  321. {
  322. double l = point_dist2(*p);
  323. if (l < GRASS_EPSILON) {
  324. point_scalar(*p, 0.0, p);
  325. }
  326. else {
  327. point_scalar(*p, (double)1.0 / sqrt(sqrt(sqrt(l))), p);
  328. }
  329. return;
  330. }
  331. /* approximates given line using hermite cubic spline
  332. * interpolates by steps of length step
  333. * returns the number of point in result
  334. */
  335. int hermite(struct line_pnts *Points, double step, double angle_thresh,
  336. int loop_support, int with_z)
  337. {
  338. POINT_LIST head, *last, *point;
  339. POINT p0, p1, t0, t1, tmp, res;
  340. double length, next, length_begin, l;
  341. double s;
  342. double h1, h2, h3, h4;
  343. int n, i;
  344. int ni;
  345. int is_loop;
  346. n = Points->n_points;
  347. /* line is too short */
  348. if (n <= 2) {
  349. return n;
  350. }
  351. is_loop = 0;
  352. /* is it loop ? */
  353. if (Points->x[0] == Points->x[n - 1] &&
  354. Points->y[0] == Points->y[n - 1] &&
  355. (Points->z[0] == Points->z[n - 1] || with_z == 0) && loop_support){
  356. is_loop = 1;
  357. }
  358. /* convert degrees=>radians */
  359. angle_thresh *= M_PI / 180.0;
  360. head.next = NULL;
  361. point = last = &head;
  362. if (!is_loop) {
  363. point_assign(Points, 0, with_z, &p0, 0);
  364. point_assign(Points, 1, with_z, &p1, 0);
  365. /* length of line from point 0 to i+1 */
  366. length = point_dist(p0, p1);
  367. /* tangent at p0, p1 */
  368. point_subtract(p1, p0, &t0);
  369. refine_tangent(&t0);
  370. point_assign(Points, 2, with_z, &tmp, 0);
  371. point_subtract(tmp, p0, &t1);
  372. refine_tangent(&t1);
  373. }
  374. else {
  375. point_assign(Points, n - 2, with_z, &p0, 0);
  376. point_assign(Points, 0, with_z, &p1, 0);
  377. /* length of line from point n - 2 to 0 = n - 1 */
  378. length = point_dist(p0, p1);
  379. /* tangent at p0, p1 */
  380. point_assign(Points, 1, with_z, &tmp, 0);
  381. point_subtract(tmp, p0, &t0);
  382. refine_tangent(&t0);
  383. point_assign(Points, 0, with_z, &p0, 0);
  384. point_assign(Points, 1, with_z, &p1, 0);
  385. /* length of line from point 0 to 1 */
  386. length = point_dist(p0, p1);
  387. /* tangent at p0, p2 */
  388. point_assign(Points, 2, with_z, &tmp, 0);
  389. point_subtract(tmp, p0, &t1);
  390. refine_tangent(&t1);
  391. }
  392. /* length of line 0..i */
  393. length_begin = 0;
  394. next = 0;
  395. /* we always operate on the segment point[i]..point[i+1] */
  396. i = 0;
  397. while (i < n - 1) {
  398. if (next > length || (length - length_begin < GRASS_EPSILON)) { /* segmet i..i+1 is finished or too short */
  399. i++;
  400. if (i >= n - 1)
  401. break; /* we are already out of line */
  402. point_assign(Points, i, with_z, &p0, is_loop);
  403. point_assign(Points, i + 1, with_z, &p1, is_loop);
  404. length_begin = length;
  405. length += point_dist(p0, p1);
  406. ni = i + 2;
  407. if (!is_loop && ni > n - 1)
  408. ni = n - 1; /* ensure that we are in the line */
  409. t0 = t1;
  410. point_assign(Points, ni, with_z, &tmp, is_loop);
  411. point_subtract(tmp, p0, &t1);
  412. refine_tangent(&t1);
  413. }
  414. else {
  415. l = length - length_begin; /* length of actual segment */
  416. s = (next - length_begin) / l; /* 0<=s<=1 where we want to add new point on the line */
  417. /* then we need to calculate 4 control polynomials */
  418. h1 = 2 * s * s * s - 3 * s * s + 1;
  419. h2 = -2 * s * s * s + 3 * s * s;
  420. h3 = s * s * s - 2 * s * s + s;
  421. h4 = s * s * s - s * s;
  422. point_scalar(p0, h1, &res);
  423. point_scalar(p1, h2, &tmp);
  424. point_add(res, tmp, &res);
  425. point_scalar(t0, h3, &tmp);
  426. point_add(res, tmp, &res);
  427. point_scalar(t1, h4, &tmp);
  428. point_add(res, tmp, &res);
  429. point_list_add(last, res);
  430. last = last->next;
  431. next += step;
  432. }
  433. /* if the angle between 2 vectors is less then eps, remove the
  434. * middle point */
  435. if (point->next && point->next->next && point->next->next->next) {
  436. if (point_angle_between
  437. (point->next->p, point->next->next->p,
  438. point->next->next->next->p) < angle_thresh) {
  439. point_list_delete_next(point->next);
  440. }
  441. else
  442. point = point->next;
  443. }
  444. }
  445. point_assign(Points, n - 1, with_z, &p0, 0);
  446. point_list_add(last, p0);
  447. if (point_list_copy_to_line_pnts(head, Points) == -1)
  448. G_fatal_error(_("Out of memory"));
  449. point_list_free(head);
  450. return Points->n_points;
  451. }
  452. /* snakes algorithm for line simplification/generalization
  453. * returns the number of points in the output line. This is
  454. * always equal to the number of points in the original line
  455. *
  456. * alpha, beta are 2 parameters which change the behaviour of the algorithm
  457. *
  458. * TODO: Add parameter iterations, so the runnining time is O(N^3 * log iterations)
  459. * instead of O(N^3 * itearations). Probably not needed, for many iterations,
  460. * the result is almost straight line
  461. */
  462. int snakes(struct line_pnts *Points, double alpha, double beta,
  463. int loop_support, int with_z)
  464. {
  465. MATRIX g, ginv, xcoord, ycoord, zcoord, xout, yout, zout;
  466. int n = Points->n_points;
  467. int i, j;
  468. double x0, y0, z0;
  469. double a = 2.0 * alpha + 6.0 * beta;
  470. double b = -alpha - 4.0 * beta;
  471. double c = beta;
  472. double val[5] = { c, b, a, b, c };
  473. int plus = 4;
  474. int is_loop = 0;
  475. if (n < plus)
  476. return n;
  477. /* is it loop ? */
  478. if (Points->x[0] == Points->x[n - 1] &&
  479. Points->y[0] == Points->y[n - 1] &&
  480. (Points->z[0] == Points->z[n - 1] || with_z == 0) && loop_support){
  481. is_loop = 1;
  482. if (n < plus + 2)
  483. return n;
  484. }
  485. if (!matrix_init(n + 2 * plus, n + 2 * plus, &g)) {
  486. G_fatal_error(_("Out of memory"));
  487. return n;
  488. }
  489. if (!matrix_init(n + 2 * plus, 1, &xcoord)) {
  490. G_fatal_error(_("Out of memory"));
  491. return n;
  492. }
  493. if (!matrix_init(n + 2 * plus, 1, &ycoord)) {
  494. G_fatal_error(_("Out of memory"));
  495. return n;
  496. }
  497. if (!matrix_init(n + 2 * plus, 1, &zcoord)) {
  498. G_fatal_error(_("Out of memory"));
  499. return n;
  500. }
  501. if (!matrix_init(n + 2 * plus, 1, &xout)) {
  502. G_fatal_error(_("Out of memory"));
  503. return n;
  504. }
  505. if (!matrix_init(n + 2 * plus, 1, &yout)) {
  506. G_fatal_error(_("Out of memory"));
  507. return n;
  508. }
  509. if (!matrix_init(n + 2 * plus, 1, &zout)) {
  510. G_fatal_error(_("Out of memory"));
  511. return n;
  512. }
  513. x0 = Points->x[0];
  514. y0 = Points->y[0];
  515. z0 = Points->z[0];
  516. /* store the coordinates in the column vectors */
  517. for (i = 0; i < n; i++) {
  518. xcoord.a[i + plus][0] = Points->x[i] - x0;
  519. ycoord.a[i + plus][0] = Points->y[i] - y0;
  520. zcoord.a[i + plus][0] = Points->z[i] - z0;
  521. }
  522. if (!is_loop) {
  523. /* repeat first and last point at the beginning and end
  524. * of each vector respectively */
  525. for (i = 0; i < plus; i++) {
  526. xcoord.a[i][0] = 0;
  527. ycoord.a[i][0] = 0;
  528. zcoord.a[i][0] = 0;
  529. }
  530. for (i = n + plus; i < n + 2 * plus; i++) {
  531. xcoord.a[i][0] = Points->x[n - 1] - x0;
  532. ycoord.a[i][0] = Points->y[n - 1] - y0;
  533. zcoord.a[i][0] = Points->z[n - 1] - z0;
  534. }
  535. }
  536. else {
  537. /* loop: point 0 and point n - 1 are identical */
  538. /* repeat last and first points at the beginning and
  539. * end of each vector respectively */
  540. /* add points from n - plus - 1 to n - 2 */
  541. for (i = 0, j = n - plus - 1; i < plus; i++, j++) {
  542. xcoord.a[i][0] = Points->x[j] - x0;;
  543. ycoord.a[i][0] = Points->y[j] - y0;;
  544. zcoord.a[i][0] = Points->z[j] - z0;;
  545. }
  546. /* add points from 1 to plus + 1 */
  547. for (i = n + plus, j = 1; i < n + 2 * plus; i++, j++) {
  548. xcoord.a[i][0] = Points->x[j] - x0;
  549. ycoord.a[i][0] = Points->y[j] - y0;
  550. zcoord.a[i][0] = Points->z[j] - z0;
  551. }
  552. }
  553. /* calculate the matrix */
  554. for (i = 0; i < n + 2 * plus; i++)
  555. for (j = 0; j < n + 2 * plus; j++) {
  556. int index = j - i + 2;
  557. if (index >= 0 && index <= 4)
  558. g.a[i][j] = val[index];
  559. else
  560. g.a[i][j] = 0;
  561. }
  562. matrix_add_identity((double)1.0, &g);
  563. /* find its inverse */
  564. if (!matrix_inverse(&g, &ginv, 0)) {
  565. G_fatal_error(_("Unable to find the inverse matrix"));
  566. return n;
  567. }
  568. if (!matrix_mult(&ginv, &xcoord, &xout)
  569. || !matrix_mult(&ginv, &ycoord, &yout)
  570. || !matrix_mult(&ginv, &zcoord, &zout)) {
  571. G_fatal_error(_("Unable to calculate the output vectors"));
  572. return n;
  573. }
  574. if (!is_loop) {
  575. /* copy the new values of coordinates, but
  576. * never move the last and first point */
  577. for (i = 1; i < n - 1; i++) {
  578. Points->x[i] = xout.a[i + plus][0] + x0;
  579. Points->y[i] = yout.a[i + plus][0] + y0;
  580. if (with_z)
  581. Points->z[i] = zout.a[i + plus][0] + z0;
  582. }
  583. }
  584. else {
  585. /* copy the new values of coordinates,
  586. * also move the last and first point */
  587. for (i = 0; i < n; i++) {
  588. Points->x[i] = xout.a[i + plus][0] + x0;
  589. Points->y[i] = yout.a[i + plus][0] + y0;
  590. if (with_z)
  591. Points->z[i] = zout.a[i + plus][0] + z0;
  592. }
  593. Points->x[n - 1] = Points->x[0];
  594. Points->y[n - 1] = Points->y[0];
  595. Points->z[n - 1] = Points->z[0];
  596. }
  597. matrix_free(&g);
  598. matrix_free(&ginv);
  599. matrix_free(&xcoord);
  600. matrix_free(&ycoord);
  601. matrix_free(&zcoord);
  602. matrix_free(&xout);
  603. matrix_free(&yout);
  604. matrix_free(&zout);
  605. return Points->n_points;
  606. }