1// This file is part of SmallBASIC
2//
3// SmallBASIC RTL - GRAPHICS
4//
5// This program is distributed under the terms of the GPL v2.0 or later
6// Download the GNU Public License (GPL) from www.gnu.org
7//
8// Copyright(C) 2000 Nicholas Christopoulos
9
10#include "common/pproc.h"
11#include "common/messages.h"
12
13// graphics - relative coordinates
14int gra_x;
15int gra_y;
16
17void graph_reset() {
18 gra_x = gra_y = 0;
19}
20
21//
22// VIEW [x1,y1,x2,y2[,c[,b]]]
23//
24void cmd_view() {
25 ipt_t p1, p2;
26 int32_t prev_color = dev_fgcolor;
27 int32_t color = dev_bgcolor;
28 int32_t bcolor = -1;
29
30 if (code_peek() != kwTYPE_EOC && code_peek() != kwTYPE_LINE) {
31 p1 = par_getipt();
32 if (prog_error)
33 return;
34 par_getcomma();
35 if (prog_error)
36 return;
37 p2 = par_getipt();
38 if (prog_error)
39 return;
40 if (code_peek() == kwTYPE_SEP) {
41 par_getcomma();
42 if (prog_error)
43 return;
44 color = par_getint();
45 if (prog_error)
46 return;
47 if (code_peek() == kwTYPE_SEP) {
48 par_getcomma();
49 if (prog_error)
50 return;
51 bcolor = par_getint();
52 if (prog_error)
53 return;
54 }
55 }
56
57 dev_setcolor(color);
58 dev_rect(p1.x, p1.y, p2.x, p2.y, 1);
59 if (bcolor != -1) {
60 dev_setcolor(bcolor);
61 dev_rect(p1.x - 1, p1.y - 1, p2.x + 1, p2.y + 1, 0);
62 }
63 dev_setcolor(prev_color);
64 dev_viewport(p1.x, p1.y, p2.x, p2.y);
65 } else {
66 dev_viewport(0, 0, 0, 0);
67 }
68}
69
70//
71// WINDOW [x1,y1,x2,y2]
72//
73void cmd_window() {
74 ipt_t p1, p2;
75
76 if (code_peek() != kwTYPE_EOC && code_peek() != kwTYPE_LINE) {
77 p1 = par_getipt();
78 if (prog_error)
79 return;
80 par_getcomma();
81 if (prog_error)
82 return;
83 p2 = par_getipt();
84 if (prog_error)
85 return;
86 dev_window(p1.x, p2.y, p2.x, p1.y); // QB compatible
87 // dev_window(p1.x, p1.y, p2.x, p2.y); // SB default (logical one)
88 } else {
89 dev_window(0, 0, 0, 0);
90 }
91}
92
93//
94// PSET [STEP] x, y [, color | COLOR color]
95//
96void cmd_pset() {
97 long color = dev_fgcolor;
98 int32_t step1 = 0;
99 ipt_t pt;
100
101 /*
102 * [STEP] x, y
103 */
104 if (code_peek() == kwSTEP) {
105 code_skipnext();
106 step1 = 1;
107 }
108
109 pt = par_getipt();
110 if (step1)
111 (pt.x += gra_x, pt.y += gra_y);
112
113 if (code_peek() == kwCOLOR) {
114 code_skipnext();
115 color = par_getint();
116 if (prog_error)
117 return;
118 }
119 if (code_peek() == kwTYPE_SEP) {
120 par_getcomma();
121 if (prog_error)
122 return;
123 color = par_getint();
124 if (prog_error)
125 return;
126 }
127
128 gra_x = pt.x;
129 gra_y = pt.y;
130
131 if (color != dev_fgcolor) {
132 int prev_color = dev_fgcolor;
133
134 dev_setcolor(color);
135 dev_setpixel(pt.x, pt.y);
136 dev_setcolor(prev_color);
137 } else
138 dev_setpixel(pt.x, pt.y);
139}
140
141//
142// LINE [STEP] x, y [{,|STEP} x2, y2] [, color | COLOR color]
143//
144void cmd_line() {
145 ipt_t p1, p2;
146 byte step = 0;
147 int32_t color = dev_fgcolor;
148
149 /*
150 * [STEP] x, y
151 */
152 if (code_peek() == kwSTEP) {
153 code_skipnext();
154 step = 1;
155 }
156
157 p1 = par_getipt();
158 if (step) {
159 (p1.x += gra_x, p1.y += gra_y, step = 0);
160 }
161 if (code_peek() == kwTYPE_SEP) {
162 par_getcomma();
163 }
164 /*
165 * x, y [,] ---> [STEP] ?
166 */
167 if (code_peek() == kwSTEP) {
168 code_skipnext();
169 step = 1;
170 }
171
172 if (code_peek() != kwCOLOR && code_peek() != kwTYPE_EOC && code_peek() != kwTYPE_LINE) {
173 /*
174 * [STEP] x, y [[,]STEP] ---> x2, y2
175 */
176 p2 = par_getipt();
177 if (step)
178 (p2.x += p1.x, p2.y += p1.y);
179 } else {
180 p2 = p1;
181 p1.x = gra_x;
182 p1.y = gra_y;
183 }
184
185 if (code_peek() == kwTYPE_SEP) {
186 par_getcomma();
187 if (prog_error)
188 return;
189 color = par_getint();
190 if (prog_error)
191 return;
192 }
193 if (code_peek() == kwCOLOR) {
194 code_skipnext();
195 color = par_getint();
196 if (prog_error)
197 return;
198 }
199
200 /*
201 * draw
202 */
203 gra_x = p2.x;
204 gra_y = p2.y;
205
206 if (color != dev_fgcolor) {
207 int32_t prev_color = dev_fgcolor;
208
209 dev_setcolor(color);
210 dev_line(p1.x, p1.y, p2.x, p2.y);
211 dev_setcolor(prev_color);
212 } else
213 dev_line(p1.x, p1.y, p2.x, p2.y);
214}
215
216//
217// RECT [STEP] x, y [{,|STEP} x2, y2] [COLOR color] [FILLED]
218//
219void cmd_rect() {
220 ipt_t p1, p2;
221 int32_t color = dev_fgcolor;
222 byte fill = 0, step = 0;
223
224 /*
225 * [STEP] x, y
226 */
227 if (code_peek() == kwSTEP) {
228 code_skipnext();
229 step = 1;
230 }
231
232 p1 = par_getipt();
233 if (step)
234 (p1.x += gra_x, p1.y += gra_y, step = 0);
235
236 if (code_peek() == kwTYPE_SEP)
237 par_getcomma();
238
239 /*
240 * x, y [,] ---> [STEP] ?
241 */
242 if (code_peek() == kwSTEP) {
243 code_skipnext();
244 step = 1;
245 }
246
247 if (code_peek() != kwCOLOR && code_peek() != kwFILLED && code_peek() != kwTYPE_EOC
248 && code_peek() != kwTYPE_LINE) {
249 /*
250 * [STEP] x, y [[,]STEP] ---> x2, y2
251 */
252 p2 = par_getipt();
253 if (step)
254 (p2.x += p1.x, p2.y += p1.y);
255 } else {
256 p2 = p1;
257 p1.x = gra_x;
258 p1.y = gra_y;
259 }
260
261 if (code_peek() == kwTYPE_SEP) {
262 par_getcomma();
263 if (prog_error)
264 return;
265 color = par_getint();
266 if (prog_error)
267 return;
268 }
269 if (code_peek() == kwCOLOR) {
270 code_skipnext();
271 color = par_getint();
272 if (prog_error)
273 return;
274 }
275 if (code_peek() == kwFILLED) {
276 code_skipnext();
277 fill = 1;
278 }
279
280 /*
281 * draw
282 */
283 gra_x = p2.x;
284 gra_y = p2.y;
285
286 if (color != dev_fgcolor) {
287 int prev_color = dev_fgcolor;
288
289 dev_setcolor(color);
290 dev_rect(p1.x, p1.y, p2.x, p2.y, fill);
291 dev_setcolor(prev_color);
292 } else
293 dev_rect(p1.x, p1.y, p2.x, p2.y, fill);
294}
295
296//
297// DRAWPOLY v() [, xorg, yorg [, scale[, color]] [COLOR color] [FILLED]
298//
299// 0,2,4... x
300// 1,3,5... y
301//
302void cmd_drawpoly() {
303 int i, count;
304 var_num_t xorg = 0, yorg = 0;
305 int32_t prev_color = dev_fgcolor;
306 int32_t color = dev_fgcolor;
307 byte filled = 0, scalef = 0;
308 var_num_t scale = 1.0;
309 ipt_t *poly = NULL;
310
311 // array
312 count = par_getipoly(&poly);
313 if (prog_error) {
314 free(poly);
315 return;
316 }
317 if (count == 0) {
318 free(poly);
319 return;
320 }
321 // x,y origin
322 if (code_peek() == kwTYPE_SEP) {
323 par_getcomma();
324 if (!prog_error) {
325 xorg = par_getreal();
326 if (!prog_error) {
327 par_getcomma();
328 if (!prog_error)
329 yorg = par_getreal();
330 }
331 }
332
333 if (prog_error) {
334 free(poly);
335 return;
336 }
337
338 // scale factor
339 if (code_peek() == kwTYPE_SEP) {
340 par_getcomma();
341 if (!prog_error) {
342 scale = par_getnum();
343 if (!prog_error) {
344 scalef++;
345 if (code_peek() == kwTYPE_SEP) {
346 par_getcomma();
347 if (!prog_error)
348 color = par_getint();
349 }
350 }
351 }
352 }
353
354 if (prog_error) {
355 free(poly);
356 return;
357 }
358 }
359
360 // color
361 if (code_peek() == kwCOLOR) {
362 code_skipnext();
363 color = par_getint();
364 if (prog_error) {
365 free(poly);
366 return;
367 }
368 }
369
370 // filled
371 if (code_peek() == kwFILLED) {
372 code_skipnext();
373 filled++;
374 }
375
376 // scale it and move it
377 if (scalef || xorg != 0 || yorg != 0) {
378 if (scalef) {
379 for (i = 0; i < count; i++) {
380 poly[i].x = xorg + poly[i].x * scale;
381 poly[i].y = yorg + poly[i].y * scale;
382 }
383 } else {
384 for (i = 0; i < count; i++) {
385 poly[i].x = xorg + poly[i].x;
386 poly[i].y = yorg + poly[i].y;
387 }
388 }
389 }
390
391 // ready
392 if (color != dev_fgcolor)
393 dev_setcolor(color);
394
395 if (!filled) {
396 for (i = 1; i < count; i++)
397 dev_line(poly[i - 1].x, poly[i - 1].y, poly[i].x, poly[i].y);
398 } else
399 dev_pfill(poly, count);
400
401 // cleanup
402 free(poly);
403
404 if (color != prev_color)
405 dev_setcolor(prev_color);
406}
407
408//
409// CIRCLE [STEP] x, y, r [, aspect[, color]] [COLOR color] [FILLED]
410//
411void cmd_circle() {
412 int32_t color = dev_fgcolor;
413 byte fill = 0, step = 0;
414 ipt_t pt;
415 int r;
416 var_num_t aspect = 1.0;
417 byte code;
418
419 /*
420 * [STEP] x, y
421 */
422 code = code_peek();
423 if (code == kwSTEP) {
424 code_skipnext();
425 step = 1;
426 }
427
428 // xc,yc
429 pt = par_getipt();
430 if (prog_error)
431 return;
432 // r
433 par_getcomma();
434 if (prog_error)
435 return;
436 r = par_getint();
437 if (prog_error)
438 return;
439 if (step)
440 (pt.x += gra_x, pt.y += gra_y);
441
442 // aspect
443 if (code_peek() == kwTYPE_SEP) {
444 par_getcomma();
445 if (prog_error)
446 return;
447 aspect = par_getnum();
448 if (prog_error)
449 return;
450
451 if (code_peek() == kwTYPE_SEP) {
452 par_getcomma();
453 if (prog_error)
454 return;
455 color = par_getint();
456 if (prog_error)
457 return;
458 }
459 }
460
461 // COLOR
462 if (code_peek() == kwCOLOR) {
463 code_skipnext();
464 color = par_getint();
465 if (prog_error)
466 return;
467 }
468
469 // FILLED
470 if (code_peek() == kwFILLED) {
471 code_skipnext();
472 fill = 1;
473 }
474
475 if (color != dev_fgcolor) {
476 int32_t prev_color = dev_fgcolor;
477
478 dev_setcolor(color);
479 dev_ellipse(pt.x, pt.y, r, r, aspect, fill);
480 dev_setcolor(prev_color);
481 } else
482 dev_ellipse(pt.x, pt.y, r, r, aspect, fill);
483}
484
485//
486// ARC [STEP] x, y, r, start, end [, aspect[, color]] [COLOR color]
487//
488void cmd_arc() {
489 int32_t color = dev_fgcolor;
490 byte step = 0;
491 int r;
492 var_num_t as, ae, aspect = 1.0;
493 byte code;
494 ipt_t pt;
495
496 /*
497 * [STEP] x, y
498 */
499 code = code_peek();
500 if (code == kwSTEP) {
501 code_skipnext();
502 step = 1;
503 }
504
505 // xc,yc
506 pt = par_getipt();
507 if (prog_error)
508 return;
509
510 // r
511 par_getcomma();
512 if (prog_error)
513 return;
514 r = par_getint();
515 if (prog_error)
516 return;
517
518 // a.st.
519 par_getcomma();
520 if (prog_error)
521 return;
522 as = par_getnum();
523 if (prog_error)
524 return;
525
526 // a.end
527 par_getcomma();
528 if (prog_error)
529 return;
530 ae = par_getnum();
531 if (prog_error)
532 return;
533
534 if (step)
535 (pt.x += gra_x, pt.y += gra_y);
536
537 // aspect
538 if (code_peek() == kwTYPE_SEP) {
539 par_getcomma();
540 if (prog_error)
541 return;
542 aspect = par_getnum();
543 if (prog_error)
544 return;
545
546 if (code_peek() == kwTYPE_SEP) {
547 par_getcomma();
548 if (prog_error)
549 return;
550 color = par_getint();
551 if (prog_error)
552 return;
553 }
554 }
555
556 if (code_peek() == kwCOLOR) {
557 code_skipnext();
558 color = par_getint();
559 if (prog_error)
560 return;
561 }
562
563 if (color != dev_fgcolor) {
564 int32_t prev_color = dev_fgcolor;
565
566 dev_setcolor(color);
567 dev_arc(pt.x, pt.y, r, as, ae, aspect);
568 dev_setcolor(prev_color);
569 } else {
570 dev_arc(pt.x, pt.y, r, as, ae, aspect);
571 }
572}
573
574//
575// PAINT [STEP] x, y [, fillcolor [, bordercolor]]
576//
577void cmd_paint() {
578 int32_t prev_color = dev_fgcolor;
579 int32_t color = dev_fgcolor;
580 byte step = 0;
581 int32_t fc = dev_fgcolor, bc = -1;
582 byte code;
583 ipt_t pt;
584
585 /*
586 * [STEP] x, y
587 */
588 code = code_peek();
589 if (code == kwSTEP) {
590 code_skipnext();
591 step = 1;
592 }
593
594 // xc,yc
595 pt = par_getipt();
596 if (prog_error) {
597 return;
598 }
599 if (step) {
600 (pt.x += gra_x, pt.y += gra_y);
601 }
602 // fillcolor
603 if (code_peek() == kwTYPE_SEP) {
604 par_getcomma();
605 if (prog_error) {
606 return;
607 }
608 fc = par_getint();
609 if (prog_error) {
610 return;
611 }
612 }
613
614 // bordercolor
615 if (code_peek() == kwTYPE_SEP) {
616 par_getcomma();
617 if (prog_error) {
618 return;
619 }
620 bc = par_getint();
621 if (prog_error) {
622 return;
623 }
624 }
625
626 dev_setcolor(color);
627 dev_ffill(pt.x, pt.y, fc, bc);
628 dev_setcolor(prev_color);
629}
630
631//
632char *draw_getval(const char *src, int *c) {
633 char *p = (char *) src;
634 char *dst, buf[64];
635
636 dst = buf;
637 p++;
638 *c = 0;
639 if (*p == '-') {
640 *dst = '-';
641 dst++;
642 p++;
643 }
644
645 while (is_digit(*p)) {
646 *dst++ = *p++;
647 }
648 *dst = '\0';
649
650 *c = xstrtol(buf);
651 return p;
652}
653
654//
655// DRAW "commands"
656//
657void cmd_draw() {
658 register int draw = 1, update = 1;
659 int x, y, r;
660 int32_t prev_color = dev_fgcolor;
661 char *p;
662 var_t var;
663
664 par_getstr(&var);
665 if (prog_error) {
666 return;
667 }
668 p = var.v.p.ptr;
669 while (*p) {
670
671 // 'N' command must affect only the next drawing command.
672 update = 1;
673 // Haraszti -- 'B' command must affect only the next drawing command.
674 draw = 1;
675
676 // commands prefix
677 while (strchr("BbNn", *p)) {
678 if (*p == 'B' || *p == 'b') { // do not draw
679 draw = 0;
680 p++;
681 } else {
682 draw = 1;
683 }
684 if (*p == 'N' || *p == 'n') { // do not update the position
685 update = 0;
686 p++;
687 } else {
688 update = 1;
689 }
690 }
691
692 // commands
693 switch (*p) {
694 case 'U':
695 case 'u': // up
696 p = draw_getval(p, &y);
697 if (draw) {
698 dev_line(gra_x, gra_y, gra_x, gra_y - y);
699 }
700 if (update) {
701 gra_y -= y;
702 }
703 continue;
704 case 'D':
705 case 'd': // down
706 p = draw_getval(p, &y);
707 if (draw) {
708 dev_line(gra_x, gra_y, gra_x, gra_y + y);
709 }
710 if (update) {
711 gra_y += y;
712 }
713 continue;
714 case 'L':
715 case 'l': // left
716 p = draw_getval(p, &x);
717 if (draw) {
718 dev_line(gra_x, gra_y, gra_x - x, gra_y);
719 }
720 if (update) {
721 gra_x -= x;
722 }
723 continue;
724 case 'R':
725 case 'r': // right
726 p = draw_getval(p, &x);
727 if (draw) {
728 dev_line(gra_x, gra_y, gra_x + x, gra_y);
729 }
730 if (update) {
731 gra_x += x;
732 }
733 continue;
734 case 'E':
735 case 'e': // up & right
736 p = draw_getval(p, &x);
737 if (draw) {
738 dev_line(gra_x, gra_y, gra_x + x, gra_y - x);
739 }
740 if (update) {
741 gra_x += x;
742 gra_y -= x;
743 }
744 continue;
745 case 'F':
746 case 'f': // down & right
747 p = draw_getval(p, &x);
748 if (draw) {
749 dev_line(gra_x, gra_y, gra_x + x, gra_y + x);
750 }
751 if (update) {
752 gra_x += x;
753 gra_y += x;
754 }
755 continue;
756 case 'G':
757 case 'g': // down & left
758 p = draw_getval(p, &x);
759 if (draw) {
760 dev_line(gra_x, gra_y, gra_x - x, gra_y + x);
761 }
762 if (update) {
763 gra_x -= x;
764 gra_y += x;
765 }
766 continue;
767 case 'H':
768 case 'h': // up & left
769 p = draw_getval(p, &x);
770 if (draw) {
771 dev_line(gra_x, gra_y, gra_x - x, gra_y - x);
772 }
773 if (update) {
774 gra_x -= x;
775 gra_y -= x;
776 }
777 continue;
778 case 'M':
779 case 'm': // move to x, y
780 if (*(p + 1) == '-') { // relative
781 r = -1;
782 p++;
783 }
784 if (*(p + 1) == '+') { // relative
785 r = 1;
786 p++;
787 } else {
788 r = 0; // absolute
789 }
790 p = draw_getval(p, &x);
791 if (*p != ',') {
792 rt_raise(ERR_DRAW_SEP);
793 v_free(&var);
794 return;
795 } else {
796 // Haraszti -- next pointer forward is an error because draw_getval
797 // contain p++ too!!!
798 // p ++;
799 }
800 p = draw_getval(p, &y);
801
802 if (r) {
803 if (draw)
804 dev_line(gra_x, gra_y, gra_x + x * r, gra_y + y * r);
805 if (update) {
806 gra_x += x * r;
807 gra_y += x * r;
808 }
809 } else {
810 if (draw) {
811 dev_line(gra_x, gra_y, x, y);
812 }
813 if (update) {
814 gra_x = x;
815 gra_y = y;
816 }
817 }
818 continue;
819 case 'C':
820 case 'c': // color
821 p = draw_getval(p, &x);
822 dev_setcolor(x);
823 continue;
824 // Haraszti -- next case filter out the spaces or tabs and semicolons
825 // (GWBASIC compatibility)
826 case ' ':
827 case '\t':
828 case ';':
829 p++;
830 continue;
831 default:
832 rt_raise(ERR_DRAW_CMD, *p);
833 v_free(&var);
834 return;
835 }
836 p++;
837 }
838
839 dev_setcolor(prev_color);
840 v_free(&var);
841}
842
843//
844// CHART chart-type, v() [, mark-type [, x1, y1, x2, y2]]
845//
846// chart-type
847// 1 = line-chart
848// 2 = bar-chart
849//
850// mark-type (bit-mask)
851// 0 = none
852// 1 = labels
853// 2 = ruler
854//
855void cmd_chart_fstr(var_num_t v, char *buf) {
856 if (fabsl(v) >= 10E+9) {
857 ftostr(v / 1E+9, buf);
858 if (buf[3] == '.') {
859 buf[3] = '\0';
860 } else {
861 buf[4] = '\0';
862 }
863 strcat(buf, "G");
864 } else if (fabsl(v) >= 10E+6) {
865 ftostr(v / 1E+6, buf);
866 if (buf[3] == '.') {
867 buf[3] = '\0';
868 } else {
869 buf[4] = '\0';
870 }
871 strcat(buf, "M");
872 } else if (fabsl(v) >= 10E+3) {
873 ftostr(v / 1E+3, buf);
874 if (buf[3] == '.') {
875 buf[3] = '\0';
876 } else {
877 buf[4] = '\0';
878 }
879 strcat(buf, "K");
880 } else {
881 ftostr(v, buf);
882 buf[5] = '\0';
883 }
884}
885
886/*
887 * draw a chart
888 *
889 * x1,y1-x2,y2 = the area to draw
890 * vals = the values
891 * count = the number of the values
892 * xvals, xcount = for ruler the xvalues (use NULL for default)
893 * chart = chart type (1=line chart, 0=bar chart, 5=points)
894 * marks = marks type (2 & ruler, 1 & marks)
895 */
896void chart_draw(int x1, int y1, int x2, int y2, var_num_t *vals, int count,
897 var_num_t *xvals, int xcount, int chart, int marks) {
898 var_num_t lx, ly;
899 char buf[32];
900 int32_t color = 0;
901 int rx1 = x1;
902
903 // ready
904 dev_settextcolor(0, 15);
905 int *pts = (int *) malloc(sizeof(int) * count * 2);
906
907 if (marks & 0x2) {
908 // ruler
909 x1 += dev_textwidth("00000") + 1;
910 y2 -= (dev_textheight("0") + 1);
911 }
912
913 if (marks & 0x1) {
914 if (chart == 1) {
915 // line
916 x1 += 2;
917 x2 -= 2;
918 y1 += 2;
919 y2 -= 2;
920 }
921 }
922
923 int dx = (x2 - x1);
924 int dy = (y2 - y1);
925
926 // limits
927 var_num_t vmin = vals[0];
928 var_num_t vmax = vals[0];
929 for (int i = 1; i < count; i++) {
930 if (vmin > vals[i]) {
931 vmin = vals[i];
932 }
933 if (vmax < vals[i]) {
934 vmax = vals[i];
935 }
936 }
937
938 if (chart == 1) {
939 // line-chart
940 lx = ((var_num_t) dx) / (var_num_t) (count - 1);
941 } else {
942 lx = ((var_num_t) dx) / (var_num_t) count;
943 }
944 ly = ((var_num_t) dy) / (vmax - vmin);
945
946 // calc points
947 for (int i = 0; i < count; i++) {
948 int x = x1 + i * lx;
949 int y = y1 + (dy - ((vals[i] - vmin) * ly));
950 pts[i * 2] = x > 0 ? x : 0;
951 pts[i * 2 + 1] = y > 0 ? y : 0;
952 }
953
954 // draw ruler
955 if (marks & 0x2) {
956 // vertical
957 int fh = dev_textheight("0");
958 int n = dy / (fh * 1.5);
959
960 if ((n - 1) > 0) {
961 for (int i = 0; i <= n; i++) {
962 var_num_t v;
963 if (i == 0) {
964 v = vmin;
965 }
966 else if (i == n) {
967 v = vmax;
968 } else {
969 v = vmin + (((var_num_t) i + 1) * ((vmax - vmin) / (var_num_t) (n + 1)));
970 }
971 cmd_chart_fstr(v, buf);
972
973 int y = y1 + (dy - ((v - vmin) * ly));
974 if (i != 0) {
975 dev_setxy(rx1 + 1, y + 1, 0);
976 } else {
977 dev_setxy(rx1 + 1, y - fh, 0);
978 }
979 dev_print(buf);
980 dev_line(x1 - 4, y, x1, y);
981 }
982 }
983
984 // horizontal
985 int fw = dev_textwidth("000");
986 n = -1;
987 if (count <= 24) {
988 if (count * (fw * 1.34) < dx) {
989 n = count;
990 }
991 }
992
993 if (n == -1) {
994 n = dx / (fw * 1.5);
995 }
996 if ((n - 1) > 0) {
997 for (int i = 0; i < n; i++) {
998 var_num_t v;
999 if (i == 0) {
1000 v = 0;
1001 } else {
1002 v = i * ((var_num_t) count / (var_num_t) n);
1003 }
1004 if (xvals) {
1005 // I have xvals
1006 var_num_t x;
1007 var_num_t xmin = xvals[0];
1008 var_num_t xmax = xvals[xcount - 1];
1009 var_num_t dx = xmax - xmin;
1010 if (i == 0) {
1011 x = xmin;
1012 } else if (i == n) {
1013 x = xmax;
1014 } else {
1015 x = xmin + ((dx / n) * i);
1016 }
1017 ftostr(x, buf);
1018 } else {
1019 // i don't have xvals
1020 ftostr(i + 1, buf);
1021 }
1022
1023 buf[3] = '\0';
1024 fw = dev_textwidth(buf);
1025
1026 int x = x1 + v * lx;
1027 if (chart == 1 || chart == 5) {
1028 dev_setxy(x - fw, y2 + 1, 0);
1029 } else {
1030 if (x + fw + 1 < x2) {
1031 dev_setxy(x + 1, y2 + 1, 0);
1032 }
1033 }
1034
1035 dev_print(buf);
1036 dev_line(x, y2, x, y2 + 4);
1037 }
1038 }
1039
1040 dev_line(x1, y1, x1, y2);
1041 dev_line(x1, y2, x2, y2);
1042
1043 x1++;
1044 y2--;
1045 dx = (x2 - x1);
1046 dy = (y2 - y1);
1047 }
1048
1049 // draw
1050 switch (chart) {
1051 case 1:
1052 case 5:
1053 // line chart
1054 // points
1055 if (chart == 5) {
1056 for (int i = 0; i < count; i++) {
1057 dev_setpixel(pts[i * 2], pts[i * 2 + 1]);
1058 }
1059 } else {
1060 for (int i = 1; i < count; i++) {
1061 dev_line(pts[(i - 1) * 2], pts[(i - 1) * 2 + 1], pts[i * 2], pts[i * 2 + 1]);
1062 }
1063 }
1064
1065 // draw marks
1066 if (marks & 0x1) {
1067 for (int i = 0; i < count; i++) {
1068 cmd_chart_fstr(vals[i], buf);
1069
1070 int fw = dev_textwidth(buf);
1071 int fh = dev_textheight(buf);
1072 int mx = pts[i * 2] - fw / 2;
1073 int my = pts[i * 2 + 1];
1074
1075 if (my > (y1 + (y2 - y1) / 2)) {
1076 my -= fh;
1077 }
1078 if (mx <= x1) {
1079 mx = x1 + 1;
1080 }
1081 if (mx + fw >= x2) {
1082 mx = x2 - fw;
1083 }
1084 dev_setxy(mx, my, 0);
1085 dev_print(buf);
1086 dev_rect(pts[i * 2] - 2, pts[i * 2 + 1] - 2,
1087 pts[i * 2] + 2, pts[i * 2 + 1] + 2, 1);
1088 }
1089 }
1090 break;
1091
1092 case 2:
1093 // bar chart
1094 // draw rect
1095 color = 0;
1096 for (int i = 1; i < count; i++) {
1097 if (os_color_depth > 2) {
1098 dev_setcolor(color);
1099 color++;
1100 if (color >= 15) {
1101 color = 0;
1102 }
1103 }
1104 dev_rect(pts[(i - 1) * 2], pts[(i - 1) * 2 + 1], pts[i * 2] - 2, y2, 1);
1105 }
1106
1107 if (os_color_depth > 2) {
1108 dev_setcolor(color);
1109 }
1110 dev_rect(pts[(count - 1) * 2], pts[(count - 1) * 2 + 1],
1111 pts[(count - 1) * 2] + lx - 1, y2, 1);
1112
1113 // draw marks
1114 if (marks & 0x1) {
1115 color = 0;
1116 for (int i = 0; i < count; i++) {
1117 cmd_chart_fstr(vals[i], buf);
1118
1119 int fw = dev_textwidth(buf);
1120 int fh = dev_textheight(buf);
1121 int mx = pts[i * 2] + lx / 2 - fw / 2;
1122 int my = pts[i * 2 + 1];
1123
1124 if (os_color_depth > 2) {
1125 if (my - fh >= y1) {
1126 dev_settextcolor(0, 15);
1127 } else {
1128 if (color >= 7 && color != 8) {
1129 dev_settextcolor(0, color);
1130 } else {
1131 dev_settextcolor(15, color);
1132 }
1133 }
1134
1135 color++;
1136 if (color >= 15) {
1137 color = 0;
1138 }
1139 }
1140
1141 if (my - fh >= y1) {
1142 my -= fh;
1143 }
1144 if (mx <= x1) {
1145 mx = x1 + 1;
1146 }
1147 if (mx + fw >= x2) {
1148 mx = x2 - fw;
1149 }
1150 dev_setxy(mx, my, 0);
1151 dev_print(buf);
1152 }
1153 }
1154 break;
1155 };
1156
1157 free(pts);
1158}
1159
1160//
1161// CHART
1162//
1163void cmd_chart() {
1164 int32_t prev_fgcolor = dev_fgcolor;
1165 int32_t prev_bgcolor = dev_bgcolor;
1166 int x1 = 0;
1167 int y1 = 0;
1168 int x2 = os_graf_mx;
1169 int y2 = os_graf_my;
1170
1171 // chart type
1172 int chart = par_getint(); IF_PROG_ERR_RTN;
1173 par_getcomma(); IF_PROG_ERR_RTN;
1174
1175 // array
1176 var_t *var_p = par_getvarray(); IF_PROG_ERR_RTN;
1177 if (!var_p || var_p->type != V_ARRAY) {
1178 err_varisnotarray();
1179 return;
1180 }
1181
1182 // optional labels-flag
1183 int marks;
1184 if (code_peek() == kwTYPE_SEP) {
1185 par_getcomma(); IF_PROG_ERR_RTN;
1186 marks = par_getint(); IF_PROG_ERR_RTN;
1187
1188 // optional x1,y1,x2,y2
1189 if (code_peek() == kwTYPE_SEP) {
1190 par_getcomma(); IF_PROG_ERR_RTN;
1191 x1 = par_getint(); IF_PROG_ERR_RTN;
1192 par_getcomma(); IF_PROG_ERR_RTN;
1193 y1 = par_getint(); IF_PROG_ERR_RTN;
1194 par_getcomma(); IF_PROG_ERR_RTN;
1195 x2 = par_getint(); IF_PROG_ERR_RTN;
1196 par_getcomma(); IF_PROG_ERR_RTN;
1197 y2 = par_getint(); IF_PROG_ERR_RTN;
1198 }
1199 } else {
1200 marks = 0;
1201 }
1202
1203 // get array's values
1204 int count = v_asize(var_p);
1205 var_num_t *vals = (var_num_t *) malloc(sizeof(var_num_t) * count);
1206 for (int i = 0; i < count; i++) {
1207 var_t *elem_p = v_elem(var_p, i);
1208 if (prog_error) {
1209 free(vals);
1210 return;
1211 }
1212 switch (elem_p->type) {
1213 case V_INT:
1214 vals[i] = elem_p->v.i;
1215 break;
1216 case V_NUM:
1217 vals[i] = elem_p->v.n;
1218 break;
1219 case V_STR:
1220 vals[i] = v_getreal(elem_p);
1221 break;
1222 default:
1223 err_typemismatch();
1224 free(vals);
1225 return;
1226 }
1227 }
1228
1229 chart_draw(x1, y1, x2, y2, vals, count, NULL, 0, chart, marks);
1230
1231 free(vals);
1232 dev_settextcolor(prev_fgcolor, prev_bgcolor);
1233}
1234
1235var_t *par_getm3() {
1236 // array
1237 var_t *vp = par_getvarray();
1238 if (prog_error) {
1239 return NULL;
1240 }
1241 if (vp == NULL || vp->type != V_ARRAY || v_asize(vp) != 9) {
1242 err_typemismatch();
1243 return NULL;
1244 }
1245 return vp;
1246}
1247
1248void m3combine(var_t *m, var_num_t nm[3][3]) {
1249 var_num_t om[3][3];
1250 int i, j;
1251 var_t *e;
1252
1253 // copy m to om
1254 for (i = 0; i < 3; i++) {
1255 for (j = 0; j < 3; j++) {
1256 e = v_elem(m, (i * 3 + j));
1257 if (e->type == V_NUM) {
1258 om[i][j] = e->v.n;
1259 } else if (e->type == V_INT) {
1260 om[i][j] = e->v.i;
1261 } else {
1262 om[i][j] = v_getval(e);
1263 }
1264 }
1265 }
1266
1267 // combine
1268 for (i = 0; i < 3; i++) {
1269 for (j = 0; j < 3; j++) {
1270 e = v_elem(m, (i * 3 + j));
1271 if (e->type != V_NUM) {
1272 v_free(e);
1273 }
1274 e->type = V_NUM;
1275 e->v.n = nm[i][0] * om[0][j] + nm[i][1] * om[1][j] + nm[i][2] * om[2][j];
1276 }
1277 }
1278
1279}
1280
1281//
1282void m3ident(var_num_t m[3][3]) {
1283 int i, j;
1284
1285 for (i = 0; i < 3; i++) {
1286 for (j = 0; j < 3; j++) {
1287 m[i][j] = (i == j) ? 1.0 : 0.0;
1288 }
1289 }
1290}
1291
1292//
1293// M3IDENT BYREF m3x3
1294//
1295void cmd_m3ident() {
1296 var_t *m, *e;
1297 int i, j;
1298
1299 m = par_getm3();
1300 if (prog_error) {
1301 return;
1302 }
1303 for (i = 0; i < 3; i++) {
1304 for (j = 0; j < 3; j++) {
1305 e = v_elem(m, (i * 3 + j));
1306 v_init(e);
1307 e->type = V_NUM;
1308 e->v.n = (i == j) ? 1.0 : 0.0;
1309 }
1310 }
1311}
1312
1313//
1314// M3ROTATE BYREF m3x3, angle[, x, y]
1315//
1316void cmd_m3rotate() {
1317 var_t *m;
1318 var_num_t angle, x = 0, y = 0, c, s;
1319 var_num_t matrix[3][3];
1320
1321 m = par_getm3();
1322 if (prog_error) {
1323 return;
1324 }
1325 par_getcomma();
1326 if (prog_error) {
1327 return;
1328 }
1329 angle = par_getnum();
1330 if (prog_error) {
1331 return;
1332 }
1333 if (code_peek() == kwTYPE_SEP) {
1334 par_getcomma();
1335 if (prog_error)
1336 return;
1337 x = par_getnum();
1338 if (prog_error)
1339 return;
1340 par_getcomma();
1341 if (prog_error)
1342 return;
1343 y = par_getnum();
1344 if (prog_error) {
1345 return;
1346 }
1347 }
1348
1349 c = cos(angle);
1350 s = sin(angle);
1351
1352 m3ident(matrix);
1353 matrix[0][0] = c;
1354 matrix[0][1] = s;
1355 matrix[1][0] = -s;
1356 matrix[1][1] = c;
1357 matrix[2][0] = (1.0 - c) * x + (s * y);
1358 matrix[2][1] = (1.0 - c) * y - (s * x);
1359 m3combine(m, matrix);
1360}
1361
1362//
1363// M3SCALE BYREF m3x3, x, y, fx, fy
1364//
1365void cmd_m3scale() {
1366 var_t *m;
1367 var_num_t x, y, fx, fy;
1368 var_num_t matrix[3][3];
1369
1370 m = par_getm3();
1371 if (prog_error)
1372 return;
1373 par_getcomma();
1374 if (prog_error)
1375 return;
1376 x = par_getnum();
1377 if (prog_error)
1378 return;
1379 par_getcomma();
1380 if (prog_error)
1381 return;
1382 y = par_getnum();
1383 if (prog_error)
1384 return;
1385 par_getcomma();
1386 if (prog_error)
1387 return;
1388 fx = par_getnum();
1389 if (prog_error)
1390 return;
1391 par_getcomma();
1392 if (prog_error)
1393 return;
1394 fy = par_getnum();
1395 if (prog_error)
1396 return;
1397
1398 m3ident(matrix);
1399 matrix[0][0] = fx;
1400 matrix[1][1] = fy;
1401 matrix[2][0] = (1.0 - fx) * x;
1402 matrix[2][1] = (1.0 - fy) * y;
1403 m3combine(m, matrix);
1404}
1405
1406//
1407// M3TRANS BYREF m3x3, x, y
1408//
1409void cmd_m3translate() {
1410 var_t *m;
1411 var_num_t x, y;
1412 var_num_t matrix[3][3];
1413
1414 m = par_getm3();
1415 if (prog_error)
1416 return;
1417 par_getcomma();
1418 if (prog_error)
1419 return;
1420 x = par_getnum();
1421 if (prog_error)
1422 return;
1423 par_getcomma();
1424 if (prog_error)
1425 return;
1426 y = par_getnum();
1427 if (prog_error)
1428 return;
1429
1430 m3ident(matrix);
1431 matrix[2][0] = x;
1432 matrix[2][1] = y;
1433 m3combine(m, matrix);
1434}
1435
1436//
1437// M3APPLY m3x3, BYREF poly
1438//
1439void cmd_m3apply() {
1440 var_t *m, *p, *e;
1441 var_num_t om[3][3], x, y;
1442 int i, j, count;
1443
1444 m = par_getm3();
1445 if (prog_error)
1446 return;
1447 par_getcomma();
1448 if (prog_error)
1449 return;
1450 p = par_getvarray();
1451 if (prog_error)
1452 return;
1453 count = v_asize(p);
1454
1455 // copy m to om
1456 for (i = 0; i < 3; i++) {
1457 for (j = 0; j < 3; j++) {
1458 e = v_elem(m, i * 3 + j);
1459 om[i][j] = v_getreal(e);
1460 }
1461 }
1462
1463 // apply
1464 e = v_elem(p, 0);
1465 if (e->type != V_ARRAY) {
1466 int o;
1467
1468 count = (v_asize(p) >> 1);
1469 for (i = 0; i < count; i++) {
1470 o = i << 1;
1471 x = v_getreal(v_elem(p, o));
1472 y = v_getreal(v_elem(p, o + 1));
1473 v_setreal(v_elem(p, o), x * om[0][0] + y * om[1][0] + om[2][0]);
1474 v_setreal(v_elem(p, o + 1), x * om[0][1] + y * om[1][1] + om[2][1]);
1475 }
1476 } else {
1477 for (i = 0; i < count; i++) {
1478 e = v_elem(p, i);
1479
1480 if (e->type != V_ARRAY)
1481 err_parsepoly(i, 10);
1482 else if ((v_asize(e) % 2) != 0)
1483 err_parsepoly(i, 11);
1484
1485 if (prog_error)
1486 break;
1487
1488 x = v_getreal(v_elem(e, 0));
1489 y = v_getreal(v_elem(e, 1));
1490 v_setreal(v_elem(e, 0), x * om[0][0] + y * om[1][0] + om[2][0]);
1491 v_setreal(v_elem(e, 1), x * om[0][1] + y * om[1][1] + om[2][1]);
1492 }
1493 }
1494}
1495
1496//
1497// INTERSECT a.x, a.y, b.x, b.y, c.x, c.y, d.x, d.y, BYREF type, BYREF r.x, BYREF r.y
1498//
1499void cmd_intersect() {
1500 var_num_t a, b, c, s;
1501 pt_t A, B, C, D, R;
1502 var_t *type, *vrx, *vry = NULL;
1503 byte style = 0;
1504
1505 // parameters
1506 A = par_getpt();
1507 if (prog_error)
1508 return;
1509 par_getcomma();
1510 if (prog_error)
1511 return;
1512 B = par_getpt();
1513 if (prog_error)
1514 return;
1515 par_getcomma();
1516 if (prog_error)
1517 return;
1518 C = par_getpt();
1519 if (prog_error)
1520 return;
1521 par_getcomma();
1522 if (prog_error)
1523 return;
1524 D = par_getpt();
1525 if (prog_error)
1526 return;
1527 par_getcomma();
1528 if (prog_error)
1529 return;
1530
1531 type = par_getvar_ptr();
1532 if (prog_error)
1533 return;
1534 par_getcomma();
1535 if (prog_error)
1536 return;
1537
1538 vrx = par_getvar_ptr();
1539 if (prog_error)
1540 return;
1541 if (code_peek() == kwTYPE_SEP) {
1542 par_getcomma();
1543 if (prog_error)
1544 return;
1545 vry = par_getvar_ptr();
1546 if (prog_error)
1547 return;
1548 } else
1549 style = 1;
1550
1551 // initialize vars
1552 v_free(type);
1553 R.x = R.y = 0;
1554
1555 //
1556 a = (B.y - A.y) * (D.x - C.x) - (B.x - A.x) * (D.y - C.y);
1557 b = (A.x - C.x) * (D.y - C.y) - (A.y - C.y) * (D.x - C.x);
1558 c = (A.x - C.x) * (B.y - A.y) - (A.y - C.y) * (B.x - A.x);
1559
1560 if (a == 0.0)
1561 type->v.i = (b == 0.0) ? 3 : 2;
1562 else {
1563 if (a > 0.0) {
1564 if ((b >= 0.0 && b <= a) && (c >= 0.0 && c <= a)) {
1565 type->v.i = 1;
1566 } else {
1567 type->v.i = 0;
1568 }
1569 } else {
1570 if ((b <= 0.0 && b >= a) && (c <= 0.0 && c >= a)) {
1571 type->v.i = 1;
1572 } else {
1573 type->v.i = 0;
1574 }
1575 }
1576 }
1577
1578 //
1579 if (type->v.i == 1 || type->v.i == 0) {
1580
1581 if (b == a || c == a || c == 0.0)
1582 if (type->v.i == 1)
1583 type->v.i = 4; // Special case
1584
1585 s = b / a;
1586
1587 if (C.x == D.x)
1588 R.x = C.x;
1589 else
1590 R.x = A.x + ((B.x - A.x) * s);
1591
1592 if (C.y == D.y)
1593 R.y = C.y;
1594 else
1595 R.y = A.y + ((B.y - A.y) * s);
1596 }
1597
1598 //
1599 if (style == 1) {
1600 v_toarray1(vrx, 2);
1601 v_setreal(v_elem(vrx, 0), R.x);
1602 v_setreal(v_elem(vrx, 1), R.y);
1603 } else {
1604 v_setreal(vrx, R.x);
1605 v_setreal(vry, R.y);
1606 }
1607}
1608
1609//
1610// POLYEXT poly, BYREF xmin, BYREF ymin, BYREF xmax, BYREF ymax
1611//
1612void cmd_polyext() {
1613 var_t *xmin, *ymin, *xmax, *ymax;
1614 int count, i;
1615 pt_t *poly = NULL;
1616
1617 count = par_getpoly(&poly);
1618 if (prog_error)
1619 return;
1620
1621 par_massget("PPPP", &xmin, &ymin, &xmax, &ymax);
1622 if (prog_error) {
1623 free(poly);
1624 return;
1625 }
1626
1627 // initialize vars
1628 v_free(xmin);
1629 xmin->type = V_NUM;
1630 v_free(xmax);
1631 xmax->type = V_NUM;
1632 v_free(ymin);
1633 ymin->type = V_NUM;
1634 v_free(ymax);
1635 ymax->type = V_NUM;
1636
1637 if (count == 0) {
1638 xmin->v.n = ymin->v.n = xmax->v.n = ymax->v.n = 0.0;
1639 free(poly);
1640 return;
1641 }
1642
1643 xmin->v.n = xmax->v.n = poly[0].x;
1644 ymin->v.n = ymax->v.n = poly[0].y;
1645 for (i = 1; i < count; i++) {
1646 if (poly[i].x > xmax->v.n)
1647 xmax->v.n = poly[i].x;
1648 if (poly[i].x < xmin->v.n)
1649 xmin->v.n = poly[i].x;
1650
1651 if (poly[i].y > ymax->v.n)
1652 ymax->v.n = poly[i].y;
1653 if (poly[i].y < ymin->v.n)
1654 ymin->v.n = poly[i].y;
1655 }
1656
1657 free(poly);
1658}
1659