Little fix after the last commit (mostly a git fail)
[eigenmath-fx.git] / draw.cpp
blobd24df1eb9b27b27e07c03ed7c1050aa064a5662a
1 // Draw a graph
2 extern "C"{
3 #include "fxlib.h"
5 #include <string.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <math.h>
10 #include "stdafx.h"
11 #include "defs.h"
13 #define DIMX 128
14 #define DIMY 64
16 #define F p3
17 #define T p4
18 #define X p5
19 #define Y p6
20 #define XT p7
21 #define YT p8
23 static double tmin, tmax;
24 static double xmin, xmax;
25 static double ymin, ymax;
27 #define YMAX 1000
29 typedef struct {
30 int x, y;
31 double t;
32 } DRAWBUF;
34 DRAWBUF *draw_buf;
36 static int draw_count;
38 void
39 eval_draw(void)
41 draw_buf=(DRAWBUF*)calloc(YMAX,sizeof(DRAWBUF));
42 F = cadr(p1);
43 T = caddr(p1);
45 if (T == symbol(NIL)) {
46 push(F);
47 rewrite();
48 guess();
49 T = pop();
50 F = pop();
53 push(get_binding(T));
54 push(get_arglist(T));
56 draw_main();
58 p2 = pop();
59 p1 = pop();
60 set_binding_and_arglist(T, p1, p2);
62 // return value
64 push(symbol(NIL));
67 void
68 draw_main(void)
70 if (draw_flag) {
71 draw_flag = 0; // so "stop" really stops
72 stop("draw calls draw");
75 draw_flag++;
77 setup_trange();
79 setup_xrange();
81 setup_yrange();
83 check_for_parametric_draw();
85 create_point_set();
87 emit_graph();
89 draw_flag--;
92 /* xrange sets the horizontal scale
94 yrange sets the vertical scale
96 Normally, the function F is evaluated from xrange[1] to xrange[2].
98 However, if F returns a vector then parametric drawing is used. In this
99 case F is evaluated from trange[1] to trange[2].
102 void
103 check_for_parametric_draw(void)
105 eval_f(tmin);
106 p1 = pop();
107 if (!istensor(p1)) {
108 tmin = xmin;
109 tmax = xmax;
113 #define N 100
115 void
116 create_point_set(void)
118 int i, n;
119 double t;
121 draw_count = 0;
123 for (i = 0; i <= N; i++) {
124 t = tmin + ((double) i / (double) N) * (tmax - tmin);
125 new_point(t);
128 n = draw_count;
130 for (i = 0; i < n - 1; i++)
131 fill(i, i + 1, 0);
134 void
135 new_point(double t)
137 double x, y;
139 if (draw_count >= YMAX)
140 return;
142 draw_buf[draw_count].x = -10000;
143 draw_buf[draw_count].y = -10000;
144 draw_buf[draw_count].t = t;
146 draw_count++;
148 get_xy(t);
150 if (!isnum(XT) || !isnum(YT))
151 return;
153 push(XT);
154 x = pop_double();
155 x = (x - xmin) / (xmax - xmin);
156 x = (double) DIMX * x + 0.5; // map 0-1 to 0-DIM, +0.5 so draw(x^3) looks right
158 push(YT);
159 y = pop_double();
160 y = (y - ymin) / (ymax - ymin);
161 y = (double) DIMY * y + 0.5; // map 0-1 to 0-DIM, +0.5 so draw(x^3) looks right
163 if (x < -10000.0)
164 x = -10000.0;
165 if (x > 10000.0)
166 x = 10000.0;
167 if (y < -10000.0)
168 y = -10000.0;
169 if (y > 10000.0)
170 y = 10000.0;
172 draw_buf[draw_count - 1].x = (int) x;
173 draw_buf[draw_count - 1].y = (int) y;
176 // Evaluate F(t) and return in XT and YT.
178 void
179 get_xy(double t)
181 eval_f(t);
183 p1 = pop();
185 if (istensor(p1)) {
186 if (p1->u.tensor->nelem >= 2) {
187 XT = p1->u.tensor->elem[0];
188 YT = p1->u.tensor->elem[1];
189 } else {
190 XT = symbol(NIL);
191 YT = symbol(NIL);
193 return;
196 push_double(t);
197 XT = pop();
198 YT = p1;
201 // Evaluate F(t) without stopping due to an error such as divide by zero.
203 void
204 eval_f(double t)
206 // These must be volatile or it crashes. (Compiler error?)
207 // Read it backwards, save_tos is a volatile int, etc.
209 int volatile save_tos;
210 U ** volatile save_frame;
212 save();
213 save_tos = tos;
214 save_frame = frame;
216 draw_flag++;
218 if (setjmp(draw_stop_return)) {
219 tos = save_tos;
220 push(symbol(NIL));
221 frame = save_frame;
222 restore();
223 draw_flag--;
224 return;
227 push_double(t);
228 p1 = pop();
229 set_binding(T, p1);
231 push(F);
232 eval();
233 yyfloat();
234 eval();
236 restore();
237 draw_flag--;
240 #define MAX_DEPTH 6
242 void
243 fill(int i, int k, int level)
245 int dx, dy, j;
246 double t;
248 if (level >= MAX_DEPTH || draw_count >= YMAX)
249 return;
251 dx = abs(draw_buf[i].x - draw_buf[k].x);
252 dy = abs(draw_buf[i].y - draw_buf[k].y);
254 if (dx < 1 && dy < 1)
255 return;
257 t = (draw_buf[i].t + draw_buf[k].t) / 2.0;
259 j = draw_count;
261 new_point(t);
263 fill(i, j, level + 1);
264 fill(j, k, level + 1);
267 //-----------------------------------------------------------------------------
269 // Normalize x to [0,1]
271 // Example: xmin = -10, xmax = 10, xmax - xmin = 20
273 // x x - xmin (x - xmin) / (xmax - xmin)
275 // -10 0 0.00
277 // -5 5 0.25
279 // 0 10 0.50
281 // 5 15 0.75
283 // 10 20 1.00
285 //-----------------------------------------------------------------------------
287 void
288 setup_trange(void)
290 save();
291 setup_trange_f();
292 restore();
295 void
296 setup_trange_f(void)
298 // default range is (-pi, pi)
300 tmin = -M_PI;
302 tmax = M_PI;
304 p1 = usr_symbol("trange");
306 if (!issymbol(p1))
307 return;
309 p1 = get_binding(p1);
311 // must be two element vector
313 if (!istensor(p1) || p1->u.tensor->ndim != 1 || p1->u.tensor->nelem != 2)
314 return;
316 push(p1->u.tensor->elem[0]);
317 eval();
318 yyfloat();
319 eval();
320 p2 = pop();
322 push(p1->u.tensor->elem[1]);
323 eval();
324 yyfloat();
325 eval();
326 p3 = pop();
328 if (!isnum(p2) || !isnum(p3))
329 return;
331 push(p2);
332 tmin = pop_double();
334 push(p3);
335 tmax = pop_double();
337 if (tmin == tmax)
338 stop("draw: trange is zero");
341 void
342 setup_xrange(void)
344 save();
345 setup_xrange_f();
346 restore();
349 void
350 setup_xrange_f(void)
352 // default range is (-10,10)
354 xmin = -10.0;
356 xmax = 10.0;
358 p1 = usr_symbol("xrange");
360 if (!issymbol(p1))
361 return;
363 p1 = get_binding(p1);
365 // must be two element vector
367 if (!istensor(p1) || p1->u.tensor->ndim != 1 || p1->u.tensor->nelem != 2)
368 return;
370 push(p1->u.tensor->elem[0]);
371 eval();
372 yyfloat();
373 eval();
374 p2 = pop();
376 push(p1->u.tensor->elem[1]);
377 eval();
378 yyfloat();
379 eval();
380 p3 = pop();
382 if (!isnum(p2) || !isnum(p3))
383 return;
385 push(p2);
386 xmin = pop_double();
388 push(p3);
389 xmax = pop_double();
391 if (xmin == xmax)
392 stop("draw: xrange is zero");
395 //-----------------------------------------------------------------------------
397 // Example: yrange=(-10,10)
399 // y d v (vertical pixel coordinate)
401 // 10 0.00 0
403 // 5 0.25 100
405 // 0 0.50 200
407 // -5 0.75 300
409 // -10 1.00 400
411 // We have
413 // d = (10 - y) / 20
415 // = (B - y) / (B - A)
417 // where yrange=(A,B)
419 // To convert d to v, multiply by N where N = 400.
421 //-----------------------------------------------------------------------------
423 void
424 setup_yrange(void)
426 save();
427 setup_yrange_f();
428 restore();
431 void
432 setup_yrange_f(void)
434 // default range is (-10,10)
436 ymin = -10.0;
438 ymax = 10.0;
440 p1 = usr_symbol("yrange");
442 if (!issymbol(p1))
443 return;
445 p1 = get_binding(p1);
447 // must be two element vector
449 if (!istensor(p1) || p1->u.tensor->ndim != 1 || p1->u.tensor->nelem != 2)
450 return;
452 push(p1->u.tensor->elem[0]);
453 eval();
454 yyfloat();
455 eval();
456 p2 = pop();
458 push(p1->u.tensor->elem[1]);
459 eval();
460 yyfloat();
461 eval();
462 p3 = pop();
464 if (!isnum(p2) || !isnum(p3))
465 return;
467 push(p2);
468 ymin = pop_double();
470 push(p3);
471 ymax = pop_double();
473 if (ymin == ymax)
474 stop("draw: yrange is zero");
477 void get_xyminmax(double* xminp, double* xmaxp, double* yminp, double* ymaxp) {
478 *xminp = xmin;
479 *xmaxp = xmax;
480 *yminp = ymin;
481 *ymaxp = ymax;
484 #define XOFF 0
485 #define YOFF 24
487 static void emit_xaxis(void);
488 static void emit_yaxis(void);
489 static void emit_xscale(void);
490 static void emit_yscale(void);
492 static void get_xzero(void);
493 static void get_yzero(void);
495 static int xzero, yzero;
497 void
498 emit_graph(void)
500 int i, x, y;
501 Bdisp_AllClr_VRAM();
502 get_xzero();
503 get_yzero();
504 emit_xaxis();
505 emit_yaxis();
506 emit_xscale();
507 emit_yscale();
509 for (i = 0; i < draw_count; i++) {
510 x = draw_buf[i].x;
511 y = DIMY - draw_buf[i].y; // flip the y coordinate
512 if (x < 0 || x > DIMX)
513 continue;
514 if (y < 0 || y > DIMY)
515 continue;
516 Bdisp_SetPoint_VRAM(x+XOFF, y+YOFF, 1);
518 set_has_drawn(1);
521 static void
522 emit_xaxis(void)
524 int x, y, x2, y2;
526 if (yzero < 0 || yzero > DIMY)
527 return;
529 x = XOFF;
530 y = YOFF + yzero;
532 x2 = XOFF + DIMX;
533 y2 = YOFF + yzero;
535 Bdisp_DrawLineVRAM(x, y, x2, y2);
538 static void
539 emit_yaxis(void)
541 int x, y, x2, y2;
543 if (xzero < 0 || xzero > DIMX)
544 return;
546 x = XOFF + xzero;
547 y = YOFF;
549 x2 = XOFF + xzero;
550 y2 = YOFF + DIMY;
552 Bdisp_DrawLineVRAM(x, y, x2, y2);
555 static void
556 get_xzero(void)
558 double x;
559 x = -((double) DIMX) * xmin / (xmax - xmin) + 0.5;
560 if (x < -10000.0)
561 x = -10000.0;
562 if (x > 10000.0)
563 x = 10000.0;
564 xzero = (int) x;
567 static void
568 get_yzero(void)
570 double y;
571 y = -((double) DIMY) * ymin / (ymax - ymin) + 0.5;
572 if (y < -10000.0)
573 y = -10000.0;
574 if (y > 10000.0)
575 y = 10000.0;
576 yzero = DIMY - (int) y; // flip the y coordinate
579 static void emit_xscale_f(int, char *);
581 static void
582 emit_xscale(void)
584 static char s[100];
585 sprintf(s, "%g", xmin);
586 emit_xscale_f(0, s);
587 sprintf(s, "%g", xmax);
588 emit_xscale_f(DIMX, s);
591 static void
592 emit_xscale_f(int xx, char *s)
594 int w, x, y;
595 y = 0;
596 w = 0;
597 PrintMini( w, y, (unsigned char*)s, 0); // get width
599 x = XOFF + xx;
600 if(x >= DIMX) x = x-w;
601 y = YOFF + yzero - 24+2;
603 PrintMini( x, y, (unsigned char*)s, 0);
606 static void emit_yscale_f(int, char *);
608 static void
609 emit_yscale(void)
611 static char s[100];
612 sprintf(s, "%g", ymax);
613 emit_yscale_f(0, s);
614 sprintf(s, "%g", ymin);
615 emit_yscale_f(DIMY, s);
618 static void
619 emit_yscale_f(int yy, char *s)
621 int w, x, y;
623 y = 0;
624 w = 0;
625 PrintMini( w, y, (unsigned char*)s, 0); // get width
627 x = xzero - w;
628 y = YOFF + yy;
629 if(y >= DIMY) y = y-9;
630 y -= 24;
631 PrintMini( x, y, (unsigned char*)s, 0);
634 #define XOFF 0
635 #define YOFF 0
637 #define SHIM 10
639 static int k;
640 static unsigned char *buf;
642 static void emit_box(void);
643 static void emit_xaxis(void);
644 static void emit_yaxis(void);
645 static void emit_xscale(void);
646 static void emit_yscale(void);
647 static void emit_xzero(void);
648 static void emit_yzero(void);
649 static void get_xzero(void);
650 static void get_yzero(void);
652 static int xzero, yzero;
654 void
655 emit_graph(void)
657 int h, i, len, x, y;
659 get_xzero();
660 get_yzero();
662 len = 1000 + 5 * draw_count;
664 buf = (unsigned char *) malloc(len);
666 h = DIM + SHIM + text_metric[SMALL_FONT].ascent + text_metric[SMALL_FONT].descent;
668 //buf[0] = (unsigned char) (h >> 8);
669 //buf[1] = (unsigned char) h;
671 //buf[2] = (unsigned char) (DIM >> 8);
672 //buf[3] = (unsigned char) DIM;
674 k = 0;
676 emit_box();
678 emit_xaxis();
679 emit_yaxis();
681 emit_xscale();
682 emit_yscale();
684 emit_xzero();
685 emit_yzero();
687 for (i = 0; i < draw_count; i++) {
688 x = draw_buf[i].x;
689 y = DIM - draw_buf[i].y; // flip the y coordinate
690 if (x < 0 || x > DIM)
691 continue;
692 if (y < 0 || y > DIM)
693 continue;
694 x += XOFF;
695 y += YOFF;
696 buf[k++] = DRAW_POINT;
697 buf[k++] = (unsigned char) (x >> 8);
698 buf[k++] = (unsigned char) x;
699 buf[k++] = (unsigned char) (y >> 8);
700 buf[k++] = (unsigned char) y;
703 buf[k++] = 0;
705 shipout(buf, DIM + 1, h);
708 static void
709 get_xzero(void)
711 double x;
712 x = -((double) DIM) * xmin / (xmax - xmin) + 0.5;
713 if (x < -10000.0)
714 x = -10000.0;
715 if (x > 10000.0)
716 x = 10000.0;
717 xzero = (int) x;
720 static void
721 get_yzero(void)
723 double y;
724 y = -((double) DIM) * ymin / (ymax - ymin) + 0.5;
725 if (y < -10000.0)
726 y = -10000.0;
727 if (y > 10000.0)
728 y = 10000.0;
729 yzero = DIM - (int) y; // flip the y coordinate
732 static void
733 emit_box(void)
735 int x, y;
737 buf[k++] = DRAW_BOX;
739 x = XOFF;
740 y = YOFF;
741 buf[k++] = (unsigned char) (x >> 8);
742 buf[k++] = (unsigned char) x;
743 buf[k++] = (unsigned char) (y >> 8);
744 buf[k++] = (unsigned char) y;
746 x = XOFF + DIM;
747 y = YOFF + DIM;
748 buf[k++] = (unsigned char) (x >> 8);
749 buf[k++] = (unsigned char) x;
750 buf[k++] = (unsigned char) (y >> 8);
751 buf[k++] = (unsigned char) y;
754 static void
755 emit_xaxis(void)
757 int x, y;
759 if (yzero < 0 || yzero > DIM)
760 return;
762 buf[k++] = DRAW_LINE;
764 x = XOFF;
765 y = YOFF + yzero;
767 buf[k++] = (unsigned char) (x >> 8);
768 buf[k++] = (unsigned char) x;
769 buf[k++] = (unsigned char) (y >> 8);
770 buf[k++] = (unsigned char) y;
772 x = XOFF + DIM;
773 y = YOFF + yzero;
775 buf[k++] = (unsigned char) (x >> 8);
776 buf[k++] = (unsigned char) x;
777 buf[k++] = (unsigned char) (y >> 8);
778 buf[k++] = (unsigned char) y;
781 static void
782 emit_yaxis(void)
784 int x, y;
786 if (xzero < 0 || xzero > DIM)
787 return;
789 buf[k++] = DRAW_LINE;
791 x = XOFF + xzero;
792 y = YOFF;
794 buf[k++] = (unsigned char) (x >> 8);
795 buf[k++] = (unsigned char) x;
796 buf[k++] = (unsigned char) (y >> 8);
797 buf[k++] = (unsigned char) y;
799 x = XOFF + xzero;
800 y = YOFF + DIM;
802 buf[k++] = (unsigned char) (x >> 8);
803 buf[k++] = (unsigned char) x;
804 buf[k++] = (unsigned char) (y >> 8);
805 buf[k++] = (unsigned char) y;
808 static void emit_xscale_f(int, char *);
810 static void
811 emit_xscale(void)
813 static char s[100];
814 sprintf(s, "%g", xmin);
815 emit_xscale_f(0, s);
816 sprintf(s, "%g", xmax);
817 emit_xscale_f(DIM, s);
820 static void
821 emit_xscale_f(int xx, char *s)
823 int d, i, len, w, x, y;
825 // want to center the number w/o sign
827 w = text_width(SMALL_FONT, s);
829 if (*s == '-')
830 d = w - text_width(SMALL_FONT, s + 1);
831 else
832 d = 0;
834 x = XOFF + xx - (w - d) / 2 - d;
835 y = YOFF + DIM + SHIM;
837 buf[k++] = SMALL_FONT;
838 buf[k++] = (unsigned char) (x >> 8);
839 buf[k++] = (unsigned char) x;
840 buf[k++] = (unsigned char) (y >> 8);
841 buf[k++] = (unsigned char) y;
843 len = (int) strlen(s);
845 buf[k++] = (unsigned char) len;
847 for (i = 0; i < len; i++)
848 buf[k++] = (unsigned char) s[i];
851 static void emit_yscale_f(int, char *);
853 static void
854 emit_yscale(void)
856 static char s[100];
857 sprintf(s, "%g", ymax);
858 emit_yscale_f(0, s);
859 sprintf(s, "%g", ymin);
860 emit_yscale_f(DIM, s);
863 static void
864 emit_yscale_f(int yy, char *s)
866 int i, len, w, x, y;
868 w = text_width(SMALL_FONT, s);
870 x = XOFF - SHIM - w;
871 y = YOFF + yy - text_metric[SMALL_FONT].ascent / 2;
873 buf[k++] = SMALL_FONT;
874 buf[k++] = (unsigned char) (x >> 8);
875 buf[k++] = (unsigned char) x;
876 buf[k++] = (unsigned char) (y >> 8);
877 buf[k++] = (unsigned char) y;
879 len = (int) strlen(s);
881 buf[k++] = (unsigned char) len;
883 for (i = 0; i < len; i++)
884 buf[k++] = (unsigned char) s[i];
887 // emit the '0' axis label
889 // make sure it doesn't hit the other labels
891 static void
892 emit_xzero(void)
894 int x, y;
896 if (xzero < DIM / 4 || xzero > 3 * DIM / 4)
897 return;
899 x = XOFF + xzero - text_width(SMALL_FONT, "0") / 2;
900 y = YOFF + DIM + SHIM;
902 buf[k++] = SMALL_FONT;
903 buf[k++] = (unsigned char) (x >> 8);
904 buf[k++] = (unsigned char) x;
905 buf[k++] = (unsigned char) (y >> 8);
906 buf[k++] = (unsigned char) y;
907 buf[k++] = 1;
908 buf[k++] = '0';
911 // emit the '0' axis label
913 // make sure it doesn't hit the other labels
915 static void
916 emit_yzero(void)
918 int x, y;
920 if (yzero < DIM / 4 || yzero > 3 * DIM / 4)
921 return;
923 x = XOFF - SHIM - text_width(SMALL_FONT, "0");
924 y = YOFF + yzero - text_metric[SMALL_FONT].ascent / 2;
926 buf[k++] = SMALL_FONT;
927 buf[k++] = (unsigned char) (x >> 8);
928 buf[k++] = (unsigned char) x;
929 buf[k++] = (unsigned char) (y >> 8);
930 buf[k++] = (unsigned char) y;
931 buf[k++] = 1;
932 buf[k++] = '0';