1 /****************************************************************************
2  * Copyright (C) 2008 by Matteo Franchin                                    *
3  *                                                                          *
4  * This file is part of Box.                                                *
5  *                                                                          *
6  *   Box is free software: you can redistribute it and/or modify it         *
7  *   under the terms of the GNU Lesser General Public License as published  *
8  *   by the Free Software Foundation, either version 3 of the License, or   *
9  *   (at your option) any later version.                                    *
10  *                                                                          *
11  *   Box is distributed in the hope that it will be useful,                 *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
14  *   GNU Lesser General Public License for more details.                    *
15  *                                                                          *
16  *   You should have received a copy of the GNU Lesser General Public       *
17  *   License along with Box.  If not, see <http://www.gnu.org/licenses/>.   *
18  ****************************************************************************/
19 
20 /* rasterizer.c - ottobre 2002
21  *
22  * Questo file contiene quanto basta per poter ...
23  */
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <math.h>
28 
29 /* De-commentare per includere i messaggi di debug */
30 /*#define DEBUG*/
31 
32 #include "error.h"
33 #include "graphic.h"
34 #include "g.h"
35 
36 /* Conversione da coordinate relative a coordinate assolute (floating) */
37 #define CV_XF_A(w, x)  (((BoxReal) (x) - (w)->ltx)/(w)->stepx)
38 #define CV_YF_A(w, y)  (((BoxReal) (y) - (w)->lty)/(w)->stepy)
39 /* Conversione di lunghezze relative in lunghezze assolute */
40 #define CV_LXF_A(w, x)  (((BoxReal) (x))/(w)->stepx)
41 #define CV_LYF_A(w, y)  (((BoxReal) (y))/(w)->stepy)
42 /* Conversione da coordinate assolute a coordinate intermedie */
43 #define CV_A_MED(x)  ((int) floor(x) + (int) ceil(x))
44 /* Conversione da coordinate intermedie a coordinate intere */
45 #define CV_MED_GT(x)  (((BoxInt) (x) + 1) >> 1)
46 #define CV_MED_LW(x)  (((BoxInt) (x) - 1) >> 1)
47 
48 /* Informazioni sulla corrente finestra grafica */
49 #define GRP_MLOUT    -1
50 #define GRP_MROUT    0x7fff
51 
52 #define RSTBLOCKDIM    16384
53 #define MAXROWPERBLOCK  8192
54 
55 #define WORD      unsigned short
56 #define SWORD     short
57 
58 #define GRP_AXMIN(w) (0)
59 #define GRP_AXMAX(w) ((w)->numptx - 1)
60 #define GRP_AYMIN(w) (0)
61 #define GRP_AYMAX(w) ((w)->numpty - 1)
62 #define GRP_IXMIN(w) (0)
63 #define GRP_IXMAX(w) ((w)->numptx - 1)
64 #define GRP_IYMIN(w) (0)
65 #define GRP_IYMAX(w) ((w)->numpty - 1)
66 
67 #define GRP_IXMIN(w) (0)
68 #define GRP_IXMAX(w) ((w)->numptx - 1)
69 #define GRP_IYMIN(w) (0)
70 #define GRP_IYMAX(w) ((w)->numpty - 1)
71 
72 #define WINNUMROW(w) ((w)->numpty)
73 
74 struct block_desc {
75   SWORD  ymin;    /* Coordinata y corrispondente alla prima riga descritta */
76   SWORD  ymax;    /* Coordinata y corrispondente all'ultima riga descritta */
77   WORD  dim;    /* Dimensioni totali del buffer in WORD */
78   WORD  *buffer;  /* Puntatore al buffer */
79   WORD  numfree;  /* Numero di "posti" (cioe' WORD) ancora liberi */
80   WORD  winext;    /* Posizione in WORD del prossimo posto libero */
81   WORD  *inext;    /* Puntatore al prossimo posto libero */
82   struct block_desc *next;  /* Puntatore al prossimo blocco */
83 };
84 
85 typedef struct block_desc block_desc;
86 
87 block_desc *first = 0;
88 
89 /* Procedure esterne da error.c */
90 extern void err_print(FILE *stream);
91 
92 /***************************************************************************************/
93 /* MACRO DEFINITE IN QUESTO FILE */
94 
95 /* Questa macro viene utilizzata per inserire un marcatore di bordo
96  * (nelle procedure rst__line e rst__cong) e serve a tenere conto
97  * degli eventuali "sconfinamenti" del bordo rispetto allo schermo.
98  */
99 #define MARK(w, y, x) \
100   if ((x) < GRP_AXMIN(w)) \
101     rst__mark((w), (y), GRP_MLOUT); \
102   else if (x > GRP_AXMAX(w)) \
103     rst__mark((w), (y), GRP_MROUT); \
104   else \
105     rst__mark((w), (y), CV_A_MED(x))
106 
107 /***************************************************************************************/
108 /* DICHIARAZIONE DELLE PROCEDURE DEFINITE IN QUESTO FILE */
109 
110 /* Le procedure che iniziano con rst__ sono procedure riservate
111  * scritte per essere utilizzate solo da altre procedure di questo file
112  */
113 void rst__block_reset(block_desc *rstblock);
114 block_desc *rst__trytomark(BoxGWin *w, SWORD y, SWORD x);
115 void rst__mark(BoxGWin *w, SWORD y, SWORD x);
116 void rst__line(BoxGWin *w, BoxPoint *pa, BoxPoint *pb);
117 void rst__cong(BoxGWin *w, BoxPoint *pa, BoxPoint *pb, BoxPoint *pc);
118 void rst__curve(BoxGWin *w, BoxPoint *pa, BoxPoint *pb, BoxPoint *pc, BoxReal c);
119 void rst__poly(BoxGWin *w, BoxPoint *p, int n);
120 
121 /* Le procedure "di libreria" definite per essere chiamate dall'esterno
122  * iniziano con rst_
123  */
124 static void My_Add_Create_Path(BoxGWin *w);
125 static void My_Begin_Drawing(BoxGWin *w);
126 static void My_Draw_Path(BoxGWin *w, DrawStyle *style);
127 static void My_Add_Line_Path(BoxGWin *w, BoxPoint *a, BoxPoint *b);
128 static void My_Add_Join_Path(BoxGWin *w, BoxPoint *a, BoxPoint *b, BoxPoint *c);
129 static void My_Close_Path(BoxGWin *w);
130 void rst_curve(BoxGWin *w, BoxPoint *a, BoxPoint *b, BoxPoint *c, BoxReal cut);
131 static void My_Add_Circle_Path(BoxGWin *w, BoxPoint *ctr, BoxPoint *a, BoxPoint *b);
132 void rst_poly(BoxGWin *w, BoxPoint *p, int n);
133 static void My_Fg_Color(BoxGWin *w, Color *c);
134 static void My_Bg_Color(BoxGWin *w, Color *c);
135 static void My_Add_Fake_Point(BoxGWin *w, BoxPoint *p);
136 
rst_repair(BoxGWin * w)137 void rst_repair(BoxGWin *w) {
138   w->create_path = My_Add_Create_Path;
139   w->begin_drawing = My_Begin_Drawing;
140   w->draw_path = My_Draw_Path;
141   w->add_line_path = My_Add_Line_Path;
142   w->add_join_path = My_Add_Join_Path;
143   w->close_path = My_Close_Path;
144   w->add_circle_path = My_Add_Circle_Path;
145   w->set_fg_color = My_Fg_Color;
146   w->set_bg_color = My_Bg_Color;
147   w->add_fake_point = My_Add_Fake_Point;
148 }
149 
150 /***************************************************************************************/
151 /* PROCEDURE PER LA RASTERIZZAZIONE DI POLIGONI ED ALTRE FIGURE */
152 
153 /* NOME: rst__block_reset
154  * DESCRIZIONE: Resetta il blocco di rasterizzazione specificato.
155  *  Dunque il blocco viene svuotato e il suo contenuto va perso.
156  */
rst__block_reset(block_desc * rstblock)157 void rst__block_reset(block_desc *rstblock) {
158   WORD y;
159   WORD *ptr;
160 
161   y = rstblock->ymax - rstblock->ymin + 1;
162   rstblock->numfree = RSTBLOCKDIM - y;
163   rstblock->winext = RSTBLOCKDIM - 1;
164   rstblock->inext = rstblock->buffer + (WORD) rstblock->winext;
165 
166   for(ptr = rstblock->buffer; y-- > 0; ptr++)
167     *ptr = (WORD) 0;
168 }
169 
170 /* NOME: My_Add_Create_Path
171  * DESCRIZIONE: Pulisce il buffer di rasterizzazione,
172  *  senza deallocare memoria.
173  */
My_Add_Create_Path(BoxGWin * w)174 static void My_Add_Create_Path(BoxGWin *w) {
175   block_desc *rstblock;
176 
177   /* Facciamo un loop su tutti i blocchi */
178   for(rstblock = first; rstblock != (block_desc *) 0; rstblock = rstblock->next)
179     rst__block_reset(rstblock);  /* Puliamo ciascun blocco */
180 }
181 
182 /* NOME: My_Begin_Drawing
183  * DESCRIZIONE: Prepara il sistema nel suo stato iniziale,
184  *  pronto per rasterizzare.
185  */
My_Begin_Drawing(BoxGWin * w)186 static void My_Begin_Drawing(BoxGWin *w) {
187   int i, numblockmin, numblockalc = 0;
188   WORD ymin, ymax;
189   WORD *bufptr;
190   block_desc *rstblock;
191 
192   ymax = -1;
193   numblockmin = (WINNUMROW(w) + MAXROWPERBLOCK - 1) / MAXROWPERBLOCK;
194 
195   /* Ora conto i blocchi fin'ora allocati */
196   for(rstblock = first; rstblock != (block_desc *) 0; rstblock = rstblock->next)
197     ++numblockalc;
198 
199   if (numblockalc < numblockmin) {
200     /* Devo allocare altri blocchi */
201     numblockalc = numblockmin - numblockalc;
202 
203     for (i=0; i<numblockalc; i++) {
204       rstblock = (block_desc *) malloc( sizeof(block_desc) );
205       bufptr = (WORD *) malloc( RSTBLOCKDIM * sizeof(WORD) );
206 
207       if ( (rstblock == (block_desc *) 0) || (bufptr == (WORD *) 0) ) {
208         ERRORMSG("My_Begin_Drawing", "Memoria esaurita");
209         return;
210       }
211 
212       rstblock->buffer = bufptr;
213       rstblock->dim = RSTBLOCKDIM;
214       rstblock->next = first;
215 
216       first = rstblock;
217     }
218   } else if (numblockalc > numblockmin) {
219     /* Ho blocchi in piu': li dealloco */
220     numblockalc -= numblockmin;
221 
222     for (i=0; i<numblockalc; i++) {
223       rstblock = first;
224       first = rstblock->next;
225       free(rstblock->buffer);
226       free(rstblock);
227     }
228   }
229 
230   /* Adesso ho un numero giusto di blocchi allocati.
231    * Devo assegnare loro le righe e pulirli.
232    */
233   ymax = -1;
234   rstblock = first;
235   for(i=0; i<numblockalc; i++) {
236     ymin = ymax + 1;
237     ymax += MAXROWPERBLOCK;
238     if (ymax < WINNUMROW(w)) {
239       rstblock->ymin = ymin;
240       rstblock->ymax = ymax;
241       rst__block_reset(rstblock);
242 
243       rstblock = rstblock->next;
244     } else {
245       rstblock->ymin = ymin;
246       rstblock->ymax = WINNUMROW(w) - 1;
247       rst__block_reset(rstblock);
248     }
249   }
250 }
251 
252 /* NOME: rst__trytomark
253  * DESCRIZIONE: Prova ad inserire un bordo alla riga y, in ascissa x
254  *  Se non c'e' abbastanza memoria per farlo rinuncia
255  *  e restituisce il puntatore al blocco pieno, in cui andava
256  *  inserito il marcatore di bordo, altrimenti restituisce 0.
257  *  In tal caso, a meno che non si sia verificato un errore,
258  *  l'operazione e' stata completata con successo.
259  */
rst__trytomark(BoxGWin * w,SWORD y,SWORD x)260 block_desc *rst__trytomark(BoxGWin *w, SWORD y, SWORD x) {
261   WORD *ptr, *newptr;
262   block_desc *rstblock;
263 
264   /* Cerchiamo la riga y fra quelle presenti nei blocchi */
265   for(rstblock = first; rstblock != NULL; rstblock = rstblock->next) {
266     if (y >= rstblock->ymin && y <= rstblock->ymax) {
267       /* Ho trovato il blocco che "contiene" la riga y.
268        * Ora controllo se questo blocco pu contenere un altro bordo
269        */
270       if (rstblock->numfree < 2) {
271         /* Il blocco e' pieno: rinuncio all'operazione */
272         return rstblock;
273 
274       } else {
275         /* C'e' spazio per inserire un altro marcatore di bordo.
276          * Allora faccio un loop per vedere a che punto della riga
277          * questo debba essere inserito.
278          */
279         y -= rstblock->ymin;
280         for (newptr = (WORD *) rstblock->buffer + (WORD) y;
281              *newptr != (WORD) 0; newptr = ptr) {
282           ptr = (WORD *) rstblock->buffer + (WORD) *newptr;
283           if (*(ptr+1) > x)
284             break;
285         }
286 
287         /* ptr punta al marcatore in seguito al quale
288          * devo aggiungere quello nuovo
289          */
290         *(rstblock->inext--) = x;
291         *(rstblock->inext--) = *newptr;
292         *newptr = --rstblock->winext;
293         --rstblock->winext;
294         rstblock->numfree -= 2;
295       }
296 
297       return NULL;
298     }
299   }
300 
301   ERRORMSG("rst__trytomark", "Nessun blocco contiene la riga y");
302   return NULL;
303 }
304 
305 /* NOME: rst__mark
306  * DESCRIZIONE: Inserisce un marcatore di bordo alla riga y, colonna x.
307  *  Se non c'e' abbastanza memoria nella riga, alloca nuova memoria
308  * in modo da poter concludere l'operazione.
309  */
rst__mark(BoxGWin * w,SWORD y,SWORD x)310 void rst__mark(BoxGWin *w, SWORD y, SWORD x) {
311   block_desc *rstblock;
312 
313   /* Se la libreria di rasterizzazione non e' stata ancora inizializzata
314    * allora provvediamo subito!
315    */
316   if (first == NULL)
317     My_Begin_Drawing(w);
318 
319   rstblock = rst__trytomark(w, y, x);
320   if (rstblock != NULL) {
321     /* In questo caso non c'e' spazio sufficiente nel blocco rstblock
322      * per inserire un nuovo marcatore di bordo: dobbiamo allocare
323      * un nuovo blocco.
324      */
325     WORD y;
326     WORD *bufptr, *row1, *row2, *col1;
327     block_desc *newrstblock;
328 
329     newrstblock = (block_desc *) malloc( sizeof(block_desc) );
330     bufptr = (WORD *) malloc( RSTBLOCKDIM * sizeof(WORD) );
331 
332     if (newrstblock == NULL || bufptr == NULL) {
333       ERRORMSG("rst_mark", "Memoria esaurita");
334       return;
335     }
336 
337     newrstblock->buffer = bufptr;
338     newrstblock->dim = RSTBLOCKDIM;
339     newrstblock->winext = RSTBLOCKDIM - 1;
340     newrstblock->inext = rstblock->buffer + (WORD) rstblock->winext;
341 
342     /* Ora copiamo il vecchio blocco nel nuovo
343      * e cosi' facendo lo riordiniamo
344      */
345     row1 = rstblock->buffer;
346     row2 = newrstblock->buffer;
347 
348     /* Facciamo un loop su tutte le righe contenute nel blocco */
349     for(y = rstblock->ymin; y <= rstblock->ymax; y++) {
350       row2++;
351       for (col1 = row1++; *col1 != (WORD) 0;
352            col1 = (WORD *) rstblock->buffer + (WORD) *col1) {}
353     }
354 
355     ERRORMSG("rst_mark", "Feature in fase di implementazione");
356   }
357 }
358 
359 /* NOME: My_Draw_Path
360  * DESCRIZIONE: Legge il contenuto del buffer di rasterizzazione
361  *  e traccia il disegno corrispondente sulla finestra grafica.
362  */
My_Draw_Path(BoxGWin * w,DrawStyle * style)363 static void My_Draw_Path(BoxGWin *w, DrawStyle *style) {
364   int border;
365   FillStyle fs = style->fill_style;
366   SWORD y, x1=0, x2, xmin;
367   WORD *row, *col;
368   block_desc *rstblock;
369   static int msg_already_displayed = 0;
370 
371   switch(fs) {
372   case FILLSTYLE_VOID: return;
373   case FILLSTYLE_EO: break;
374   default:
375     if (! msg_already_displayed) {
376       g_warning("Unsupported drawing style: using even-odd fill algorithm!");
377       msg_already_displayed = 1;
378     }
379     fs = FILLSTYLE_EO;
380     break;
381   }
382 
383   if (style->bord_width > 0.0) {
384     g_warning("Unsupported drawing style: border cannot be traced!");
385   }
386 
387   /* Facciamo un loop in modo da considerare tutti i blocchi */
388   for(rstblock = first; rstblock != (block_desc *) 0;
389       rstblock = rstblock->next) {
390     /* Facciamo un loop su tutte le righe contenute nel blocco */
391     row = rstblock->buffer;
392     for(y = rstblock->ymin; y <= rstblock->ymax; y++) {
393       /* Ora disegnamo ognuna delle righe */
394       xmin = 0;
395       if (fs == FILLSTYLE_EO) {
396         border = 0;
397         for (col = row++; *col != (WORD) 0;) {
398           col = (WORD *) rstblock->buffer + (WORD) *col;
399 
400           if (border == 1) {
401             /* Bordo destro: traccio la linea */
402             x2 = CV_MED_LW(*(col+1));
403 
404             if (x2 >= xmin) {
405               BoxGWin_Draw_Hor_Line(w, y, x1, x2);
406               xmin = x2 + 1;
407             }
408           } else {
409             /* Bordo sinistro */
410             x1 = CV_MED_GT(*(col+1));
411             if (x1 > xmin) xmin = x1;
412           }
413           border ^= 1;
414         }
415 
416       } else {
417         int num = 0;
418         /* Plain fill */
419         for (col = row++; *col != (WORD) 0;) {
420           col = (WORD *) rstblock->buffer + (WORD) *col;
421           if (num == 0)
422             x1 = CV_MED_GT(*(col+1));
423           else
424             x2 = CV_MED_GT(*(col+1));
425           ++num;
426         }
427 
428         if (num > 1)
429           BoxGWin_Draw_Hor_Line(w, y, x1, x2);
430       }
431     }
432   }
433 }
434 
435 /* NOME: My_Add_Line_Path
436  * DESCRIZIONE: Rasterizza la linea congiungente i due punti a e b.
437  * NOTA: Le coordinate dei punti sono coordinate relative.
438  */
My_Add_Line_Path(BoxGWin * w,BoxPoint * a,BoxPoint * b)439 static void My_Add_Line_Path(BoxGWin *w, BoxPoint *a, BoxPoint *b) {
440   BoxPoint ia, ib;
441   ia.x = CV_XF_A(w, a->x); ia.y = CV_YF_A(w, a->y);
442   ib.x = CV_XF_A(w, b->x); ib.y = CV_YF_A(w, b->y);
443 
444   rst__line(w, & ia, & ib);
445 }
446 
447 /* NOME: rst__line
448  * DESCRIZIONE: Rasterizza la linea congiungente i due punti *pa e *pb.
449  * NOTA: Le coordinate dei punti sono coordinate assolute.
450  */
rst__line(BoxGWin * w,BoxPoint * pa,BoxPoint * pb)451 void rst__line(BoxGWin *w, BoxPoint *pa, BoxPoint *pb) {
452   BoxReal ly;
453 
454   if (pa->y > pb->y) { register BoxPoint *tmp; tmp = pa; pa = pb; pb = tmp; }
455 
456   ly = pb->y - pa->y;
457   if (ly < 0.95) {
458     /* In tal caso ho solo un valore di y, oppure nessuno */
459     BoxInt y1, y2;
460 
461     if (pb->y < GRP_AYMIN(w) || pa->y > GRP_AYMAX(w)) return;
462 
463     y1 = CV_MED_GT(CV_A_MED(pa->y));
464     y2 = CV_MED_LW(CV_A_MED(pb->y));
465 
466     if (y1 == y2) {
467       /* Solo in questo caso la retta "si vede" */
468       BoxReal x;
469 
470       x = pa->x + ((((BoxReal) y1) - pa->y)/ly)*(pb->x - pa->x);
471 
472       MARK(w, y1, x);
473     }
474 
475   } else {
476     /* In tal caso la retta ha una inclinazione rilevante */
477     BoxInt y1, y2, y;
478     BoxReal a, b, x;
479 
480     /* Esco in caso di linea non visibile (sopra o sotto) */
481     if (pb->y < GRP_AYMIN(w) || pa->y > GRP_AYMAX(w)) return;
482 
483     a = (pb->x - pa->x)/ly;
484     b = pa->x - pa->y * a;
485 
486     if (pa->y < GRP_AYMIN(w))
487           y1 = GRP_IYMIN(w);
488         else
489       y1 = CV_MED_GT(CV_A_MED(pa->y));
490 
491     if (pb->y > GRP_AYMAX(w))
492       y2 = GRP_IYMAX(w);
493     else
494       y2 = CV_MED_LW(CV_A_MED(pb->y));
495 
496     x = b + a*((BoxReal) y1);
497     for(y = y1; y <= y2; y++) {
498 
499       MARK(w, y, x);
500 
501       x += a;
502     }
503   }
504 }
505 
506 /* NOME: My_Add_Join_Path
507  * DESCRIZIONE: Rasterizza una curva tipo "quarto di cerchio stirato".
508  *  Dati 3 punti a, b, c questo tipo di curva e' un quarto di cerchio
509  *  stirato lungo il parallelogramma che ha per vertici a, b, c (il quarto
510  *  vertice del parallelogramma e' determinato dai 3 precedenti) in modo
511  *  tale che il cerchio parte dal punto a si dirige verso b (senza toccarlo)
512  *  e curva finendo in c.
513  * NOTA: Le coordinate dei punti sono coordinate relative.
514  */
My_Add_Join_Path(BoxGWin * w,BoxPoint * a,BoxPoint * b,BoxPoint * c)515 static void My_Add_Join_Path(BoxGWin *w, BoxPoint *a, BoxPoint *b, BoxPoint *c) {
516   BoxPoint ia, ib, ic;
517   ia.x = CV_XF_A(w, a->x);
518   ia.y = CV_YF_A(w, a->y);
519   ib.x = CV_XF_A(w, b->x);
520   ib.y = CV_YF_A(w, b->y);
521   ic.x = CV_XF_A(w, c->x);
522   ic.y = CV_YF_A(w, c->y);
523 
524   rst__cong(w, & ia, & ib, & ic);
525 }
526 
My_Close_Path(BoxGWin * w)527 static void My_Close_Path(BoxGWin *w) {}
528 
529 /* NOME: rst__cong
530  * DESCRIZIONE: Come My_Add_Join_Path, ma utilizza coordinate assolute.
531  */
rst__cong(BoxGWin * w,BoxPoint * pa,BoxPoint * pb,BoxPoint * pc)532 void rst__cong(BoxGWin *w, BoxPoint *pa, BoxPoint *pb, BoxPoint *pc) {
533   BoxInt iymin, iymax, iy;
534   BoxReal ymin, ymax;
535   BoxReal v01x, v01y, v21x, v21y, v02x, v02y, h;
536 
537   /* Comincio col trovare la massima proiezione della curva
538    * sull'asse y, cioe' l'intervallo di ordinate in cui stanno sicuramente
539    * tutti i punti della curva: basta a tal proposito che l'intervallo
540    * contenga le ordinate dei tre punti p[0], p[1], p[2]
541    */
542   if (pa->y < pb->y) {
543     ymin = pa->y;
544     ymax = pb->y;
545   } else {
546     ymin = pb->y;
547     ymax = pa->y;
548   }
549 
550   if (pc->y > ymax)
551     ymax = pc->y;
552   else if (pc->y < ymin)
553     ymin = pc->y;
554 
555   /* Se l'intervallo sta sopra o sotto lo "schermo" e' come se non ci fosse */
556   if (ymax < GRP_AYMIN(w) || ymin > GRP_AYMAX(w)) return;
557 
558   /* Devo per tagliare l'eventuale parte "invisibile" dell'intervallo */
559   if (ymin < GRP_AYMIN(w)) ymin = GRP_AYMIN(w);
560   if (ymax > GRP_AYMAX(w)) ymax = GRP_AYMAX(w);
561 
562   /* Ora converto le coordinate in coordinate intere */
563   iymin = CV_MED_GT(CV_A_MED(ymin));
564   iymax = CV_MED_LW(CV_A_MED(ymax));
565 
566   /* Calcolo i due vettori che descrivono il parallelogramma
567    * in cui e' inserita la curva, diretti "fuori dall'origine".
568    */
569   v21x = pb->x - pc->x;
570   v21y = pb->y - pc->y;
571 
572   v01x = pb->x - pa->x;
573   v01y = pb->y - pa->y;
574 
575   /* Calcolo il vettore 02 */
576   v02x = pc->x - pa->x;
577   v02y = pc->y - pa->y;
578 
579   /* Controllo che il parallelogramma non sia troppo stretto:
580    * in tal caso lo posso sostituire con una retta.
581    * Devo calcolare l'altezza del triangolo 012 di base 02
582    * e questa altezza deve essere molto minore di 1
583    * (noi consideriamo << 0.05).
584    * NOTA: h e' questa altezza al quadrato per 4.
585    * NOTA: i casi degeneri(punti allineati) rientrano
586    *  nella categoria dell' "h stretto"
587    */
588   h = pow(v21x * v01y - v21y * v01x, 2.0)/(v02x*v02x + v02y*v02y);
589   if (h < 0.01) {
590     rst__line(w, pa, pc);
591     return;
592   }
593 
594   {
595   /* Ora mi calcolo i parametri a, b, c della generica retta orizzontale
596    * che ha equazione ax + by + c = 0 scritta nelle coordinate
597    * normalizzate (x, y). Questo fascio di rette si ottiene al variare del
598    * solo parametro c, mentre a e b rimangono inalterati.
599    * Detto 3 il verice del parallelogramma 0123, (x, y) sono le coordinate
600    * del sistema di riferimento (non ortonormale!!!) con origine in 3
601    * e versori v01 e v21
602    */
603   BoxReal a, b, c, c2, cstep, s1mc2;
604   BoxReal i1x, i1y, i2x, i2y, rx, ry, x1, x2;
605 
606   /* cstep e' il valore da sommare a c per passare alla retta orizzontale
607    * tipo Y = k alla retta orizzontale Y = k+1
608    * (X, Y) sono le usuali coordinate di schermo (mentre (x, y) ...)
609    */
610   cstep = 1.0/sqrt(v01y*v01y + v21y*v21y);
611   a = -v01y * cstep;
612   b = -v21y * cstep;
613 
614   /* Parto dalla retta orizzontale Y = iymin */
615   c = (v21y - pa->y + ((BoxReal) iymin))*cstep;
616 
617   for (iy = iymin; iy <= iymax; iy++) {
618     c2 = c*c;
619 
620     if (c2 <= 1.0) {
621       s1mc2 = sqrt(1.0 - c2);
622       i1x = i2x = -a*c;
623       i1y = i2y = -b*c;
624       rx = -b * s1mc2;
625       ry = a * s1mc2;
626 
627       /* Ecco i 2 punti di intersezione */
628       if ( ((i1x += rx) < 0.0) || ((i1y += ry) < 0.0) ) {
629         i2x -= rx; i2y -= ry;
630         if ( (i2x >= 0.0) && (i2y >= 0.0) ) {
631           x1 = pa->x + v01x*i2x + v21x*(i2y - 1.0);
632           MARK(w, iy, x1);
633         }
634       } else {
635         if ( ((i2x -= rx) < 0.0) || ((i2y -= ry) < 0.0) ) {
636           x1 = pa->x + v01x*i1x + v21x*(i1y - 1.0);
637           MARK(w, iy, x1);
638         } else {
639           x1 = pa->x + v01x*i1x + v21x*(i1y - 1.0);
640           x2 = pa->x + v01x*i2x + v21x*(i2y - 1.0);
641           MARK(w, iy, x1);
642           MARK(w, iy, x2);
643         }
644       }
645     }
646 
647     c += cstep;
648   }
649 
650   }
651 }
652 
653 /* NOME: rst_curve
654  * DESCRIZIONE: Rasterizza una curva tipo "quarto di cerchio stirato".
655  *  La procedura e' molto simile a My_Add_Join_Path ma e' piu' generale.
656  *  Infatti e' possibile specificare un ulteriore parametro cut, che chiamiamo
657  *  parametro di taglio, che consente di "tagliare" la curva.
658  *  Se questo e' 0 la rst_curve e' equivalente a My_Add_Join_Path, mentre
659  *  man mano che esso si avvicina a 1 la curva si smorza sempre di piu'
660  *  fino a trasformarsi nella retta che va dal punto a al c.
661  *  Viceversa per cut negativi la curva aumenta la sua "spigolosita'"
662  *  Fino a trasformarsi(per cut = -1) nella spezzata che congiunge in sequenza
663  *  i punti a, b e c.
664  * NOTA: Le coordinate dei punti sono coordinate relative.
665  */
rst_curve(BoxGWin * w,BoxPoint * a,BoxPoint * b,BoxPoint * c,BoxReal cut)666 void rst_curve(BoxGWin *w, BoxPoint *a, BoxPoint *b, BoxPoint *c, BoxReal cut) {
667   BoxPoint ia, ib, ic;
668   ia.x = CV_XF_A(w, a->x);
669   ia.y = CV_YF_A(w, a->y);
670   ib.x = CV_XF_A(w, b->x);
671   ib.y = CV_YF_A(w, b->y);
672   ic.x = CV_XF_A(w, c->x);
673   ic.y = CV_YF_A(w, c->y);
674 
675   rst__curve(w, & ia, & ib, & ic, cut);
676 }
677 
678 /* NOME: rst__curve
679  * DESCRIZIONE: Come rst_curve ma utilizza coordinate assolute.
680  */
rst__curve(BoxGWin * w,BoxPoint * pa,BoxPoint * pb,BoxPoint * pc,BoxReal c)681 void rst__curve(BoxGWin *w, BoxPoint *pa, BoxPoint *pb, BoxPoint *pc, BoxReal c) {
682   BoxReal v10x, v10y, v12x, v12y;
683   BoxPoint q[5], *pq;
684 
685   if (c < -1.0) c = -1.0;
686   if (c > 1.0) c = 1.0;
687 
688   c = ((sqrt(2.0)-1.5)*c*c + c/2 + 2 - sqrt(2));
689 
690   v10x = pa->x - pb->x;
691   v10y = pa->y - pb->y;
692   v12x = pc->x - pb->x;
693   v12y = pc->y - pb->y;
694 
695   q[0].x = pa->x;
696   q[0].y = pa->y;
697   q[4].x = pc->x;
698   q[4].y = pc->y;
699 
700   q[1].x = pb->x + v10x * c;
701   q[1].y = pb->y + v10y * c;
702   q[3].x = pb->x + v12x * c;
703   q[3].y = pb->y + v12y * c;
704 
705   /* 2 e' il punto medio tra 1 e 3 */
706   q[2].x = (q[1].x + q[3].x)/2.0;
707   q[2].y = (q[1].y + q[3].y)/2.0;
708 
709   pq = q;
710   rst__cong(w, pq, pq+1, pq+2);
711   rst__cong(w, pq+3, pq+4, pq+5);
712 }
713 
714 /* DESCRIZIONE: Rasterizza una curva tipo "cerchio stirato" (cioe'
715  *  ellissi, infatti stirando una circonferenza - anche lungo direzioni
716  *  non ortogonali tra loro - si ottiene sempre un'ellisse!)
717  *  ctr e' il centro dell'ellisse, va e vb i vettori lungo cui la circonferenza
718  *  viene stirata. Per intenderci, l'equazione della curva (espressa in funzione
719  *  dell'angolo theta) sara':
720  *     P(theta) = va * cos(theta) + vb * sin(theta)
721  *  P(theta) e' il punto della curva corrispondente all'angolo theta, il quale
722  *  varia tra 0 e 2 pigreco, va e vb sono i due vettori dati da:
723  *   va = a - ctr  e  vb = b - ctr, dove a e b sono passati come argomenti
724  *  a My_Add_Circle_Path.
725  */
My_Add_Circle_Path(BoxGWin * w,BoxPoint * pctr,BoxPoint * pa,BoxPoint * pb)726 static void My_Add_Circle_Path(BoxGWin *w,
727                                BoxPoint *pctr, BoxPoint *pa, BoxPoint *pb) {
728   BoxPoint ctr, a, b;
729   BoxInt iy, y1, y2;
730   BoxReal xmin, xmax, ymin, ymax;
731   BoxReal C, C2, D, k1, k2, x, dx, y;
732 
733   a.x = CV_XF_A(w, pa->x - pctr->x);
734   a.y = CV_YF_A(w, pa->y - pctr->y);
735   b.x = CV_XF_A(w, pb->x - pctr->x);
736   b.y = CV_YF_A(w, pb->y - pctr->y);
737   ctr.x = CV_XF_A(w, pctr->x);
738   ctr.y = CV_YF_A(w, pctr->y);
739 
740   C2 = a.y * a.y + b.y * b.y;
741   C = sqrt(C2);
742 
743   ymin = ctr.y - C;
744   ymax = ctr.y + C;
745 
746   /* Esco in caso di cerchio non visibile */
747   if (ymax < GRP_AYMIN(w) || ymin > GRP_AYMAX(w)) return;
748 
749   D = sqrt( a.x * a.x + b.x * b.x );
750   xmin = ctr.x - D;
751   xmax = ctr.x + D;
752   if (xmax < GRP_AXMIN(w) || xmin > GRP_AXMAX(w)) return;
753 
754   k1 = ( a.x * a.y + b.x * b.y ) / C2;
755   k2 = ( a.x * b.y - a.y * b.x ) / C2;
756 
757   /* Taglio le parti che escono sopra o sotto lo "schermo" */
758   if (ymin < GRP_AYMIN(w))
759     y1 = GRP_IYMIN(w);
760   else
761     y1 = CV_MED_GT(CV_A_MED(ymin));
762 
763   if (ymax > GRP_AYMAX(w))
764     y2 = GRP_IYMAX(w);
765   else
766     y2 = CV_MED_LW(CV_A_MED(ymax));
767 
768   y = ((BoxReal) y1) - ctr.y;
769   x = ctr.x + y*k1;
770 
771   for(iy = y1; iy <= y2; iy++) {
772 
773     dx = k2 * sqrt(C2 - y*y);
774     MARK(w, iy, x - dx);
775     MARK(w, iy, x + dx);
776 
777     x += k1;
778     ++y;
779   }
780 }
781 
782 /* NOME: rst__poly
783  * DESCRIZIONE: Rasterizza un poligono chiuso di n vertici:
784  * p[0], p[1], ..., p[n-1] (punti espressi in coordinate assolute).
785  */
rst__poly(BoxGWin * w,BoxPoint * p,int n)786 void rst__poly(BoxGWin *w, BoxPoint *p, int n) {
787   int i;
788   BoxPoint q;
789 
790   if (n<2) {
791     WARNINGMSG("rst__poly", "Poligono con meno di 2 vertici");
792     return;
793   }
794 
795   q.x = p->x;
796   q.y = p->y;
797 
798   for(i=1; i<n; i++) {
799     BoxPoint *previous_p = p++;
800     rst__line(w, previous_p, p);
801   }
802 
803   rst__line(w, & q, p);
804 }
805 
806 /* NOME: rst_poly
807  * DESCRIZIONE: Rasterizza un poligono chiuso di n vertici:
808  * p[0], p[1], ..., p[n-1]
809  */
rst_poly(BoxGWin * w,BoxPoint * p,int n)810 void rst_poly(BoxGWin *w, BoxPoint *p, int n) {
811   int i, j = 1;
812   BoxPoint r, q[2];
813 
814   if (n<2) {
815     WARNINGMSG("rst_poly", "Poligono con meno di 2 vertici");
816     return;
817   }
818 
819   r.x = q[0].x = CV_XF_A(w, p->x);
820   r.y = q[0].y = CV_YF_A(w, p->y);
821 
822   for(i=1; i<n; i++) {
823     ++p;
824     q[j].x = CV_XF_A(w, p->x);
825     q[j].y = CV_YF_A(w, p->y);
826     rst__line(w, & q[0], & q[1]);
827     j ^= 1;
828   }
829 
830   rst__line(w, & r, & q[j ^ 1]);
831 }
832 
833 /* DESCRIZIONE: Setta il colore di primo piano.
834  */
My_Fg_Color(BoxGWin * w,Color * c)835 static void My_Fg_Color(BoxGWin *w, Color *c) {
836   color cb;
837   palitem *newcol;
838 
839   grp_color_build(c, & cb);
840   newcol = grp_color_request(w->pal, & cb);
841   if (newcol != NULL)
842     BoxGWin_Set_Color(w, newcol->index);
843 }
844 
845 /* DESCRIZIONE: Setta il colore di sfondo.
846  */
My_Bg_Color(BoxGWin * w,Color * c)847 static void My_Bg_Color(BoxGWin *w, Color *c) {
848   color cb;
849   grp_color_build(c, & cb);
850   (w->bgcol)->c = cb;
851 }
852 
My_Add_Fake_Point(BoxGWin * w,BoxPoint * p)853 static void My_Add_Fake_Point(BoxGWin *w, BoxPoint *p) {}
854