Rotazione Immagini

Ci sto lavorando

 

 

Oggetto: Considerazioni generali

 

 

Contenuti:

1

 

2

 

3  
4  

 

Esempi:

1

 

2  
3  
4  

 

Immaginiamo di voler disegnare un'immagine che rappresenta un pulsante di forma rettangolare dimensioni 110 x 46 pixel:

 

La vogliamo ruotare di 90° così:

 

Diamo per scontato come si carica un'immagine (vedi qui). Riportiamo solo il metodo repaint:

 

24     public void paintComponent(Graphics g) {
25         super.paintComponent(g);
26         Graphics2D g2d =(Graphics2D)g;
27         icon.paintIcon(this, g2d, 0, 0);       
28     }          

 

Se vogliamo farla ruotare dobbiamo modificare il metodo paintComponent:

 

24     public void paintComponent(Graphics g) {
25         super.paintComponent(g);
26         Graphics2D g2d =(Graphics2D)g;
27         g2d.translate(icoH,0);
28         g2d.rotate(Math.toRadians(90));
29         icon.paintIcon(this, g2d, icoPx, icoPy);
30     }  

Questa è la normale disposizione degli assi

Nella fig. 1 abbiamo la posizione di partenza. La x cresce da 0 a destra positiva e la y cresce da 0 in basso positiva.

Dopo aver eseguito il metodo rotate l'immagine e tutto il sistema di assi ruota di 90°. Questo fa si che la nostra immagine esca fuori campo, in effetti non la vedremo più. Per poterla vedere di nuovo abbiamo fatto preventivamente traslare di 40 pixel a destra il sistema ed ecco che l'immagine è di nuovo in campo visibile. Attenzione ora l'origine degli assi è a 40 pixel dal bordo sinistro del componente. E cosa peggiore è che gli assi sono anchessi rovesciati. Per spostarsi a destra bisogna decrementare la y (fare -y) e per andare in basso bisogna aumentare la x. Questo provoca un bel rompicapo.

Una situazione che mi ha creato non pochi problemi è stato quando (nel progetto ImgControl) ho dovuto spostare delle immagini ruotate e contemporaneamente limitare l'area di contenimento delle azioni del mouse. Mi spiego se si ha un oggetto, nel nostro caso un generico Jcomponent, come tale è semsibile alle azioni del mouse, un click, un mousepressed, un mousedragged ecc. Se a questo oggetto ad es. diamo una dimensione di 100x100 pixel e all'interno disegniamo un'immagine do 40x40 pixel, un bel cerchio magari al centro, potremo effettuere con il mouse qualsiasi delle azioni previste su tutta l'area del componente ossia all'interno dei 100x100 pixel della sua area. Eventi che possiamo intercettare con il solito meccanismo (sorgente ,evento ascoltatore) . Ma se volessimo che l'area sensibile si limiti alla sola immagine? All'interno dei 40x40 pixel? Java ci da questa possibilità mediante il metodo contais. Se si chea una forma (shape) ad esempio un quadrato 40x40 con le stesse coordinate dell'immagine e si passa a contais allora il sistema sa che dovrà raccoglier le azioni del mouse solo all'interno di questa area. Adesso solo cliccando sull'immagine potrò avere una risposta al di fuori qualsiasi azione del mous arà ignorata.

 

129     @Override
130     public void paintComponent(Graphics g) {
131         super.paintComponent(g);
132         Graphics2D g2d =(Graphics2D)g;          
137                 shape=new Ellipse2D.Float(icoPt.x ,icoPt.y ,icoW,icoH);
140                 icon.paintIcon(this, g2d, icoPt.x, icoPt.y);
141         }                  
142     }
143 
144     @Override
145     public boolean contains(int x, int y) { 
147         return shape.contains(x ,y);    
148     }  

 

