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