PageRenderTime 53ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/Pristine/V/A3D.C

http://github.com/AnimatorPro/Animator-Pro
C | 1619 lines | 1394 code | 126 blank | 99 comment | 187 complexity | dd85f53584d14adb22870ce5e89ca9a3 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /* a3d.c - This file contains most of the code for the optics section. */
  2. #include "jimk.h"
  3. #include "fli.h"
  4. #include "flicmenu.h"
  5. #include "a3d.h"
  6. #include "inks.h"
  7. #include "poly.h"
  8. #include "commonst.h"
  9. #include "a3d.str"
  10. extern char curveflag; /* draw points as polygon or curve? */
  11. extern int is_path; /* Use path or curve tension cont. bias? */
  12. extern Flicmenu a3d_menu; /* Optics panel menu */
  13. extern Vertex rot_theta; /* guy that gets directly dinked by xyz sliders */
  14. extern Poly working_poly; /* Where polygons always get loaded first */
  15. extern Pull root_pull; /* Base of pull-down tree */
  16. extern Pull presets_pull; /* 1st branch of optics pull-downs */
  17. extern WORD got_path; /* a3ddat.c lets us know here if there's a path */
  18. extern char inspin; /* a3ddat.c's flag if spin sub-panel is up */
  19. /* Variables to hold our graphic element (source for optics) */
  20. static Vcel *ado_cel; /* if it's a raster element */
  21. static Poly ado_spline1, ado_spline2; /* if it's a vector element */
  22. static Poly ado_path_poly; /* a place for the path in RAM */
  23. static Vertex *ado_vpoly; /* 3D element point list */
  24. static Point *ado_dpoly; /* 2D ready to render point list */
  25. static int ado_ptcount; /* How many points in above lists */
  26. /* is it a vector element or a raster element? */
  27. static
  28. is_vector()
  29. {
  30. return (vs.ado_source == OPS_SPLINE || vs.ado_source == OPS_POLY);
  31. }
  32. int imax(int a, int b)
  33. {
  34. if (a > b)
  35. return(a);
  36. else
  37. return(b);
  38. }
  39. /* Get RAM for all the buffers we need for the source side of
  40. the graphic element and also the path if there is one */
  41. static
  42. make_ado_poly()
  43. {
  44. char *name1, *name2;
  45. if (is_vector())
  46. {
  47. if (jexists(poly1_name) && jexists(poly2_name))
  48. {
  49. name2 = poly1_name;
  50. name1 = poly2_name;
  51. }
  52. else
  53. {
  54. name1 = name2 = poly_name;
  55. }
  56. if (!load_poly(name1))
  57. goto BADOUT;
  58. copy_structure(&working_poly, &ado_spline2, sizeof(ado_spline2) );
  59. zero_structure(&working_poly, sizeof(working_poly) );
  60. if (!load_poly(name2) )
  61. goto BADOUT;
  62. copy_structure(&working_poly, &ado_spline1, sizeof(ado_spline1) );
  63. zero_structure(&working_poly, sizeof(working_poly) );
  64. ado_ptcount = imax(ado_spline1.pt_count,ado_spline2.pt_count);
  65. }
  66. else
  67. ado_ptcount = 4;
  68. if ((ado_vpoly = begmem( sizeof(*ado_vpoly) * ado_ptcount)) == NULL)
  69. goto BADOUT;
  70. if ((ado_dpoly = begmem( sizeof( *ado_dpoly) * ado_ptcount)) == NULL)
  71. goto BADOUT;
  72. if (got_path)
  73. {
  74. if (!load_poly(ppoly_name) )
  75. goto BADOUT;
  76. copy_structure(&working_poly, &ado_path_poly, sizeof(ado_path_poly) );
  77. zero_structure(&working_poly, sizeof(working_poly) );
  78. }
  79. return(1);
  80. BADOUT:
  81. free_ado_poly();
  82. return(0);
  83. }
  84. /* Free up everything make_ado_poly() above took off the heap */
  85. static
  86. free_ado_poly()
  87. {
  88. gentle_freemem(ado_vpoly);
  89. ado_vpoly = NULL;
  90. gentle_freemem(ado_dpoly);
  91. ado_dpoly = NULL;
  92. poly_nopoints(&ado_spline1);
  93. poly_nopoints(&ado_spline2);
  94. poly_nopoints(&ado_path_poly);
  95. }
  96. /* 2 dimensional rotation */
  97. static
  98. partial_rot(theta, xx, yy)
  99. WORD theta;
  100. WORD *xx, *yy;
  101. {
  102. WORD s, c;
  103. WORD x, y;
  104. x = *xx;
  105. y = *yy;
  106. s = isin(theta);
  107. c = icos(theta);
  108. *xx = itmult(x,c) + itmult(y,s);
  109. *yy = itmult(y,c) + itmult(x,-s);
  110. }
  111. /* Do the yaw, pitch, and roll rotation to a 3-d point relative to
  112. wherever they've twisted the axis. */
  113. static
  114. act_rotate( point, op, scale)
  115. register struct vertex *point;
  116. register struct ado_setting *op;
  117. WORD scale;
  118. {
  119. register WORD theta;
  120. register WORD s,c;
  121. WORD x,y,z;
  122. point->x -= op->spin_center.x;
  123. point->y -= op->spin_center.y;
  124. point->z -= op->spin_center.z;
  125. theta = op->itheta1;
  126. if (theta)
  127. {
  128. partial_rot(theta, &point->x, &point->y);
  129. }
  130. theta = op->itheta2;
  131. if (theta)
  132. {
  133. partial_rot(theta, &point->y, &point->z);
  134. }
  135. theta = op->spin_theta.x;
  136. if (theta)
  137. {
  138. theta = itmult(theta, scale);
  139. partial_rot(theta, &point->x, &point->z);
  140. }
  141. theta = op->spin_theta.y;
  142. if (theta)
  143. {
  144. theta = itmult(theta, scale);
  145. partial_rot(theta, &point->y, &point->z);
  146. }
  147. theta = op->spin_theta.z;
  148. if (theta)
  149. {
  150. theta = itmult(theta, scale);
  151. partial_rot(theta, &point->x, &point->y);
  152. }
  153. theta = -op->itheta2;
  154. if (theta)
  155. {
  156. partial_rot(theta, &point->y, &point->z);
  157. }
  158. theta = -op->itheta1;
  159. if (theta)
  160. {
  161. partial_rot(theta, &point->x, &point->y);
  162. }
  163. point->x += op->spin_center.x;
  164. point->y += op->spin_center.y;
  165. point->z += op->spin_center.z;
  166. }
  167. /* Do the x, y, and both scaling to a single 3-d point */
  168. static
  169. act_size(point, op, scale)
  170. register struct vertex *point;
  171. register struct ado_setting *op;
  172. WORD scale;
  173. {
  174. int dif1, dif2;
  175. dif1 = point->x - op->size_center.x;
  176. dif2 = sscale_by(dif1, op->xp, op->xq);
  177. dif2 = sscale_by(dif2, op->bp, op->bq);
  178. point->x += itmult(dif2-dif1, scale);
  179. dif1 = point->y - op->size_center.y;
  180. dif2 = sscale_by(dif1, op->yp, op->yq);
  181. dif2 = sscale_by(dif2, op->bp, op->bq);
  182. point->y += itmult(dif2-dif1, scale);
  183. }
  184. /* Do the move (translation) part of optics transform to 1 point */
  185. static
  186. act_move(p, op, scale)
  187. WORD *p, *op, scale;
  188. {
  189. int i;
  190. i = 3;
  191. while (--i >= 0)
  192. *p++ += itmult(*op++, scale);
  193. }
  194. /* build a rectangular polygon fitting a cel */
  195. static
  196. sq_vpoly(c, dest)
  197. Vcel *c;
  198. register WORD *dest;
  199. {
  200. int x,y,w,h;
  201. x = c->x;
  202. y = c->y;
  203. w = c->w;
  204. h = c->h;
  205. w += x-1;
  206. h += y-1;
  207. *dest++ = x;
  208. *dest++ = y;
  209. *dest++ = 0;
  210. *dest++ = x;
  211. *dest++ = h;
  212. *dest++ = 0;
  213. *dest++ = w;
  214. *dest++ = h;
  215. *dest++ = 0;
  216. *dest++ = w;
  217. *dest++ = y;
  218. *dest++ = 0;
  219. }
  220. /* Give our fixed point multiply routine a better name */
  221. #define scale_mult itmult
  222. /* Put position along path defined by poly into delta_array */
  223. static
  224. calc_path_pos(poly, delta_array, scale, closed)
  225. Poly *poly;
  226. Vertex *delta_array;
  227. int scale;
  228. int closed;
  229. {
  230. #define TSHIFT 4
  231. WORD samp0, little_scale;
  232. LLpoint *samp_val0, *samp_val1;
  233. LLpoint *pt1;
  234. WORD samples, t0samp;
  235. int i;
  236. samples = poly->pt_count - 1 + closed;
  237. if (samples < 1)
  238. {
  239. delta_array->x = delta_array->y = delta_array->z = 0;
  240. return;
  241. }
  242. if (scale == SCALE_ONE)
  243. {
  244. samp0 = samples - 1;
  245. little_scale = SCALE_ONE;
  246. }
  247. else
  248. {
  249. samp0 = (long)scale * samples / SCALE_ONE;
  250. t0samp = (long)samp0 * SCALE_ONE / samples;
  251. little_scale = (scale - t0samp) * samples;
  252. }
  253. pt1 = samp_val0 = poly->clipped_list;
  254. i = samp0;
  255. while (--i >= 0)
  256. samp_val0 = samp_val0->next;
  257. samp_val1 = samp_val0->next;
  258. delta_array->x = ((scale_mult( samp_val1->x<<TSHIFT, little_scale)
  259. + scale_mult(samp_val0->x<<TSHIFT, SCALE_ONE - little_scale)
  260. +TSHIFT/2)>>TSHIFT)-pt1->x;
  261. delta_array->y = ((scale_mult( samp_val1->y<<TSHIFT, little_scale)
  262. + scale_mult(samp_val0->y<<TSHIFT, SCALE_ONE - little_scale)
  263. +TSHIFT/2)>>TSHIFT)-pt1->y;
  264. delta_array->z = ((scale_mult( samp_val1->z<<TSHIFT, little_scale)
  265. + scale_mult(samp_val0->z<<TSHIFT, SCALE_ONE - little_scale)
  266. +TSHIFT/2)>>TSHIFT)-pt1->z;
  267. #undef TSHIFT
  268. }
  269. /* Do one full optics transformation to a list of points */
  270. static
  271. move_vpoly(s,d,count,op,scale,path)
  272. Vertex *s, *d; /* It's ok for these to point to same list */
  273. int count; /* point count */
  274. struct ado_setting *op; /* the transformation */
  275. int scale; /* How far into this tranformation? 0 to SCALE_ONE */
  276. int path; /* We got a path to cope with too? */
  277. {
  278. Vertex path_point;
  279. Poly *pp;
  280. Poly sp_poly;
  281. int ok;
  282. if (path)
  283. {
  284. if (vs.ado_path == 0) /* splining... */
  285. {
  286. is_path = 1; /* tell spline to use path tension cont bias */
  287. ok = make_sp_poly(&ado_path_poly, &sp_poly, vs.pa_closed, 16);
  288. is_path = 0;
  289. if (!ok)
  290. return(0);
  291. calc_path_pos(&sp_poly, &path_point, scale, vs.pa_closed);
  292. freemem(sp_poly.clipped_list);
  293. }
  294. else
  295. calc_path_pos(&ado_path_poly, &path_point, scale, vs.pa_closed);
  296. }
  297. while (--count >= 0)
  298. {
  299. copy_structure(s,d,sizeof(*d) );
  300. act_size(d, op, scale);
  301. act_rotate( d, op, scale);
  302. act_move(d, &op->move, scale);
  303. if (path)
  304. {
  305. d->x += path_point.x;
  306. d->y += path_point.y;
  307. d->z += path_point.z;
  308. }
  309. s++;
  310. d++;
  311. }
  312. return(1);
  313. }
  314. #define TOOBIG 10000
  315. /* Transform 3-d pointlist into 2-d pointlist doing perspective
  316. calculations the cheap way */
  317. static
  318. calc_zpoly(s, d, count)
  319. register Vertex *s;
  320. register Point *d;
  321. int count;
  322. {
  323. int x, y, z;
  324. while (--count >= 0)
  325. {
  326. z = s->z + GROUND_Z;
  327. if (z < 1)
  328. return(0);
  329. x = d->x = sscale_by(s->x-XMAX/2, GROUND_Z, z) + XMAX/2;
  330. y = d->y = sscale_by(s->y-YMAX/2, GROUND_Z, z) + YMAX/2;
  331. if (x < -TOOBIG || x > TOOBIG || y < -TOOBIG || y > TOOBIG)
  332. return(0);
  333. d += 1;
  334. s += 1;
  335. }
  336. return(1);
  337. }
  338. otoobig()
  339. {
  340. continu_line(a3d_100 /* "Shape too big, sorry." */);
  341. }
  342. /* Take a 3-d poly and run it through transformation stack. Then
  343. put result through a perspective calculation to yield a 2-D
  344. result. */
  345. static
  346. ado_transform(points, count, scale, dest)
  347. Vector *points;
  348. int count;
  349. int scale;
  350. Point *dest;
  351. {
  352. struct ado_setting *as;
  353. int path;
  354. as = &vs.move3;
  355. path = got_path;
  356. while (as != NULL)
  357. {
  358. move_vpoly(points, points, count, as, scale, path);
  359. path = 0; /* Just first guy gets a path */
  360. scale = SCALE_ONE;
  361. as = as->next;
  362. }
  363. if (!calc_zpoly(points, dest, count))
  364. {
  365. otoobig();
  366. return(0);
  367. }
  368. return(1);
  369. }
  370. /* figure out the center of graphic element */
  371. default_center(v)
  372. Vertex *v;
  373. {
  374. get_ado_cel();
  375. v->x = ado_cel->x + ado_cel->w/2;
  376. v->y = ado_cel->y + ado_cel->h/2;
  377. v->z = 0;
  378. if (is_vector())
  379. {
  380. if (make_ado_poly())
  381. {
  382. extern int pxmin, pxmax, pymin, pymax;
  383. find_pminmax(&ado_spline1);
  384. v->x = (pxmin + pxmax)/2;
  385. v->y = (pymin + pymax)/2;
  386. free_ado_poly();
  387. }
  388. }
  389. }
  390. /* Given two polys, generate a poly 'tween' the two. This is
  391. a convenient place to promote the 2-D poly to a 3-D one too. */
  392. xform_to_ado_poly(sp0, sp1, d, count, scale)
  393. Poly *sp0, *sp1;
  394. Vertex *d;
  395. int count, scale;
  396. {
  397. Vertex da0, da1;
  398. LLpoint *s0, *s1;
  399. int tween_scale;
  400. int count1 = count-!sp0->closed;
  401. int i;
  402. if (count <= 0)
  403. return;
  404. s0 = sp0->clipped_list;
  405. s1 = sp1->clipped_list;
  406. if (scale != 0 && scale != SCALE_ONE &&
  407. sp0->pt_count != sp1->pt_count && imax(sp0->pt_count,sp1->pt_count) > 6)
  408. /* do a tween where we generate intermediate points */
  409. {
  410. for (i=0; i<count; i++)
  411. {
  412. tween_scale = uscale_by(SCALE_ONE, i, count1);
  413. calc_path_pos(sp0, &da0, tween_scale, sp0->closed);
  414. /* path position is movement offset so add in 1st point position */
  415. da0.x += s0->x;
  416. da0.y += s0->y;
  417. da0.z += s0->z;
  418. calc_path_pos(sp1, &da1, tween_scale, sp0->closed);
  419. da1.x += s1->x;
  420. da1.y += s1->y;
  421. da1.z += s1->z;
  422. d->x = da0.x + itmult(da1.x - da0.x, scale);
  423. d->y = da0.y + itmult(da1.y - da0.y, scale);
  424. d->z = da0.z + itmult(da1.z - da0.z, scale);
  425. d += 1;
  426. }
  427. }
  428. else
  429. {
  430. while (--count>=0)
  431. {
  432. d->x = s0->x + itmult(s1->x - s0->x, scale);
  433. d->y = s0->y + itmult(s1->y - s0->y, scale);
  434. d->z = 0;
  435. d += 1;
  436. s0 = s0->next;
  437. s1 = s1->next;
  438. }
  439. }
  440. }
  441. /* Presuming we've already done a make_ado_poly this routine is
  442. all you need to get the 2-D polygon in screen coordinates
  443. to map graphic element into. This result polygon is ado_dpoly. */
  444. static
  445. ado_calc_poly(form,scale)
  446. Vscreen *form;
  447. int scale;
  448. {
  449. /* If it's a vector source let's go tween it */
  450. if (vs.ado_source == OPS_SPLINE || vs.ado_source == OPS_POLY)
  451. {
  452. ado_spline1.closed = vs.closed_curve||vs.fillp;
  453. xform_to_ado_poly(&ado_spline1,&ado_spline2,ado_vpoly,ado_ptcount,scale);
  454. }
  455. /* If it's a raster source just make a rectangular polygon */
  456. else
  457. {
  458. sq_vpoly(form, ado_vpoly); /* get initial polygon fitting cel/frame */
  459. }
  460. return(ado_transform(ado_vpoly, ado_ptcount, scale, ado_dpoly));
  461. }
  462. /* This is the 'auto vec' to render optics on one frame */
  463. static
  464. twirl1(ix, frames,scale)
  465. int ix,frames,scale;
  466. {
  467. Vscreen *tf = NULL;
  468. int success = 0;
  469. /* ~~~ */
  470. switch (vs.ado_source)
  471. {
  472. case 0: /* screen */
  473. if (vs.draw_mode != I_OPAQUE)
  474. {
  475. if (!write_gulp(another_name, uf.p, 64000L) )
  476. goto BADEND;
  477. color_form(&uf,vs.inks[0]);
  478. }
  479. if ((tf = clone_screen(render_form)) == NULL)
  480. goto BADEND;
  481. color_form(render_form,vs.inks[0]);
  482. break;
  483. case 1: /* cel */
  484. if (!load_temp_cel())
  485. goto BADEND;
  486. tf = (Vscreen *)cel;
  487. if (need_fit_cel(cel))
  488. cfit_cel(cel, render_form->cmap);
  489. break;
  490. }
  491. if (!make_ado_poly())
  492. goto BADEND;
  493. if (!ado_calc_poly(tf, scale))
  494. goto BADEND;
  495. switch (vs.ado_source)
  496. {
  497. case OPS_SCREEN:
  498. case OPS_CEL:
  499. if (!raster_transform(tf, ado_dpoly, 2))
  500. {
  501. goto BADEND;
  502. }
  503. if (vs.ado_outline)
  504. {
  505. int omode;
  506. omode = vs.draw_mode;
  507. vs.draw_mode = 0;
  508. render_outline(ado_dpoly, ado_ptcount);
  509. vs.draw_mode = omode;
  510. }
  511. break;
  512. case OPS_POLY:
  513. case OPS_SPLINE:
  514. if (!rado_poly(ado_dpoly,
  515. tween_pt_count(&ado_spline1, &ado_spline2, scale), vs.fillp,
  516. vs.ado_source == OPS_SPLINE))
  517. {
  518. free_ado_poly();
  519. goto BADEND;
  520. }
  521. break;
  522. }
  523. switch (vs.ado_source)
  524. {
  525. case 0: /* screen source */
  526. free_screen(tf);
  527. tf = NULL;
  528. if (vs.draw_mode != I_OPAQUE)
  529. {
  530. if (!read_gulp(another_name, uf.p, 64000L))
  531. goto BADEND;
  532. jdelete(another_name);
  533. }
  534. break;
  535. case 1: /* cel source */
  536. free_cel(cel);
  537. cel = NULL;
  538. break;
  539. }
  540. free_ado_poly();
  541. success = 1;
  542. BADEND:
  543. switch (vs.ado_source)
  544. {
  545. case 0:
  546. free_screen(tf);
  547. break;
  548. case 1:
  549. free_cel(cel);
  550. cel = NULL;
  551. break;
  552. }
  553. return(success);
  554. }
  555. /* Transform one 0-TWOPI based ado-op angle into something we can
  556. display on a slider */
  557. iscale_theta()
  558. {
  559. rot_theta.x = rscale_by(vs.move3.spin_theta.x, vs.ado_turn, TWOPI);
  560. rot_theta.y = rscale_by(vs.move3.spin_theta.y, vs.ado_turn, TWOPI);
  561. rot_theta.z = rscale_by(vs.move3.spin_theta.z, vs.ado_turn, TWOPI);
  562. }
  563. /* Transform one turn slider into 0-TWOPI based angle. Copes with
  564. sliders being in degrees, 1/8 circles, 1/4 circle, etc. */
  565. static
  566. nscale_theta(s, d, offset)
  567. WORD *s, *d, offset;
  568. {
  569. d[offset] = rscale_by(s[offset], TWOPI, vs.ado_turn);
  570. }
  571. /* feelme for one of the optics x/y/z sliders */
  572. ado_xyz_slider(m)
  573. Flicmenu *m;
  574. {
  575. feel_qslider(m);
  576. if (inspin)
  577. nscale_theta(&rot_theta, &vs.move3.spin_theta, m->identity);
  578. }
  579. /* zero out an optics x/y/z slider. Usual response to right click over
  580. optics x/y/z slider */
  581. xyz_zero_sl(m)
  582. Flicmenu *m;
  583. {
  584. zero_sl(m);
  585. if (inspin)
  586. nscale_theta(&rot_theta, &vs.move3.spin_theta, m->identity);
  587. }
  588. /* Gnarly math I wrote for Aegis Animator and then tried to forget.
  589. Make a 'conjugacy' matrix to compensate for axis tilt. Ie
  590. we'll go ahead and do the op-rotation as if there were no
  591. axis tilt, but bracket both sides with another rotation and
  592. it's inverse. Given the axis this guy figures out what
  593. bracketing rotations are necessary. (Boy, it's a good thing
  594. I know Linear Algebra.) */
  595. static
  596. make_rot_op()
  597. {
  598. find_conjugates(&vs.move3);
  599. }
  600. /* The center's point list and place to put transformed 3d points, and
  601. place to put 2d points */
  602. static Vertex csvecs[4] = {{0, 0, 0}, {-36, 0, 0}, {0, -36, 0}, {0, 0, 36}};
  603. static Vertex cdvecs[4];
  604. static Point cdpts[4];
  605. /* Display center. */
  606. static
  607. dcenter(dotout,scale)
  608. Vector dotout; /* marqi it? erase it? */
  609. int scale;
  610. {
  611. register Vertex *pt;
  612. int i, theta;
  613. int sizer;
  614. sizer = (vs.ado_mode == ADO_SIZE);
  615. pt = (sizer ? &vs.move3.size_center : &vs.move3.spin_center);
  616. for (i=0; i<4; i++)
  617. {
  618. cdvecs[i].x = csvecs[i].x;
  619. cdvecs[i].y = csvecs[i].y;
  620. cdvecs[i].z = csvecs[i].z;
  621. if ((theta = vs.move3.itheta1) != 0)
  622. partial_rot(theta, &cdvecs[i].x, &cdvecs[i].y);
  623. if ((theta = vs.move3.itheta2) != 0)
  624. partial_rot(theta, &cdvecs[i].y, &cdvecs[i].z);
  625. cdvecs[i].x += pt->x;
  626. cdvecs[i].y += pt->y;
  627. cdvecs[i].z += pt->z;
  628. }
  629. if (ado_transform(cdvecs, 4, scale, cdpts))
  630. {
  631. for (i=1; i<4; i++)
  632. cline(cdpts[0].x, cdpts[0].y, cdpts[i].x, cdpts[i].y, dotout);
  633. return(TRUE);
  634. }
  635. else
  636. return(FALSE);
  637. }
  638. tween_pt_count(Poly *p1, Poly *p2, int scale)
  639. {
  640. if (scale == 0)
  641. return(p1->pt_count);
  642. else if (scale == SCALE_ONE)
  643. return(p2->pt_count);
  644. else
  645. return(imax(p1->pt_count,p2->pt_count));
  646. }
  647. /* See marqi'd wireframe and center */
  648. static
  649. see_ado_poly(scale)
  650. int scale;
  651. {
  652. int pct;
  653. pct = (is_vector() ? tween_pt_count(&ado_spline1, &ado_spline2, scale) : 4);
  654. marqidata.mod = 0;
  655. if (!dcenter(marqidot, scale))
  656. return(FALSE);
  657. msome_vector(ado_dpoly,
  658. pct, marqidot, &marqidata,
  659. is_vector() && !is_closedp());
  660. return(TRUE);
  661. }
  662. /* Go do a wire-frame simulation of what ado move will look like
  663. so user can get a sense of what the timing will be before
  664. he goes to the pixel perfect (and slow) preview or even
  665. (gasp) to render it. */
  666. static
  667. ado_preview()
  668. {
  669. int occolor;
  670. int i;
  671. long clock;
  672. Point *lado_dpoly;
  673. int scale, lscale;
  674. make_ado_poly();
  675. if ((lado_dpoly = begmem(sizeof(Point)*ado_ptcount)) == NULL)
  676. {
  677. free_ado_poly();
  678. return;
  679. }
  680. mouse_on = 0;
  681. save_undo();
  682. make_rot_op();
  683. occolor = vs.ccolor;
  684. vs.ccolor = sbright;
  685. find_range();
  686. get_ado_cel();
  687. clock = get80Hz();
  688. /* ~~~ */
  689. for (i=0; i<tr_frames; i++)
  690. {
  691. scale = calc_time_scale(i, tr_frames);
  692. if (!ado_calc_poly(ado_cel,scale))
  693. break;
  694. if (i != 0)
  695. {
  696. undo_poly(lado_dpoly, ado_ptcount);
  697. if (!dcenter(copydot, lscale))
  698. break;
  699. }
  700. see_ado_poly(scale);
  701. copy_structure(ado_dpoly, lado_dpoly, sizeof(*lado_dpoly)*ado_ptcount );
  702. clock += fhead.speed;
  703. c_input();
  704. while (clock > get80hz())
  705. wait_sync();
  706. if (clock < get80hz())
  707. clock = get80hz();
  708. if (key_hit || RJSTDN)
  709. break;
  710. lscale = scale;
  711. }
  712. free_ado_poly();
  713. freemem(lado_dpoly);
  714. unundo();
  715. vs.ccolor = occolor;
  716. mouse_on = 1;
  717. return;
  718. }
  719. /* So many ways to exit from ado_preview it's easier to do the
  720. necessary menu hiding and restoring here in a little bracketing
  721. routine */
  722. mado_preview()
  723. {
  724. hide_mp();
  725. ado_preview();
  726. draw_mp();
  727. }
  728. /* the response to the 'do it' button */
  729. mauto_ado()
  730. {
  731. int omulti;
  732. hide_mp();
  733. omulti = vs.multi;
  734. vs.multi = 1;
  735. make_rot_op();
  736. if (push_inks())
  737. {
  738. if (push_cel())
  739. {
  740. /* ~~~ */
  741. doauto(twirl1);
  742. pop_cel();
  743. }
  744. else
  745. goto NOTEMP;
  746. pop_inks();
  747. }
  748. else
  749. goto NOTEMP;
  750. vs.multi = omulti;
  751. draw_mp();
  752. return;
  753. NOTEMP:
  754. noroom();
  755. draw_mp();
  756. }
  757. /* Clear top of transformation stack */
  758. static
  759. ado_clear()
  760. {
  761. a3d_disables();
  762. copy_structure(&default_vs.move3, &vs.move3, sizeof(vs.move3) );
  763. rot_theta.x = rot_theta.y = rot_theta.z = 0;
  764. default_center(&vs.move3.spin_center);
  765. copy_structure(&vs.move3.spin_center, &vs.move3.size_center,
  766. sizeof(&vs.move3.size_center));
  767. free_list((Name_list *)vs.move3.next);
  768. vs.move3.next = NULL;
  769. }
  770. /* Clear all optics motion */
  771. static
  772. ado_clear_p()
  773. {
  774. ado_clear();
  775. jdelete(ppoly_name);
  776. a3d_disables();
  777. }
  778. /* Set up optics for simple spin about horizontal axis */
  779. static
  780. auto_twirl()
  781. {
  782. ado_clear_p();
  783. vs.move3.spin_theta.x = TWOPI;
  784. }
  785. /* Set up optics for simple spin in plane */
  786. static
  787. auto_spin()
  788. {
  789. ado_clear_p();
  790. vs.move3.spin_theta.z = TWOPI;
  791. }
  792. /* set up optics for simple spin about vertical axis */
  793. static
  794. auto_whirl()
  795. {
  796. ado_clear_p();
  797. vs.move3.spin_theta.y = TWOPI;
  798. }
  799. /* Set move to a spin in plane while moving back */
  800. static
  801. auto_spin_small()
  802. {
  803. ado_clear_p();
  804. vs.move3.move.z = 500;
  805. vs.move3.spin_theta.z = TWOPI;
  806. }
  807. /* Move straight back */
  808. static
  809. auto_pull_back()
  810. {
  811. ado_clear_p();
  812. vs.move3.move.z = 500;
  813. }
  814. /* preset motion for stretch horizontally while squishing vertically */
  815. static
  816. auto_squash()
  817. {
  818. ado_clear_p();
  819. vs.move3.xp = 100;
  820. vs.move3.xq = 50;
  821. vs.move3.yp = 50;
  822. vs.move3.yq = 100;
  823. vs.move3.size_center.y = YMAX-1;
  824. }
  825. #define CLK_RAD 24
  826. /* Draw a ray for a clock to help user tell where he is during real-time
  827. sampled path */
  828. static
  829. clock_line(theta, dotout)
  830. int theta;
  831. Vector dotout;
  832. {
  833. Point clk;
  834. polar(theta-TWOPI/4, CLK_RAD, &clk);
  835. cline(XMAX/2, CLK_RAD, XMAX/2+clk.x, CLK_RAD+clk.y, dotout);
  836. }
  837. extern LLpoint *start_polyt(), *poly_add_point();
  838. /* Gather a sampled path from user mouse move */
  839. static
  840. sample_path(delay, maxpts, clock)
  841. int delay, maxpts,clock;
  842. {
  843. register LLpoint *this;
  844. int i, theta;
  845. working_poly.closed = 1;
  846. if ((this = start_polyt()) == NULL)
  847. return(0);
  848. if (clock)
  849. clock_line(0, sdot);
  850. for (i=0; i<maxpts; i++)
  851. {
  852. if (PDN)
  853. {
  854. if ((this = poly_add_point()) == NULL)
  855. goto OUT;
  856. sdot(this->x, this->y);
  857. }
  858. else
  859. break;
  860. theta = rscale_by(TWOPI, i, maxpts);
  861. if (clock)
  862. clock_line(theta, sdot);
  863. timed_input(delay);
  864. if (clock)
  865. clock_line(theta, copydot);
  866. }
  867. OUT:
  868. unundo();
  869. return(1);
  870. }
  871. /* try to calculate and then draw transformed poly */
  872. static
  873. calc_see_ado_poly(form, scale)
  874. Vscreen *form; /* source cel if any. */
  875. int scale;
  876. {
  877. if (ado_calc_poly(form,scale))
  878. {
  879. see_ado_poly(scale);
  880. return(1);
  881. }
  882. else
  883. return(0);
  884. }
  885. /* Response to pendown over drawing area when path sub-panel is
  886. being displayed */
  887. static
  888. make_path()
  889. {
  890. extern int tr_frames;
  891. int ok;
  892. if (!push_screen())
  893. {
  894. noroom();
  895. return;
  896. }
  897. make_ado_poly();
  898. make_rot_op();
  899. get_ado_cel();
  900. ok = calc_see_ado_poly(ado_cel,SCALE_ONE);
  901. free_ado_poly();
  902. if (ok)
  903. {
  904. save_undo();
  905. wait_click();
  906. if (PJSTDN)
  907. {
  908. curveflag = (vs.ado_path == PATH_SPLINE);
  909. switch (vs.ado_path)
  910. {
  911. case PATH_SPLINE:
  912. case PATH_POLY:
  913. is_path = 1;
  914. make_poly();
  915. is_path = 0;
  916. break;
  917. case PATH_SAMPLED:
  918. sample_path(5, 1024, 0);
  919. break;
  920. case PATH_CLOCKED:
  921. find_range();
  922. sample_path(fhead.speed, tr_frames, 1);
  923. break;
  924. }
  925. curveflag = 0;
  926. save_poly(ppoly_name, &working_poly);
  927. poly_nopoints(&working_poly);
  928. a3d_disables();
  929. }
  930. }
  931. pop_screen();
  932. save_undo();
  933. }
  934. /* keep sizing sliders in reasonable range */
  935. static
  936. check_prop(pq)
  937. WORD *pq;
  938. {
  939. if (pq[0] > 100)
  940. {
  941. pq[1] = rscale_by(pq[1], 100, pq[0]);
  942. pq[0] = 100;
  943. }
  944. }
  945. /* Response to click over drawing area. Lets user spin things around
  946. stretch, scale, etc. by moving around mouse instead of poking numbers
  947. into sliders. */
  948. static
  949. mouse_move()
  950. {
  951. int lastx, lasty, dx, dy;
  952. int opong, oease;
  953. Vertex *a3d_vertex;
  954. WORD *xvertex, *yvertex;
  955. struct ado_setting oset;
  956. WORD *xplace, *yplace;
  957. WORD use_y, use_x, mouse_xyz;
  958. WORD pixelfac;
  959. int xp, yp, zp;
  960. int remake_op;
  961. int calc_ok;
  962. remake_op = 0;
  963. if (vs.ado_mode == ADO_PATH)
  964. {
  965. make_path();
  966. return;
  967. }
  968. get_ado_cel();
  969. use_y = (vs.ado_mouse < 3);
  970. use_x = 1;
  971. mouse_xyz = 0;
  972. pixelfac = 1;
  973. make_ado_poly();
  974. switch (vs.ado_mode)
  975. {
  976. case ADO_SPIN:
  977. mouse_xyz = 1;
  978. switch (vs.ado_spin)
  979. {
  980. case SPIN_CENTER:
  981. a3d_vertex = &vs.move3.spin_center;
  982. break;
  983. case SPIN_AXIS:
  984. a3d_vertex = &vs.move3.spin_axis;
  985. remake_op = 1;
  986. break;
  987. case SPIN_TURNS:
  988. a3d_vertex = &vs.move3.spin_theta;
  989. pixelfac = 10;
  990. break;
  991. }
  992. break;
  993. case ADO_MOVE:
  994. a3d_vertex = &vs.move3.move;
  995. mouse_xyz = 1;
  996. break;
  997. case ADO_SIZE:
  998. pixelfac = 1;
  999. if (vs.ado_size == 0) /* center */
  1000. {
  1001. mouse_xyz = 1;
  1002. a3d_vertex = &vs.move3.size_center;
  1003. }
  1004. else
  1005. {
  1006. switch (vs.ado_szmouse)
  1007. {
  1008. case 0: /* Proportional */
  1009. xvertex = &vs.move3.bp;
  1010. use_x = 1;
  1011. use_y = 0;
  1012. break;
  1013. case 1: /* XY */
  1014. xvertex = &vs.move3.xp;
  1015. yvertex = &vs.move3.yp;
  1016. use_x = 1;
  1017. use_y = 1;
  1018. break;
  1019. case 2: /* X */
  1020. xvertex = &vs.move3.xp;
  1021. use_x = 1;
  1022. use_y = 0;
  1023. break;
  1024. case 3: /* Y */
  1025. yvertex = &vs.move3.yp;
  1026. use_x = 0;
  1027. use_y = 1;
  1028. break;
  1029. }
  1030. }
  1031. break;
  1032. }
  1033. if (mouse_xyz)
  1034. {
  1035. xvertex = yvertex = (WORD *)a3d_vertex;
  1036. #define XPOS 0
  1037. #define YPOS 1
  1038. #define ZPOS 2
  1039. /* Spinning around x axis want cursor to move ywards, so have
  1040. the following... */
  1041. if (vs.ado_mode == ADO_SPIN && vs.ado_spin == SPIN_TURNS)
  1042. {
  1043. switch (vs.ado_mouse)
  1044. {
  1045. case 0: /* XY */
  1046. xvertex += YPOS;
  1047. yvertex += XPOS;
  1048. break;
  1049. case 1: /* XZ */
  1050. yvertex += XPOS;
  1051. xvertex += ZPOS;
  1052. break;
  1053. case 2: /* ZY */
  1054. yvertex += ZPOS;
  1055. xvertex += YPOS;
  1056. break;
  1057. case 3: /* Z */
  1058. xvertex += ZPOS;
  1059. break;
  1060. case 4: /* Y */
  1061. xvertex += YPOS;
  1062. break;
  1063. case 5: /* X */
  1064. yvertex += XPOS;
  1065. use_x = 0;
  1066. use_y = 1;
  1067. break;
  1068. }
  1069. }
  1070. else
  1071. {
  1072. switch (vs.ado_mouse)
  1073. {
  1074. case 0: /* XY */
  1075. xvertex += XPOS;
  1076. yvertex += YPOS;
  1077. break;
  1078. case 1: /* XZ */
  1079. xvertex += XPOS;
  1080. yvertex += ZPOS;
  1081. break;
  1082. case 2: /* ZY */
  1083. xvertex += ZPOS;
  1084. yvertex += YPOS;
  1085. break;
  1086. case 3: /* Z */
  1087. xvertex += ZPOS;
  1088. break;
  1089. case 4: /* Y */
  1090. yvertex += YPOS;
  1091. use_x = 0;
  1092. use_y = 1;
  1093. break;
  1094. case 5: /* X */
  1095. xvertex += XPOS;
  1096. break;
  1097. }
  1098. }
  1099. }
  1100. copy_form(render_form, &uf);
  1101. make_rot_op();
  1102. if (!ado_calc_poly(ado_cel,SCALE_ONE))
  1103. goto OUT;
  1104. see_ado_poly(SCALE_ONE);
  1105. wait_click();
  1106. if (!PJSTDN)
  1107. goto OUT;
  1108. /* Save a copy of old way in case they cancel... */
  1109. copy_structure(&vs.move3, &oset, sizeof(oset) );
  1110. opong = vs.ado_pong;
  1111. oease = vs.ado_ease;
  1112. vs.ado_pong = vs.ado_ease = 0;
  1113. /* I'm fooling the compiler. Conceivably could break on a different compiler
  1114. on a processor with a wide bus especially. Structure allignment dependency.
  1115. */
  1116. lastx = grid_x;
  1117. lasty = grid_y;
  1118. for (;;)
  1119. {
  1120. dx = grid_x - lastx;
  1121. dy = grid_y - lasty;
  1122. lastx = grid_x;
  1123. lasty = grid_y;
  1124. if (!dcenter(copydot, SCALE_ONE))
  1125. {
  1126. copy_structure(&oset, &vs.move3, sizeof(oset) ); /* restore old way */
  1127. break;
  1128. }
  1129. undo_poly(ado_dpoly, ado_ptcount);
  1130. if (use_x)
  1131. *xvertex += dx*pixelfac;
  1132. if (use_y)
  1133. *yvertex += dy*pixelfac;
  1134. if (!ado_calc_poly(ado_cel,SCALE_ONE))
  1135. {
  1136. copy_structure(&oset, &vs.move3, sizeof(oset) ); /* restore old way */
  1137. break;
  1138. }
  1139. if (remake_op)
  1140. make_rot_op();
  1141. see_ado_poly(SCALE_ONE);
  1142. wait_input();
  1143. if (key_hit || RJSTDN)
  1144. {
  1145. copy_structure(&oset, &vs.move3, sizeof(oset) ); /* restore old way */
  1146. break;
  1147. }
  1148. if (PJSTDN)
  1149. break;
  1150. }
  1151. vs.ado_ease = oease;
  1152. vs.ado_pong = opong;
  1153. if (vs.ado_mode == ADO_SIZE)
  1154. {
  1155. check_prop(&vs.move3.bp);
  1156. check_prop(&vs.move3.xp);
  1157. check_prop(&vs.move3.yp);
  1158. }
  1159. OUT:
  1160. unundo();
  1161. free_ado_poly();
  1162. }
  1163. /* Hide menus and then go move things around with the mouse above */
  1164. static
  1165. mmouser()
  1166. {
  1167. hide_mp();
  1168. mouse_move();
  1169. draw_mp();
  1170. }
  1171. /* What do we do in response to a pull-down selection? */
  1172. static
  1173. ado_selit(menu, sel)
  1174. int menu, sel;
  1175. {
  1176. char *bufs[4];
  1177. char buf1[16], buf2[16];
  1178. hide_mp();
  1179. switch(menu)
  1180. {
  1181. case 0:
  1182. switch (sel)
  1183. {
  1184. case 0:
  1185. ado_clear_p();
  1186. break;
  1187. case 1:
  1188. auto_pull_back();
  1189. break;
  1190. case 2:
  1191. auto_spin();
  1192. break;
  1193. case 3:
  1194. auto_twirl();
  1195. break;
  1196. case 4:
  1197. auto_whirl();
  1198. break;
  1199. case 5:
  1200. auto_spin_small();
  1201. break;
  1202. case 6:
  1203. auto_squash();
  1204. break;
  1205. case 7:
  1206. go_files(8);
  1207. a3d_disables();
  1208. break;
  1209. }
  1210. break;
  1211. case 1:
  1212. switch (sel)
  1213. {
  1214. case 0: /* ease */
  1215. vs.ado_ease = !vs.ado_ease;
  1216. break;
  1217. case 1: /* ease out */
  1218. vs.ado_ease_out = !vs.ado_ease_out;
  1219. break;
  1220. case 2:
  1221. vs.ado_tween = !vs.ado_tween;
  1222. break;
  1223. case 3:
  1224. vs.ado_pong = !vs.ado_pong;
  1225. break;
  1226. case 4:
  1227. vs.ado_reverse = !vs.ado_reverse;
  1228. break;
  1229. case 5:
  1230. vs.ado_complete = !vs.ado_complete;
  1231. break;
  1232. }
  1233. break;
  1234. case 2:
  1235. switch (sel)
  1236. {
  1237. case 0: /* frame */
  1238. vs.ado_source = OPS_SCREEN;
  1239. break;
  1240. case 1: /* cel */
  1241. vs.ado_source = OPS_CEL;
  1242. break;
  1243. case 2:
  1244. vs.ado_source = OPS_POLY;
  1245. break;
  1246. case 3:
  1247. vs.ado_source = OPS_SPLINE;
  1248. break;
  1249. case 5: /* outline */
  1250. vs.ado_outline = !vs.ado_outline;
  1251. break;
  1252. }
  1253. break;
  1254. }
  1255. draw_mp();
  1256. }
  1257. /* one shot structure that's effectively the header to a .opt file */
  1258. struct magic_moves
  1259. {
  1260. WORD magic;
  1261. WORD moves;
  1262. };
  1263. /* Load up transformation stack from some file somebody must have liked
  1264. sometime... */
  1265. static
  1266. load_a3d(title)
  1267. char *title;
  1268. {
  1269. struct magic_moves mm;
  1270. int fd;
  1271. int i;
  1272. struct ado_setting *as;
  1273. if ((fd = jopen(title, 0)) == 0)
  1274. {
  1275. cant_find(title);
  1276. return(0);
  1277. }
  1278. ado_clear();
  1279. if (jread(fd, &mm, (long)sizeof(mm) ) != sizeof(mm) )
  1280. {
  1281. truncated(title);
  1282. goto BADOUT;
  1283. }
  1284. if (mm.magic != A3D_MAGIC)
  1285. {
  1286. continu_line(a3d_101 /* "Not a good OPTICS file" */);
  1287. goto BADOUT;
  1288. }
  1289. as = NULL;
  1290. i = mm.moves;
  1291. while (--i >= 0)
  1292. {
  1293. if (jread(fd, &vs.move3, sizeof(vs.move3) ) != sizeof(vs.move3) )
  1294. {
  1295. truncated(title);
  1296. goto BADOUT;
  1297. }
  1298. vs.move3.next = as;
  1299. if (i != 0)
  1300. {
  1301. do_move_along();
  1302. }
  1303. as = vs.move3.next;
  1304. }
  1305. jclose(fd);
  1306. return(1);
  1307. BADOUT:
  1308. jclose(fd);
  1309. return(0);
  1310. }
  1311. /* Try and save the transformation stack */
  1312. static
  1313. well_save_a3d(title)
  1314. char *title;
  1315. {
  1316. struct magic_moves mm;
  1317. int fd;
  1318. int i;
  1319. struct ado_setting *as;
  1320. mm.magic = A3D_MAGIC;
  1321. i = mm.moves = els_in_list(&vs.move3);
  1322. if ((fd = jcreate(title)) == 0)
  1323. {
  1324. cant_create(title);
  1325. return(0);
  1326. }
  1327. if (jwrite(fd, &mm, (long)sizeof(mm)) != sizeof(mm) )
  1328. {
  1329. truncated(title);
  1330. goto BADOUT;
  1331. }
  1332. while (--i >= 0)
  1333. {
  1334. as = list_el(&vs.move3, i);
  1335. if (jwrite(fd, as, sizeof(vs.move3) ) != sizeof(*as) )
  1336. {
  1337. truncated(title);
  1338. goto BADOUT;
  1339. }
  1340. }
  1341. jclose(fd);
  1342. return(1);
  1343. BADOUT:
  1344. jclose(fd);
  1345. return(0);
  1346. }
  1347. /* Save current transformation stack */
  1348. static
  1349. save_a3d(title)
  1350. char *title;
  1351. {
  1352. if (!well_save_a3d(title) )
  1353. {
  1354. jdelete(title); /* delete partial save */
  1355. return(0);
  1356. }
  1357. return(1);
  1358. }
  1359. /* Put up file requestor and then probably load up optics file */
  1360. qload_a3d()
  1361. {
  1362. char *title;
  1363. if ((title = get_filename(a3d_102 /* "Load Optics Move?" */,
  1364. ".OPT"))!=NULL)
  1365. {
  1366. load_a3d(title);
  1367. }
  1368. }
  1369. /* Put up file requestor and then probably save optics file. */
  1370. qsave_a3d()
  1371. {
  1372. char *title;
  1373. if ((title = get_filename(a3d_104 /* "Save Optics Move?" */,
  1374. ".OPT"))!=NULL)
  1375. {
  1376. if (overwrite_old(title))
  1377. save_a3d(title);
  1378. }
  1379. }
  1380. /* Point ado_cel to the right raster source */
  1381. static
  1382. get_ado_cel()
  1383. {
  1384. if (vs.ado_source == OPS_CEL)
  1385. ado_cel = cel;
  1386. else
  1387. ado_cel = (Vcel *)&vf;
  1388. }
  1389. /* just let em see the path without changing it */
  1390. mview_path()
  1391. {
  1392. hide_mp();
  1393. if (load_poly(ppoly_name))
  1394. {
  1395. save_undo();
  1396. curveflag = (vs.ado_path == 0);
  1397. is_path = 1;
  1398. rub_wpoly();
  1399. is_path = 0;
  1400. curveflag = 0;
  1401. wait_click();
  1402. unundo();
  1403. poly_nopoints(&working_poly);
  1404. }
  1405. draw_mp();
  1406. }
  1407. /* let 'em move around the points on an existing path */
  1408. edit_path()
  1409. {
  1410. Poly opoly;
  1411. hide_mp();
  1412. if (load_poly(ppoly_name) )
  1413. {
  1414. curveflag = (vs.ado_path == 0);
  1415. is_path = 1;
  1416. move_poly_points();
  1417. is_path = 0;
  1418. curveflag = 0;
  1419. save_poly(ppoly_name, &working_poly);
  1420. poly_nopoints(&working_poly);
  1421. }
  1422. draw_mp();
  1423. }
  1424. /* Duplicate top of transformation stack. */
  1425. static
  1426. do_move_along()
  1427. {
  1428. struct ado_setting *as;
  1429. make_rot_op();
  1430. if ((as = begmem(sizeof(*as)))==NULL)
  1431. return(0);
  1432. copy_structure(&vs.move3,as,sizeof(*as));
  1433. vs.move3.next = as;
  1434. return(1);
  1435. }
  1436. /* response to 'continue move' */
  1437. move_along(m)
  1438. Flicmenu *m;
  1439. {
  1440. struct ado_setting *as;
  1441. LLpoint *first, *last;
  1442. hilight(m);
  1443. if (do_move_along())
  1444. {
  1445. if (got_path) /* fold path into move op */
  1446. {
  1447. if (!vs.pa_closed)
  1448. {
  1449. if (load_poly(ppoly_name))
  1450. {
  1451. as = vs.move3.next;
  1452. first = working_poly.clipped_list;
  1453. last = list_el(first, working_poly.pt_count-1);
  1454. as->move.x += last->x - first->x;
  1455. as->move.y += last->y - first->y;
  1456. as->move.z += last->z - first->z;
  1457. poly_nopoints(&working_poly);
  1458. }
  1459. }
  1460. }
  1461. wait_a_jiffy(15);
  1462. }
  1463. draw_sel(m);
  1464. }
  1465. /* Lets go to the optics editor folks! */
  1466. go_ado()
  1467. {
  1468. Flicmenu *omenu;
  1469. Pull *orpc;
  1470. Pull *ocp;
  1471. ado_clear();
  1472. if (jexists(optics_name))
  1473. load_a3d(optics_name);
  1474. arrange_a3d_menu();
  1475. a3d_disables();
  1476. unzoom();
  1477. clip_tseg();
  1478. omenu = cur_menu; /* save old 'panel' (quick) menu */
  1479. cur_menu = &a3d_menu; /* and current panel menu */
  1480. ocp = cur_pull;
  1481. cur_pull = &root_pull;
  1482. orpc = root_pull.children;
  1483. root_pull.children = &presets_pull; /* go to ADO pulldowns */
  1484. a3d_pull_disables();
  1485. draw_mp();
  1486. sub_menu_loop(ado_selit,mmouser);
  1487. hide_mp();
  1488. cur_menu = omenu;
  1489. root_pull.children = orpc; /* and back to regular ones */
  1490. cur_pull = ocp;
  1491. save_a3d(optics_name);
  1492. rezoom();
  1493. }