come si può notare sia la shape sia l'immagine devono avere le stesse identiche coordinate. Se dovessimo spostare l'immaggine , volendo conservare la caratteristica, dovremmo spostare nella stessa misura il cerchio. L'area di contenimento dovrà seguire esattamente gli spostamenti dell'immagine.

Se malauguratamente dovessiomo prima ruotare l'immagine sarebbero guai, in quanto l'immagine ruotata risiederebbe in un insieme ribaltato (ruotato ad es. di 90°) mentre l'are di contenimento essendo legata al parente delcomponente continuerà ad avere coordinate normali (non rovesciate). Pensate a cosa bisognerà fare per far coincidere le due posizioni. Dovremmo avere la stessa posizione ma su due insiemi di coordinate diversi, come fare sotto c'è un esempio:

 

129     @Override
130     public void paintComponent(Graphics g) {
131         super.paintComponent(g);
132         Graphics2D g2d =(Graphics2D)g;
133         if(flg==1){
134             if(cur==1){             
135                 g2d.translate(icoH,0);
136                 g2d.rotate(Math.toRadians(90));
137                 shape1=new Ellipse2D.Float(Math.abs(icoPt.y), icoPt.x ,icoH,icoW); 
138             }else shape=new Ellipse2D.Float(icoPt.x ,icoPt.y ,icoW,icoH);
139                 icon.paintIcon(this, g2d, icoPt.x, icoPt.y);                    
140         }
141     }
142 
143     @Override
144     public boolean contains(int x, int y) { 
145         if(cur==1)return shape1.contains(x ,y);
146         else return shape.contains(x ,y);    
147     }

 

Se l'immagine deve essere ruotata (cur==1) si effettua la rotazione e traslazione come descritto sopra, poi si crea una shape con coordinate ripensate per coincidere con quelle dell'immagine che segue. Vediamo il ragionamento facendo riferimento all'esempio della figura sotto:

 

 1) - In shape=new Ellipse2D.Float(cordX ,cordY ,imgW,imgH);

 2) - in icon.paintIcon(this, g2d,cordX ,cordY);

 

Immaginiamo di effettuare uno spostamento di 40 pixel a destra e 80 pixel  in basso (dalla figura A alla B). L'asse di riferimento dopo la rotazione è quello +X in basso e -Y a destra la cui origine (0x,0y) è a 40 pixel dal bordo sinistro del componente.

 

Immagine Shape1
1 2 3 4 5
cordX icoPt.x 80 40 Math.abs(icoPt.y)
cordY icoPt.y -40 80 icoPt.x
imgW icoW 100 40 icoH
imgH icoH 40 100 icoW

 

Nella colonna 1 i parametri da inserire da sinisrtra a destra sono coordinata orizzontale, coordinata verticale, larghezza originale dell'immagine, altezza originale dell'immagine. Nella colonna 2 le variabili in cui dovremmo inserire i valori in clonna 3 per spostare l'immagine (sistema ruotato). In questo modo disegneremo l'immagine nella nuova posizione. Nella colonna 4 è il valore che dovremmo inserire nella linea 1) per disegnare l'ellise di contenimento ma Attenzione! nel sistema Dritto. Nella colonna 5 troviamo come ottenere questi valori da quelli che abbiamo a disposizione nel sistema rovescio. il valore 40 lo possiamo ottenere estraendo il valore assoluto da  icoPt.y che è appunto -40. Il valore 80 lo otteniamo dalla icoPt.x, il 40 lo otteniamo da icoH e 100 da  icoW.

Se l'immagine non fosse ruotata, allora sial'immagine che l'ellisse di contenimento apparterrebbero allo stesso sistema di coordinarte e quindi il tutto è più semplice:

 

shape=new Ellipse2D.Float(icoPt.x ,icoPt.y ,icoW,icoH);

icon.paintIcon(this, g2d, icoPt.x, icoPt.y);

 

Bisogna però far si che il metodo contains esegua shape1 se l'immagine è ruotata e shape se dritta.