plate.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. /*
  2. ppedit - A pattern plate editor for Spiro splines.
  3. Copyright (C) 2007 Raph Levien
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301, USA.
  16. */
  17. #include <string.h>
  18. #include <math.h>
  19. #include <stdio.h>
  20. #include "zmisc.h"
  21. #include "sexp.h"
  22. #include "bezctx_intf.h"
  23. #include "bezctx_hittest.h"
  24. #include "cornu.h"
  25. #include "spiro.h"
  26. #include "plate.h"
  27. /* This is a global while we're playing with the tangent solver. Once we get that
  28. nailed down, it will go away. */
  29. extern int n_iter;
  30. /**
  31. * These are functions for editing a Cornu spline ("plate"), intended
  32. * to be somewhat independent of the UI toolkit specifics.
  33. **/
  34. plate *
  35. new_plate(void)
  36. {
  37. plate *p = znew(plate, 1);
  38. p->n_sp = 0;
  39. p->n_sp_max = 4;
  40. p->sp = znew(subpath, p->n_sp_max);
  41. p->mmode = MOUSE_MODE_ADD_CURVE;
  42. p->last_curve_mmode = p->mmode;
  43. return p;
  44. }
  45. void
  46. free_plate(plate *p)
  47. {
  48. int i;
  49. for (i = 0; i < p->n_sp; i++) {
  50. subpath *sp = &p->sp[i];
  51. zfree(sp->kt);
  52. }
  53. zfree(p->sp);
  54. zfree(p);
  55. }
  56. plate *
  57. copy_plate(const plate *p)
  58. {
  59. int i;
  60. plate *n = znew(plate, 1);
  61. n->n_sp = p->n_sp;
  62. n->n_sp_max = p->n_sp_max;
  63. n->sp = znew(subpath, n->n_sp_max);
  64. for (i = 0; i < n->n_sp; i++) {
  65. subpath *sp = &p->sp[i];
  66. subpath *nsp = &n->sp[i];
  67. nsp->n_kt = sp->n_kt;
  68. nsp->n_kt_max = sp->n_kt_max;
  69. nsp->kt = znew(knot, nsp->n_kt_max);
  70. memcpy(nsp->kt, sp->kt, nsp->n_kt * sizeof(knot));
  71. nsp->closed = sp->closed;
  72. }
  73. n->mmode = p->mmode;
  74. return n;
  75. }
  76. void
  77. plate_select_all(plate *p, int selected)
  78. {
  79. int i, j;
  80. /* find an existing point to select, if any */
  81. for (i = 0; i < p->n_sp; i++) {
  82. subpath *sp = &p->sp[i];
  83. for (j = 0; j < sp->n_kt; j++) {
  84. knot *kt = &sp->kt[j];
  85. kt->flags &= ~KT_SELECTED;
  86. if (selected)
  87. kt->flags |= KT_SELECTED;
  88. }
  89. }
  90. }
  91. subpath *
  92. plate_find_selected_sp(plate *p)
  93. {
  94. int i, j;
  95. /* find an existing point to select, if any */
  96. for (i = 0; i < p->n_sp; i++) {
  97. subpath *sp = &p->sp[i];
  98. for (j = 0; j < sp->n_kt; j++) {
  99. knot *kt = &sp->kt[j];
  100. if (kt->flags & KT_SELECTED)
  101. return sp;
  102. }
  103. }
  104. return NULL;
  105. }
  106. subpath *
  107. plate_new_sp(plate *p)
  108. {
  109. subpath *sp;
  110. if (p->n_sp == p->n_sp_max)
  111. p->sp = zrenew(subpath, p->sp, p->n_sp_max <<= 1);
  112. sp = &p->sp[p->n_sp++];
  113. sp->n_kt = 0;
  114. sp->n_kt_max = 4;
  115. sp->kt = znew(knot, sp->n_kt_max);
  116. sp->closed = 0;
  117. return sp;
  118. }
  119. static int
  120. try_close_sp(subpath *sp, int ix, int force)
  121. {
  122. int n_kt = sp->n_kt;
  123. if (sp->closed) return 0;
  124. if (n_kt < 3) return 0;
  125. if (!force) {
  126. if (ix != 0 && ix != n_kt - 1) return 0;
  127. if (!(sp->kt[n_kt - 1 - ix].flags & KT_SELECTED)) return 0;
  128. }
  129. sp->closed = 1;
  130. return 1;
  131. }
  132. void
  133. plate_press(plate *p, double x, double y, press_mod mods)
  134. {
  135. int i, j;
  136. subpath *sp;
  137. knot *kt;
  138. const double srad = 5;
  139. kt_flags new_kt_flags = KT_SELECTED;
  140. if (p->mmode == MOUSE_MODE_ADD_CORNER)
  141. new_kt_flags |= (mods & PRESS_MOD_CTRL) ? KT_OPEN : KT_CORNER;
  142. else if (p->mmode == MOUSE_MODE_ADD_CORNU)
  143. new_kt_flags |= (mods & PRESS_MOD_CTRL) ? KT_CORNER : KT_CORNU;
  144. else if (p->mmode == MOUSE_MODE_ADD_LEFT)
  145. new_kt_flags |= (mods & PRESS_MOD_CTRL) ? KT_CORNER : KT_LEFT;
  146. else if (p->mmode == MOUSE_MODE_ADD_RIGHT)
  147. new_kt_flags |= (mods & PRESS_MOD_CTRL) ? KT_CORNER : KT_RIGHT;
  148. else
  149. new_kt_flags |= (mods & PRESS_MOD_CTRL) ? KT_CORNER : KT_OPEN;
  150. p->x0 = x;
  151. p->y0 = y;
  152. /* find an existing point to select, if any */
  153. for (i = 0; i < p->n_sp; i++) {
  154. sp = &p->sp[i];
  155. for (j = 0; j < sp->n_kt; j++) {
  156. kt = &sp->kt[j];
  157. if (hypot(kt->x - x, kt->y - y) < srad) {
  158. int was_closed = try_close_sp(sp, j, mods & PRESS_MOD_DOUBLE);
  159. if (mods & PRESS_MOD_SHIFT) {
  160. kt->flags ^= KT_SELECTED;
  161. } else if (!(kt->flags & KT_SELECTED)) {
  162. plate_select_all(p, 0);
  163. kt->flags |= KT_SELECTED;
  164. }
  165. p->description = was_closed ? "Close Path" : NULL;
  166. p->motmode = MOTION_MODE_MOVE;
  167. return;
  168. }
  169. }
  170. }
  171. if (p->mmode == MOUSE_MODE_ADD_RIGHT || p->mmode == MOUSE_MODE_ADD_LEFT)
  172. p->mmode = p->last_curve_mmode;
  173. #if 1
  174. /* test whether the button press was on a curve; if so, insert point */
  175. for (i = 0; i < p->n_sp; i++) {
  176. bezctx *bc = new_bezctx_hittest(x, y);
  177. int knot_idx;
  178. sp = &p->sp[i];
  179. free_spiro(draw_subpath(sp, bc));
  180. if (bezctx_hittest_report(bc, &knot_idx) < srad) {
  181. knot *kt;
  182. if (sp->n_kt == sp->n_kt_max)
  183. sp->kt = zrenew(knot, sp->kt, sp->n_kt_max <<= 1);
  184. plate_select_all(p, 0);
  185. kt = &sp->kt[knot_idx + 1];
  186. memmove(&kt[1], kt, (sp->n_kt - knot_idx - 1) * sizeof(knot));
  187. sp->n_kt++;
  188. kt->x = x;
  189. kt->y = y;
  190. kt->flags = new_kt_flags;
  191. p->description = "Insert Point";
  192. p->motmode = MOTION_MODE_MOVE;
  193. return;
  194. }
  195. }
  196. #endif
  197. if (p->mmode == MOUSE_MODE_SELECT) {
  198. plate_select_all(p, 0);
  199. p->sel_x0 = x;
  200. p->sel_y0 = y;
  201. p->motmode = MOTION_MODE_SELECT;
  202. return;
  203. }
  204. sp = plate_find_selected_sp(p);
  205. if (sp == NULL || sp->closed) {
  206. sp = plate_new_sp(p);
  207. p->description = p->n_sp > 1 ? "New Subpath" : "New Path";
  208. }
  209. if (sp->n_kt == sp->n_kt_max)
  210. sp->kt = zrenew(knot, sp->kt, sp->n_kt_max <<= 1);
  211. plate_select_all(p, 0);
  212. kt = &sp->kt[sp->n_kt++];
  213. kt->x = x;
  214. kt->y = y;
  215. kt->flags = new_kt_flags;
  216. if (p->description == NULL)
  217. p->description = "Add Point";
  218. p->motmode = MOTION_MODE_MOVE;
  219. }
  220. void
  221. plate_motion_move(plate *p, double x, double y)
  222. {
  223. int i, j, n = 0;
  224. double dx, dy;
  225. dx = x - p->x0;
  226. dy = y - p->y0;
  227. p->x0 = x;
  228. p->y0 = y;
  229. for (i = 0; i < p->n_sp; i++) {
  230. subpath *sp = &p->sp[i];
  231. for (j = 0; j < sp->n_kt; j++) {
  232. knot *kt = &sp->kt[j];
  233. if (kt->flags & KT_SELECTED) {
  234. kt->x += dx;
  235. kt->y += dy;
  236. n++;
  237. }
  238. }
  239. }
  240. p->description = n == 1 ? "Move Point" : "Move Points";
  241. }
  242. void
  243. plate_motion_select(plate *p, double x1, double y1)
  244. {
  245. int i, j;
  246. double x0 = p->sel_x0;
  247. double y0 = p->sel_y0;
  248. #ifdef VERBOSE
  249. printf("plate_motion_select %g %g\n", x1, y1);
  250. #endif
  251. p->x0 = x1;
  252. p->y0 = y1;
  253. if (x0 > x1) {
  254. double tmp = x1;
  255. x1 = x0;
  256. x0 = tmp;
  257. }
  258. if (y0 > y1) {
  259. double tmp = y1;
  260. y1 = y0;
  261. y0 = tmp;
  262. }
  263. for (i = 0; i < p->n_sp; i++) {
  264. subpath *sp = &p->sp[i];
  265. for (j = 0; j < sp->n_kt; j++) {
  266. knot *kt = &sp->kt[j];
  267. kt->flags &= ~KT_SELECTED;
  268. if (kt->x >= x0 && kt->x <= x1 &&
  269. kt->y >= y0 && kt->y <= y1)
  270. kt->flags |= KT_SELECTED;
  271. }
  272. }
  273. }
  274. void plate_unpress(plate *p)
  275. {
  276. p->motmode = MOTION_MODE_IDLE;
  277. }
  278. void
  279. plate_toggle_corner(plate *p)
  280. {
  281. int i, j;
  282. /* find an existing point to select, if any */
  283. for (i = 0; i < p->n_sp; i++) {
  284. subpath *sp = &p->sp[i];
  285. for (j = 0; j < sp->n_kt; j++) {
  286. knot *kt = &sp->kt[j];
  287. if (kt->flags & KT_SELECTED) {
  288. if (kt->flags & KT_CORNER) {
  289. kt->flags |= KT_OPEN;
  290. kt->flags &= ~KT_CORNER;
  291. } else {
  292. kt->flags &= ~KT_OPEN;
  293. kt->flags |= KT_CORNER;
  294. }
  295. }
  296. }
  297. }
  298. }
  299. void
  300. plate_delete_pt(plate *p)
  301. {
  302. int i, j;
  303. /* find an existing point to select, if any */
  304. for (i = 0; i < p->n_sp; i++) {
  305. subpath *sp = &p->sp[i];
  306. for (j = 0; j < sp->n_kt; j++) {
  307. knot *kt = &sp->kt[j];
  308. if (kt->flags & KT_SELECTED) {
  309. memmove(kt, &kt[1], (sp->n_kt - j - 1) * sizeof(knot));
  310. sp->n_kt--;
  311. if (sp->n_kt < 3) sp->closed = 0;
  312. j--;
  313. }
  314. }
  315. }
  316. }
  317. /* Note: caller is responsible for freeing returned spiro_seg. */
  318. spiro_seg *
  319. draw_subpath(const subpath *sp, bezctx *bc)
  320. {
  321. int n = sp->n_kt;
  322. int i;
  323. spiro_cp *path;
  324. spiro_seg *s = NULL;
  325. if (n > 1) {
  326. path = znew(spiro_cp, n);
  327. for (i = 0; i < n; i++) {
  328. kt_flags flags = sp->kt[i].flags;
  329. path[i].x = sp->kt[i].x;
  330. path[i].y = sp->kt[i].y;
  331. path[i].ty = !sp->closed && i == 0 ? '{' :
  332. !sp->closed && i == n - 1 ? '}' :
  333. (flags & KT_OPEN) ? 'o' :
  334. (flags & KT_LEFT) ? '[' :
  335. (flags & KT_RIGHT) ? ']' :
  336. (flags & KT_CORNU) ? 'c' :
  337. 'v';
  338. }
  339. s = run_spiro(path, n);
  340. spiro_to_bpath(s, n, bc);
  341. zfree(path);
  342. }
  343. return s;
  344. }
  345. int
  346. file_write_plate(const char *fn, const plate *p)
  347. {
  348. FILE *f = fopen(fn, "w");
  349. int i, j;
  350. int st;
  351. if (f == NULL)
  352. return -1;
  353. st = fprintf(f, "(plate\n");
  354. for (i = 0; i < p->n_sp; i++) {
  355. subpath *sp = &p->sp[i];
  356. for (j = 0; j < sp->n_kt; j++) {
  357. kt_flags kf = sp->kt[j].flags;
  358. const char *cmd;
  359. if (kf & KT_OPEN) cmd = "o";
  360. else if (kf & KT_CORNER) cmd = "v";
  361. else if (kf & KT_CORNU) cmd = "c";
  362. else if (kf & KT_LEFT) cmd = "[";
  363. else if (kf & KT_RIGHT) cmd = "]";
  364. st = fprintf(f, " (%s %g %g)\n", cmd, sp->kt[j].x, sp->kt[j].y);
  365. if (st < 0) break;
  366. }
  367. if (st < 0) break;
  368. if (sp->closed) {
  369. st = fprintf(f, " (z)\n");
  370. }
  371. if (st < 0) break;
  372. }
  373. if (st >= 0)
  374. st = fprintf(f, ")\n");
  375. if (st >= 0)
  376. st = fclose(f);
  377. return st < 0 ? -1 : 0;
  378. }
  379. static int
  380. file_read_plate_inner(sexp_reader *sr, plate *p)
  381. {
  382. subpath *sp = NULL;
  383. sexp_token(sr);
  384. if (sr->singlechar != '(') return -1;
  385. sexp_token(sr);
  386. if (strcmp(sr->tokbuf, "plate")) return -1;
  387. for (;;) {
  388. sexp_token(sr);
  389. if (sr->singlechar == ')') break;
  390. else if (sr->singlechar == '(') {
  391. int cmd;
  392. sexp_token(sr);
  393. cmd = sr->singlechar;
  394. if (cmd == 'o' || cmd == 'v' || cmd == '[' || cmd == ']' ||
  395. cmd == 'c') {
  396. double x, y;
  397. knot *kt;
  398. sexp_token(sr);
  399. if (!sr->is_double) return -1;
  400. x = sr->d;
  401. sexp_token(sr);
  402. if (!sr->is_double) return -1;
  403. y = sr->d;
  404. sexp_token(sr);
  405. if (sr->singlechar != ')') return -1;
  406. if (sp == NULL || sp->closed)
  407. sp = plate_new_sp(p);
  408. if (sp->n_kt == sp->n_kt_max)
  409. sp->kt = zrenew(knot, sp->kt, sp->n_kt_max <<= 1);
  410. kt = &sp->kt[sp->n_kt++];
  411. kt->x = x;
  412. kt->y = y;
  413. switch (cmd) {
  414. case 'o':
  415. kt->flags = KT_OPEN;
  416. break;
  417. case '[':
  418. kt->flags = KT_LEFT;
  419. break;
  420. case ']':
  421. kt->flags = KT_RIGHT;
  422. break;
  423. case 'c':
  424. kt->flags = KT_CORNU;
  425. break;
  426. default:
  427. kt->flags = KT_CORNER;
  428. break;
  429. }
  430. } else if (cmd == 'z') {
  431. if (sp == NULL) return -1;
  432. sp->closed = 1;
  433. sexp_token(sr);
  434. if (sr->singlechar != ')') return -1;
  435. } else
  436. return -1;
  437. } else return -1;
  438. }
  439. return 0;
  440. }
  441. plate *
  442. file_read_plate(const char *fn)
  443. {
  444. FILE *f = fopen(fn, "r");
  445. plate *p;
  446. sexp_reader sr;
  447. if (f == NULL)
  448. return NULL;
  449. sr.f = f;
  450. p = new_plate();
  451. if (file_read_plate_inner(&sr, p)) {
  452. free_plate(p);
  453. p = NULL;
  454. }
  455. fclose(f);
  456. p->mmode = MOUSE_MODE_SELECT;
  457. p->motmode = MOTION_MODE_IDLE;
  458. return p;
  459. }