File: common\zoom.c
1 /*
2 zoom.c - routines for zoombox manipulation and for panning
3
4 */
5
6 #include <string.h>
7
8 /* see Fractint.c for a description of the "include" hierarchy */
9 #include "port.h"
10 #include "prototyp.h"
11
12 #define PIXELROUND 0.00001
13
14 static void _fastcall zmo_calc(double, double, double *, double *, double);
15 static void _fastcall zmo_calcbf(bf_t,bf_t,bf_t,bf_t,bf_t,bf_t,bf_t,bf_t,bf_t);
16 static int check_pan(void);
17 static void fix_worklist(void);
18 static void _fastcall move_row(int fromrow,int torow,int col);
19
20 /* big number declarations */
21 void calc_corner(bf_t target,bf_t p1,double p2,bf_t p3,double p4,bf_t p5)
22 {
23 bf_t btmp1, btmp2 ,btmp3;
24 int saved; saved = save_stack();
25 btmp1 = alloc_stack(rbflength+2);
26 btmp2 = alloc_stack(rbflength+2);
27 btmp3 = alloc_stack(rbflength+2);
28
29 /* use target as temporary variable */
30 floattobf(btmp3, p2);
31 mult_bf(btmp1,btmp3,p3);
32 mult_bf(btmp2,floattobf(target, p4),p5);
33 add_bf(target,btmp1,btmp2);
34 add_a_bf(target,p1);
35 restore_stack(saved);
36 }
37
38 int boxcolor;
39
40 #ifndef XFRACT
41 void dispbox(void)
42 {
43 int i;
44 int boxc = (colors-1)&boxcolor;
45 unsigned char *values = (unsigned char *)boxvalues;
46 int rgb[3];
47 xorTARGA = 1;
48 for(i=0;i<boxcount;i++)
49 {
50 if(istruecolor && truemode)
51 {
52 gettruecolor(boxx[i]-sxoffs,boxy[i]-syoffs,&rgb[0],&rgb[1],&rgb[2]);
53 puttruecolor(boxx[i]-sxoffs,boxy[i]-syoffs,
54 rgb[0]^255,rgb[1]^255,rgb[2]^255);
55 }
56 else
57 values[i] = (unsigned char)getcolor(boxx[i]-sxoffs,boxy[i]-syoffs);
58 }
59 /* There is an interaction between getcolor and putcolor, so separate them */
60 if (!(istruecolor && truemode)) /* don't need this for truecolor with truemode set */
61 for(i=0;i<boxcount;i++)
62 {
63 if (colors == 2)
64 putcolor(boxx[i]-sxoffs,boxy[i]-syoffs,(1 - values[i]));
65 else
66 putcolor(boxx[i]-sxoffs,boxy[i]-syoffs,boxc);
67 }
68 xorTARGA = 0;
69 }
70
71 void clearbox(void)
72 {
73 int i;
74 xorTARGA = 1;
75 if(istruecolor && truemode)
76 {
77 dispbox();
78 }
79 else
80 {
81 unsigned char *values = (unsigned char *)boxvalues;
82 for(i=0;i<boxcount;i++)
83 {
84 putcolor(boxx[i]-sxoffs,boxy[i]-syoffs,values[i]);
85 }
86 }
87 xorTARGA = 0;
88 }
89 #endif
90
91 void drawbox(int drawit)
92 {
93 struct coords tl,bl,tr,br; /* dot addr of topleft, botleft, etc */
94 double tmpx,tmpy,dx,dy,rotcos,rotsin,ftemp1,ftemp2;
95 double fxwidth,fxskew,fydepth,fyskew,fxadj;
96 bf_t bffxwidth, bffxskew, bffydepth, bffyskew, bffxadj;
97 int saved=0;
98 if (zwidth==0) { /* no box to draw */
99 if (boxcount!=0) { /* remove the old box from display */
100 clearbox();
101 boxcount = 0; }
102 reset_zoom_corners();
103 return; }
104 if(bf_math)
105 {
106 saved = save_stack();
107 bffxwidth = alloc_stack(rbflength+2);
108 bffxskew = alloc_stack(rbflength+2);
109 bffydepth = alloc_stack(rbflength+2);
110 bffyskew = alloc_stack(rbflength+2);
111 bffxadj = alloc_stack(rbflength+2);
112 }
113 ftemp1 = PI*zrotate/72; /* convert to radians */
114 rotcos = cos(ftemp1); /* sin & cos of rotation */
115 rotsin = sin(ftemp1);
116
117 /* do some calcs just once here to reduce fp work a bit */
118 fxwidth = sxmax-sx3rd;
119 fxskew = sx3rd-sxmin;
120 fydepth = sy3rd-symax;
121 fyskew = symin-sy3rd;
122 fxadj = zwidth*zskew;
123
124 if(bf_math)
125 {
126 /* do some calcs just once here to reduce fp work a bit */
127 sub_bf(bffxwidth,bfsxmax,bfsx3rd);
128 sub_bf(bffxskew,bfsx3rd,bfsxmin);
129 sub_bf(bffydepth,bfsy3rd,bfsymax);
130 sub_bf(bffyskew,bfsymin,bfsy3rd);
131 floattobf(bffxadj, fxadj);
132 }
133
134 /* calc co-ords of topleft & botright corners of box */
135 tmpx = zwidth/-2+fxadj; /* from zoombox center as origin, on xdots scale */
136 tmpy = zdepth*finalaspectratio/2;
137 dx = (rotcos*tmpx - rotsin*tmpy) - tmpx; /* delta x to rotate topleft */
138 dy = tmpy - (rotsin*tmpx + rotcos*tmpy); /* delta y to rotate topleft */
139
140 /* calc co-ords of topleft */
141 ftemp1 = zbx + dx + fxadj;
142 ftemp2 = zby + dy/finalaspectratio;
143
144 tl.x = (int)(ftemp1*(dxsize+PIXELROUND)); /* screen co-ords */
145 tl.y = (int)(ftemp2*(dysize+PIXELROUND));
146 xxmin = sxmin + ftemp1*fxwidth + ftemp2*fxskew; /* real co-ords */
147 yymax = symax + ftemp2*fydepth + ftemp1*fyskew;
148 if(bf_math)
149 {
150 calc_corner(bfxmin,bfsxmin,ftemp1,bffxwidth,ftemp2,bffxskew);
151 calc_corner(bfymax,bfsymax,ftemp2,bffydepth,ftemp1,bffyskew);
152 }
153
154 /* calc co-ords of bottom right */
155 ftemp1 = zbx + zwidth - dx - fxadj;
156 ftemp2 = zby - dy/finalaspectratio + zdepth;
157 br.x = (int)(ftemp1*(dxsize+PIXELROUND));
158 br.y = (int)(ftemp2*(dysize+PIXELROUND));
159 xxmax = sxmin + ftemp1*fxwidth + ftemp2*fxskew;
160 yymin = symax + ftemp2*fydepth + ftemp1*fyskew;
161 if(bf_math)
162 {
163 calc_corner(bfxmax,bfsxmin,ftemp1,bffxwidth,ftemp2,bffxskew);
164 calc_corner(bfymin,bfsymax,ftemp2,bffydepth,ftemp1,bffyskew);
165 }
166 /* do the same for botleft & topright */
167 tmpx = zwidth/-2 - fxadj;
168 tmpy = 0.0-tmpy;
169 dx = (rotcos*tmpx - rotsin*tmpy) - tmpx;
170 dy = tmpy - (rotsin*tmpx + rotcos*tmpy);
171 ftemp1 = zbx + dx - fxadj;
172 ftemp2 = zby + dy/finalaspectratio + zdepth;
173 bl.x = (int)(ftemp1*(dxsize+PIXELROUND));
174 bl.y = (int)(ftemp2*(dysize+PIXELROUND));
175 xx3rd = sxmin + ftemp1*fxwidth + ftemp2*fxskew;
176 yy3rd = symax + ftemp2*fydepth + ftemp1*fyskew;
177 if(bf_math)
178 {
179 calc_corner(bfx3rd,bfsxmin,ftemp1,bffxwidth,ftemp2,bffxskew);
180 calc_corner(bfy3rd,bfsymax,ftemp2,bffydepth,ftemp1,bffyskew);
181 restore_stack(saved);
182 }
183 ftemp1 = zbx + zwidth - dx + fxadj;
184 ftemp2 = zby - dy/finalaspectratio;
185 tr.x = (int)(ftemp1*(dxsize+PIXELROUND));
186 tr.y = (int)(ftemp2*(dysize+PIXELROUND));
187
188 if (boxcount!=0) { /* remove the old box from display */
189 clearbox();
190 boxcount = 0; }
191
192 if (drawit) { /* caller wants box drawn as well as co-ords calc'd */
193 #ifndef XFRACT
194 /* build the list of zoom box pixels */
195 addbox(tl); addbox(tr); /* corner pixels */
196 addbox(bl); addbox(br);
197 drawlines(tl,tr,bl.x-tl.x,bl.y-tl.y); /* top & bottom lines */
198 drawlines(tl,bl,tr.x-tl.x,tr.y-tl.y); /* left & right lines */
209 #endif
210 dispbox();
211 }
212 }
213
214 void _fastcall drawlines(struct coords fr, struct coords to,
215 int dx, int dy)
216 { int xincr,yincr,ctr;
217 int altctr,altdec,altinc;
218 struct coords tmpp,line1,line2;
219
220 if (abs(to.x-fr.x) > abs(to.y-fr.y)) { /* delta.x > delta.y */
221 if (fr.x>to.x) { /* swap so from.x is < to.x */
222 tmpp = fr; fr = to; to = tmpp; }
223 xincr = (to.x-fr.x)*4/sxdots+1; /* do every 1st, 2nd, 3rd, or 4th dot */
224 ctr = (to.x-fr.x-1)/xincr;
225 altdec = abs(to.y-fr.y)*xincr;
226 altinc = to.x-fr.x;
227 altctr = altinc/2;
228 yincr = (to.y>fr.y)?1:-1;
229 line2.x = (line1.x = fr.x) + dx;
230 line2.y = (line1.y = fr.y) + dy;
231 while (--ctr>=0) {
232 line1.x += xincr;
233 line2.x += xincr;
234 altctr -= altdec;
235 while (altctr<0) {
236 altctr += altinc;
237 line1.y += yincr;
238 line2.y += yincr;
239 }
240 addbox(line1);
241 addbox(line2);
242 }
243 }
244
245 else { /* delta.y > delta.x */
246 if (fr.y>to.y) { /* swap so from.y is < to.y */
247 tmpp = fr; fr = to; to = tmpp; }
248 yincr = (to.y-fr.y)*4/sydots+1; /* do every 1st, 2nd, 3rd, or 4th dot */
249 ctr = (to.y-fr.y-1)/yincr;
250 altdec = abs(to.x-fr.x)*yincr;
251 altinc = to.y-fr.y;
252 altctr = altinc/2;
253 xincr = (to.x>fr.x) ? 1 : -1;
254 line2.x = (line1.x = fr.x) + dx;
255 line2.y = (line1.y = fr.y) + dy;
256 while (--ctr>=0) {
257 line1.y += yincr;
258 line2.y += yincr;
259 altctr -= altdec;
260 while (altctr<0) {
261 altctr += altinc;
262 line1.x += xincr;
263 line2.x += xincr;
264 }
265 addbox(line1);
266 addbox(line2);
267 }
268 }
269 }
270
271 void _fastcall addbox(struct coords point)
272 {
273 point.x += sxoffs;
274 point.y += syoffs;
275 if (point.x >= 0 && point.x < sxdots &&
276 point.y >= 0 && point.y < sydots) {
277 boxx[boxcount] = point.x;
278 boxy[boxcount] = point.y;
279 ++boxcount;
280 }
281 }
282
283 void moveboxf(double dx, double dy)
284 { int align,row,col;
285 align = check_pan();
286 if (dx!=0.0) {
287 if ((zbx += dx) + zwidth/2 < 0) /* center must stay onscreen */
288 zbx = zwidth/-2;
289 if (zbx + zwidth/2 > 1)
290 zbx = 1.0 - zwidth/2;
291 if (align != 0
292 && ((col = (int)(zbx*(dxsize+PIXELROUND))) & (align-1)) != 0) {
293 if (dx > 0) col += align;
294 col -= col & (align-1); /* adjust col to pass alignment */
295 zbx = (double)col/dxsize; }
296 }
297 if (dy!=0.0) {
298 if ((zby += dy) + zdepth/2 < 0)
299 zby = zdepth/-2;
300 if (zby + zdepth/2 > 1)
301 zby = 1.0 - zdepth/2;
302 if (align != 0
303 && ((row = (int)(zby*(dysize+PIXELROUND))) & (align-1)) != 0) {
304 if (dy > 0) row += align;
305 row -= row & (align-1);
306 zby = (double)row/dysize; }
307 }
308 #ifndef XFRACT
309 if (video_scroll != 0) { /* scroll screen center to the box center */
310 col = (int)((zbx + zwidth/2)*(dxsize + PIXELROUND)) + sxoffs;
311 row = (int)((zby + zdepth/2)*(dysize + PIXELROUND)) + syoffs;
312 switch (zscroll) {
313 case 0: /* fixed - screen center fixed to the zoombox center */
314 scroll_center(col,row);
315 break;
316 case 1: /* relaxed - as the zoombox center leaves the screen */
317 if ((col -= video_startx) > 0 && (col -= vesa_xres - 1) < 0)
318 col = 0;
319 if ((row -= video_starty) > 0 && (row -= vesa_yres - 1) < 0)
320 row = 0;
321 if (col != 0 || row != 0)
322 scroll_relative(col, row);
323 break;
324 }
325 }
326 #endif
327 }
328
329 static void _fastcall chgboxf(double dwidth, double ddepth)
330 {
331 if (zwidth+dwidth > 1)
332 dwidth = 1.0-zwidth;
333 if (zwidth+dwidth < 0.05)
334 dwidth = 0.05-zwidth;
335 zwidth += dwidth;
336 if (zdepth+ddepth > 1)
337 ddepth = 1.0-zdepth;
338 if (zdepth+ddepth < 0.05)
339 ddepth = 0.05-zdepth;
340 zdepth += ddepth;
341 moveboxf(dwidth/-2,ddepth/-2); /* keep it centered & check limits */
342 }
343
344 void resizebox(int steps)
345 {
346 double deltax,deltay;
347 if (zdepth*screenaspect > zwidth) { /* box larger on y axis */
348 deltay = steps * 0.036 / screenaspect;
349 deltax = zwidth * deltay / zdepth;
350 }
351 else { /* box larger on x axis */
352 deltax = steps * 0.036;
353 deltay = zdepth * deltax / zwidth;
354 }
355 chgboxf(deltax,deltay);
356 }
357
358 void chgboxi(int dw, int dd)
359 { /* change size by pixels */
360 chgboxf( (double)dw/dxsize, (double)dd/dysize );
361 }
362 #ifdef C6
363 #pragma optimize("e",off) /* MSC 6.00A messes up next rtn with "e" on */ 364 #endif
365
366 extern void show_three_bf();
367
368 static void _fastcall zmo_calcbf(bf_t bfdx, bf_t bfdy,
369 bf_t bfnewx, bf_t bfnewy,bf_t bfplotmx1, bf_t bfplotmx2, bf_t bfplotmy1,
370 bf_t bfplotmy2, bf_t bfftemp)
371 {
372 bf_t btmp1, btmp2, btmp3, btmp4, btempx, btempy ;
373 bf_t btmp2a, btmp4a;
374 int saved; saved = save_stack();
375
376 btmp1 = alloc_stack(rbflength+2);
377 btmp2 = alloc_stack(rbflength+2);
378 btmp3 = alloc_stack(rbflength+2);
379 btmp4 = alloc_stack(rbflength+2);
380 btmp2a = alloc_stack(rbflength+2);
381 btmp4a = alloc_stack(rbflength+2);
382 btempx = alloc_stack(rbflength+2);
383 btempy = alloc_stack(rbflength+2);
384
385 /* calc cur screen corner relative to zoombox, when zoombox co-ords
386 are taken as (0,0) topleft thru (1,1) bottom right */
387
388 /* tempx = dy * plotmx1 - dx * plotmx2; */
389 mult_bf(btmp1,bfdy,bfplotmx1);
390 mult_bf(btmp2,bfdx,bfplotmx2);
391 sub_bf(btempx,btmp1,btmp2);
392
393 /* tempy = dx * plotmy1 - dy * plotmy2; */
394 mult_bf(btmp1,bfdx,bfplotmy1);
395 mult_bf(btmp2,bfdy,bfplotmy2);
396 sub_bf(btempy,btmp1,btmp2);
397
398 /* calc new corner by extending from current screen corners */
399 /* *newx = sxmin + tempx*(sxmax-sx3rd)/ftemp + tempy*(sx3rd-sxmin)/ftemp; */
400 sub_bf(btmp1,bfsxmax,bfsx3rd);
401 mult_bf(btmp2,btempx,btmp1);
402 /* show_three_bf("fact1",btempx,"fact2",btmp1,"prod ",btmp2,70); */
403 div_bf(btmp2a,btmp2,bfftemp);
404 /* show_three_bf("num ",btmp2,"denom",bfftemp,"quot ",btmp2a,70); */
405 sub_bf(btmp3,bfsx3rd,bfsxmin);
406 mult_bf(btmp4,btempy,btmp3);
407 div_bf(btmp4a,btmp4,bfftemp);
408 add_bf(bfnewx,bfsxmin,btmp2a);
409 add_a_bf(bfnewx,btmp4a);
410
411 /* *newy = symax + tempy*(sy3rd-symax)/ftemp + tempx*(symin-sy3rd)/ftemp; */
412 sub_bf(btmp1,bfsy3rd,bfsymax);
413 mult_bf(btmp2,btempy,btmp1);
414 div_bf(btmp2a,btmp2,bfftemp);
415 sub_bf(btmp3,bfsymin,bfsy3rd);
416 mult_bf(btmp4,btempx,btmp3);
417 div_bf(btmp4a,btmp4,bfftemp);
418 add_bf(bfnewy,bfsymax,btmp2a);
419 add_a_bf(bfnewy,btmp4a);
420 restore_stack(saved);
421 }
422
423 static void _fastcall zmo_calc(double dx, double dy, double *newx, double *newy, double ftemp)
424 {
425 double tempx,tempy;
426 /* calc cur screen corner relative to zoombox, when zoombox co-ords
427 are taken as (0,0) topleft thru (1,1) bottom right */
428 tempx = dy * plotmx1 - dx * plotmx2;
429 tempy = dx * plotmy1 - dy * plotmy2;
430
431 /* calc new corner by extending from current screen corners */
432 *newx = sxmin + tempx*(sxmax-sx3rd)/ftemp + tempy*(sx3rd-sxmin)/ftemp;
433 *newy = symax + tempy*(sy3rd-symax)/ftemp + tempx*(symin-sy3rd)/ftemp;
434 }
435
436 void zoomoutbf(void) /* for ctl-enter, calc corners for zooming out */
437 {
438 /* (xxmin,yymax), etc, are already set to zoombox corners;
439 (sxmin,symax), etc, are still the screen's corners;
440 use the same logic as plot_orbit stuff to first calculate current screen
441 corners relative to the zoombox, as if the zoombox were a square with
442 upper left (0,0) and width/depth 1; ie calc the current screen corners
443 as if plotting them from the zoombox;
444 then extend these co-ords from current real screen corners to get
445 new actual corners
446 */
447 bf_t savbfxmin,savbfymax,bfftemp;
448 bf_t tmp1, tmp2, tmp3, tmp4, tmp5, tmp6,bfplotmx1,bfplotmx2,bfplotmy1,bfplotmy2;
449 int saved;
450 saved = save_stack();
451 savbfxmin = alloc_stack(rbflength+2);
452 savbfymax = alloc_stack(rbflength+2);
453 bfftemp = alloc_stack(rbflength+2);
454 tmp1 = alloc_stack(rbflength+2);
455 tmp2 = alloc_stack(rbflength+2);
456 tmp3 = alloc_stack(rbflength+2);
457 tmp4 = alloc_stack(rbflength+2);
458 tmp5 = alloc_stack(rbflength+2);
459 tmp6 = alloc_stack(rbflength+2);
460 bfplotmx1 = alloc_stack(rbflength+2);
461 bfplotmx2 = alloc_stack(rbflength+2);
462 bfplotmy1 = alloc_stack(rbflength+2);
463 bfplotmy2 = alloc_stack(rbflength+2);
464 /* ftemp = (yymin-yy3rd)*(xx3rd-xxmin) - (xxmax-xx3rd)*(yy3rd-yymax); */
465 sub_bf(tmp1,bfymin,bfy3rd);
466 sub_bf(tmp2,bfx3rd,bfxmin);
467 sub_bf(tmp3,bfxmax,bfx3rd);
468 sub_bf(tmp4,bfy3rd,bfymax);
469 mult_bf(tmp5,tmp1,tmp2);
470 mult_bf(tmp6,tmp3,tmp4);
471 sub_bf(bfftemp,tmp5,tmp6);
472 /* plotmx1 = (xx3rd-xxmin); */ ; /* reuse the plotxxx vars is safe */
473 copy_bf(bfplotmx1,tmp2);
474 /* plotmx2 = (yy3rd-yymax); */
475 copy_bf(bfplotmx2,tmp4);
476 /* plotmy1 = (yymin-yy3rd); */
477 copy_bf(bfplotmy1,tmp1);
478 /* plotmy2 = (xxmax-xx3rd); */;
479 copy_bf(bfplotmy2,tmp3);
480
481 /* savxxmin = xxmin; savyymax = yymax; */
482 copy_bf(savbfxmin,bfxmin); copy_bf(savbfymax,bfymax);
483
484 sub_bf(tmp1,bfsxmin,savbfxmin); sub_bf(tmp2,bfsymax,savbfymax);
485 zmo_calcbf(tmp1,tmp2,bfxmin,bfymax,bfplotmx1,bfplotmx2,bfplotmy1,
486 bfplotmy2,bfftemp);
487 sub_bf(tmp1,bfsxmax,savbfxmin); sub_bf(tmp2,bfsymin,savbfymax);
488 zmo_calcbf(tmp1,tmp2,bfxmax,bfymin,bfplotmx1,bfplotmx2,bfplotmy1,
489 bfplotmy2,bfftemp);
490 sub_bf(tmp1,bfsx3rd,savbfxmin); sub_bf(tmp2,bfsy3rd,savbfymax);
491 zmo_calcbf(tmp1,tmp2,bfx3rd,bfy3rd,bfplotmx1,bfplotmx2,bfplotmy1,
492 bfplotmy2,bfftemp);
493 restore_stack(saved);
494 }
495
496 void zoomoutdbl(void) /* for ctl-enter, calc corners for zooming out */
497 {
498 /* (xxmin,yymax), etc, are already set to zoombox corners;
499 (sxmin,symax), etc, are still the screen's corners;
500 use the same logic as plot_orbit stuff to first calculate current screen
501 corners relative to the zoombox, as if the zoombox were a square with
502 upper left (0,0) and width/depth 1; ie calc the current screen corners
503 as if plotting them from the zoombox;
504 then extend these co-ords from current real screen corners to get
505 new actual corners
506 */
507 double savxxmin,savyymax,ftemp;
508 ftemp = (yymin-yy3rd)*(xx3rd-xxmin) - (xxmax-xx3rd)*(yy3rd-yymax);
509 plotmx1 = (xx3rd-xxmin); /* reuse the plotxxx vars is safe */
510 plotmx2 = (yy3rd-yymax);
511 plotmy1 = (yymin-yy3rd);
512 plotmy2 = (xxmax-xx3rd);
513 savxxmin = xxmin; savyymax = yymax;
514 zmo_calc(sxmin-savxxmin,symax-savyymax,&xxmin,&yymax,ftemp);
515 zmo_calc(sxmax-savxxmin,symin-savyymax,&xxmax,&yymin,ftemp);
516 zmo_calc(sx3rd-savxxmin,sy3rd-savyymax,&xx3rd,&yy3rd,ftemp);
517 }
518
519 void zoomout(void) /* for ctl-enter, calc corners for zooming out */
520 {
521 if(bf_math)
522 {
523 zoomoutbf();
524 }
525 else
526 zoomoutdbl();
527 }
528
529 #ifdef C6
530 #pragma optimize("e",on) /* back to normal */ 531 #endif
532
533 void aspectratio_crop(float oldaspect,float newaspect)
534 {
535 double ftemp,xmargin,ymargin;
536 if (newaspect > oldaspect) { /* new ratio is taller, crop x */
537 ftemp = (1.0 - oldaspect / newaspect) / 2;
538 xmargin = (xxmax - xx3rd) * ftemp;
539 ymargin = (yymin - yy3rd) * ftemp;
540 xx3rd += xmargin;
541 yy3rd += ymargin;
542 }
543 else { /* new ratio is wider, crop y */
544 ftemp = (1.0 - newaspect / oldaspect) / 2;
545 xmargin = (xx3rd - xxmin) * ftemp;
546 ymargin = (yy3rd - yymax) * ftemp;
547 xx3rd -= xmargin;
548 yy3rd -= ymargin;
549 }
550 xxmin += xmargin;
551 yymax += ymargin;
552 xxmax -= xmargin;
553 yymin -= ymargin;
554 }
555
556 static int check_pan(void) /* return 0 if can't, alignment requirement if can */
557 { int i,j;
558 if ((calc_status != 2 && calc_status != 4) || evolving)
559 return(0); /* not resumable, not complete */
560 if ( curfractalspecific->calctype != StandardFractal
561 && curfractalspecific->calctype != calcmand
562 && curfractalspecific->calctype != calcmandfp
563 && curfractalspecific->calctype != lyapunov
564 && curfractalspecific->calctype != calcfroth)
565 return(0); /* not a worklist-driven type */
566 if (zwidth != 1.0 || zdepth != 1.0 || zskew != 0.0 || zrotate != 0.0)
567 return(0); /* not a full size unrotated unskewed zoombox */
568 if (stdcalcmode == 't')
569 return(0); /* tesselate, can't do it */
570 if (stdcalcmode == 'd')
571 return(0); /* diffusion scan: can't do it either */
572 if (stdcalcmode == 'o')
573 return(0); /* orbits, can't do it */
574
575 /* can pan if we get this far */
576
577 if (calc_status == 4)
578 return(1); /* image completed, align on any pixel */
579 if (potflag && pot16bit)
580 return(1); /* 1 pass forced so align on any pixel */
581 if (stdcalcmode == 'b')
582 return(1); /* btm, align on any pixel */
583 if (stdcalcmode != 'g' || (curfractalspecific->flags&NOGUESS)) {
584 if (stdcalcmode == '2' || stdcalcmode == '3') /* align on even pixel for 2pass */
585 return(2);
586 return(1); /* assume 1pass */
587 }
588 /* solid guessing */
589 start_resume();
590 get_resume(sizeof(num_worklist),&num_worklist,sizeof(worklist),worklist,0);
591 /* don't do end_resume! we're just looking */
592 i = 9;
593 for (j=0; j<num_worklist; ++j) /* find lowest pass in any pending window */
594 if (worklist[j].pass < i)
595 i = worklist[j].pass;
596 j = ssg_blocksize(); /* worst-case alignment requirement */
597 while (--i >= 0)
598 j = j>>1; /* reduce requirement */
599 return(j);
600 }
601
602 static void _fastcall move_row(int fromrow,int torow,int col)
603 /* move a row on the screen */
604 { int startcol,endcol,tocol;
605 memset(dstack,0,xdots); /* use dstack as a temp for the row; clear it */
606 if (fromrow >= 0 && fromrow < ydots) {
607 tocol = startcol = 0;
608 endcol = xdots-1;
609 if (col < 0) {
610 tocol -= col;
611 endcol += col; }
612 if (col > 0)
613 startcol += col;
614 get_line(fromrow,startcol,endcol,(BYTE *)&dstack[tocol]);
615 }
616 put_line(torow,0,xdots-1,(BYTE *)dstack);
617 }
618
619 int init_pan_or_recalc(int do_zoomout) /* decide to recalc, or to chg worklist & pan */
620 { int i,j,row,col,y,alignmask,listfull;
621 if (zwidth == 0.0)
622 return(0); /* no zoombox, leave calc_status as is */
623 /* got a zoombox */
624 if ((alignmask=check_pan()-1) < 0 || evolving) {
625 calc_status = 0; /* can't pan, trigger recalc */
626 return(0); }
627 if (zbx == 0.0 && zby == 0.0) {
628 clearbox();
629 return(0); } /* box is full screen, leave calc_status as is */
630 col = (int)(zbx*(dxsize+PIXELROUND)); /* calc dest col,row of topleft pixel */
631 row = (int)(zby*(dysize+PIXELROUND));
632 if (do_zoomout) { /* invert row and col */
633 row = 0-row;
634 col = 0-col; }
635 if ((row&alignmask) != 0 || (col&alignmask) != 0) {
636 calc_status = 0; /* not on useable pixel alignment, trigger recalc */
637 return(0); }
638 /* pan */
639 num_worklist = 0;
640 if (calc_status == 2) {
641 start_resume();
642 get_resume(sizeof(num_worklist),&num_worklist,sizeof(worklist),worklist,0);
643 } /* don't do end_resume! we might still change our mind */
644 /* adjust existing worklist entries */
645 for (i=0; i<num_worklist; ++i) {
646 worklist[i].yystart -= row;
647 worklist[i].yystop -= row;
648 worklist[i].yybegin -= row;
649 worklist[i].xxstart -= col;
650 worklist[i].xxstop -= col;
651 worklist[i].xxbegin -= col;
652 }
653 /* add worklist entries for the new edges */
654 listfull = i = 0;
655 j = ydots-1;
656 if (row < 0) {
657 listfull |= add_worklist(0,xdots-1,0,0,0-row-1,0,0,0);
658 i = 0 - row; }
659 if (row > 0) {
660 listfull |= add_worklist(0,xdots-1,0,ydots-row,ydots-1,ydots-row,0,0);
661 j = ydots - row - 1; }
662 if (col < 0)
663 listfull |= add_worklist(0,0-col-1,0,i,j,i,0,0);
664 if (col > 0)
665 listfull |= add_worklist(xdots-col,xdots-1,xdots-col,i,j,i,0,0);
666 if (listfull != 0) {
667 static FCODE msg[] = {"\
668 Tables full, can't pan current image.\n\
669 Cancel resumes old image, continue pans and calculates a new one."};
670 if (stopmsg(2,msg)) {
671 zwidth = 0; /* cancel the zoombox */
672 drawbox(1); }
673 else
674 calc_status = 0; /* trigger recalc */
675 return(0); }
676 /* now we're committed */
677 calc_status = 2;
678 clearbox();
679 if (row > 0) /* move image up */
680 for (y=0; y<ydots; ++y) move_row(y+row,y,col);
681 else /* move image down */
682 for (y=ydots; --y>=0;) move_row(y+row,y,col);
683 fix_worklist(); /* fixup any out of bounds worklist entries */
684 alloc_resume(sizeof(worklist)+20,2); /* post the new worklist */
685 put_resume(sizeof(num_worklist),&num_worklist,sizeof(worklist),worklist,0);
686 return(0);
687 }
688
689 static void _fastcall restart_window(int wknum)
690 /* force a worklist entry to restart */
691 { int yfrom,yto,xfrom,xto;
692 if ((yfrom = worklist[wknum].yystart) < 0) yfrom = 0;
693 if ((xfrom = worklist[wknum].xxstart) < 0) xfrom = 0;
694 if ((yto = worklist[wknum].yystop) >= ydots) yto = ydots - 1;
695 if ((xto = worklist[wknum].xxstop) >= xdots) xto = xdots - 1;
696 memset(dstack,0,xdots); /* use dstack as a temp for the row; clear it */
697 while (yfrom <= yto)
698 put_line(yfrom++,xfrom,xto,(BYTE *)dstack);
699 worklist[wknum].sym = worklist[wknum].pass = 0;
700 worklist[wknum].yybegin = worklist[wknum].yystart;
701 worklist[wknum].xxbegin = worklist[wknum].xxstart;
702 }
703
704 static void fix_worklist(void) /* fix out of bounds and symmetry related stuff */
705 { int i,j,k;
706 WORKLIST *wk;
707 for (i=0; i<num_worklist; ++i) {
708 wk = &worklist[i];
709 if ( wk->yystart >= ydots || wk->yystop < 0
710 || wk->xxstart >= xdots || wk->xxstop < 0) { /* offscreen, delete */
711 for (j=i+1; j<num_worklist; ++j)
712 worklist[j-1] = worklist[j];
713 --num_worklist;
714 --i;
715 continue; }
716 if (wk->yystart < 0) { /* partly off top edge */
717 if ((wk->sym&1) == 0) { /* no sym, easy */
718 wk->yystart = 0;
719 wk->xxbegin = 0; }
720 else { /* xaxis symmetry */
721 if ((j = wk->yystop + wk->yystart) > 0
722 && num_worklist < MAXCALCWORK) { /* split the sym part */
723 worklist[num_worklist] = worklist[i];
724 worklist[num_worklist].yystart = 0;
725 worklist[num_worklist++].yystop = j;
726 wk->yystart = j+1; }
727 else
728 wk->yystart = 0;
729 restart_window(i); /* restart the no-longer sym part */
730 }
731 }
732 if (wk->yystop >= ydots) { /* partly off bottom edge */
733 j = ydots-1;
734 if ((wk->sym&1) != 0) { /* uses xaxis symmetry */
735 if ((k = wk->yystart + (wk->yystop - j)) < j) {
736 if (num_worklist >= MAXCALCWORK) /* no room to split */
737 restart_window(i);
738 else { /* split it */
739 worklist[num_worklist] = worklist[i];
740 worklist[num_worklist].yystart = k;
741 worklist[num_worklist++].yystop = j;
742 j = k-1; }
743 }
744 wk->sym &= -1 - 1; }
745 wk->yystop = j; }
746 if (wk->xxstart < 0) { /* partly off left edge */
747 if ((wk->sym&2) == 0) /* no sym, easy */
748 wk->xxstart = 0;
749 else { /* yaxis symmetry */
750 if ((j = wk->xxstop + wk->xxstart) > 0
751 && num_worklist < MAXCALCWORK) { /* split the sym part */
752 worklist[num_worklist] = worklist[i];
753 worklist[num_worklist].xxstart = 0;
754 worklist[num_worklist++].xxstop = j;
755 wk->xxstart = j+1; }
756 else
757 wk->xxstart = 0;
758 restart_window(i); /* restart the no-longer sym part */
759 }
760 }
761 if (wk->xxstop >= xdots) { /* partly off right edge */
762 j = xdots-1;
763 if ((wk->sym&2) != 0) { /* uses xaxis symmetry */
764 if ((k = wk->xxstart + (wk->xxstop - j)) < j) {
765 if (num_worklist >= MAXCALCWORK) /* no room to split */
766 restart_window(i);
767 else { /* split it */
768 worklist[num_worklist] = worklist[i];
769 worklist[num_worklist].xxstart = k;
770 worklist[num_worklist++].xxstop = j;
771 j = k-1; }
772 }
773 wk->sym &= -1 - 2; }
774 wk->xxstop = j; }
775 if (wk->yybegin < wk->yystart) wk->yybegin = wk->yystart;
776 if (wk->yybegin > wk->yystop) wk->yybegin = wk->yystop;
777 if (wk->xxbegin < wk->xxstart) wk->xxbegin = wk->xxstart;
778 if (wk->xxbegin > wk->xxstop) wk->xxbegin = wk->xxstop;
779 }
780 tidy_worklist(); /* combine where possible, re-sort */
781 }
782