|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
[JAVA] Java2D/Graphics2D/AffineTransform: mapping coordinate mouse in "user space"
Lo scenario:
Ho un insieme di figure a contorno chiuso che rappresentano ognuna una sagoma reale da tagliare con una macchina CN. Queste figure vengono dispososte su una lastra di materiale di forma rettangolare e dimensione nota. Devo renderizzare queste figure, disposte sul pannello, per visualizzarle all'utente e permettergli di interfacciarsi con il mouse per riposizionarle dove meglio crede nella lastra (quindi trascinamento della sagoma, sua rotazione attorno al proprio centro). L'immagine della lastra e delle sagome che contiene viene renderizzata sull'oggetto Graphics (Graphics2D) di un JPanel contenuto in una JDialog; per una visualizzazione opportuna, il rendering viene scalato e l'immagine della lastra centrata nel JPanel. Il problema: Ad ogni evento MouseMoved si vogliono stampare a video (in una zona apposita del Graphics) le coordinate correnti del puntatore del mouse. Si recuperano le coordinate correnti del mouse all'interno del metodo public void mouseMoved(MouseEvent e) del Listener e tramite i metodi e.getX() ed e.getY(). Le coordinate così ottenute però non sono le coordinate "user space" (infatti il disegno della lastra è stato scalato e traslato) ma le coordinate screen/device space (cioè i valori X e Y correnti del mouse rispetto le origini del sistema cartesiano "nativo" del componente: l'origine è l'angolo in alto a sinistra, l'asse delle X si estende con verso positivo a destra, l'asse delle Y con verso positivo in basso). Si devono quindi mappare le coordinate così ricavate rispetto alle trasformazioni inizialmente applicate al Graphics2D: ho visto in giro che normalmente basta prendere dal Graphics2D la sua trasformazione affine modificata dai vari translate(), rotate() ecc... e utilizzare quella per calcolare l'inversa da applicare alle coordinate del mouse in "device space" per ottenere le corrispettive coordinate in "user space". Ho provato con questo approccio ma ottengo un risultato "strano" che temo dipenda da come utilizzo il Graphics2D e le AffineTransform Ho realizzato un piccolo test per verificare la cosa, e di questo ora parlo: ho un JFrame che contiene 2 JPanel: in quello superiore ci sono delle label, una checkbox e dei textField; quello inferiore a sua volta contiene un altro JPanel, dove avviene il rendering (ho fatto così perchè il JPanel esterno l'ho inserito tramite Matisse e non lo posso estendere, mentre il JPanel interno è una mia classe che estende JPanel). Quello che succede nel test è il semplice disegno degli assi cartesiani centrati sull'origine (X0Y0) del sistema di riferimento del Graphics2D su cui vengono poi disegnati un Rectangle2D e un Arc2D (stile "PIE"). Con i primi due textField in alto a sinistra si possono impostare dei valori in X e Y per eseguire una traslazione dell'origine in quelle coordinate. C'è un MouseListener che on MouseMoved stampa continuamente i valori delle coordinate mappate in "device" e in "user" space. Infine a destra c'è la stampa dei valori attualmente contenuti nella AffineTransform associata al Graphics2D su cui si renderizza. Questi valori vengono aggiornati solo all'evento MouseEntered nel JPanel di rendering. Il problema è che mentre i valori delle coordinate "device space" del mouse combaciano sempre (provare prima e dopo eventuali traslazioni) i relativi valori rimappati in "user space" non sono esatti, perchè sembra (e si evince anche esaminando i valori stampati della AffineTransform) che sia sempre presente una certa quantità di offset sia nelle coordinate X (molto poco) che nelle Y (molto di più). Guardando bene il valore di questo offset, ho quasi l'impressione che rappresenti la distanza (aka traslazione) del punto x0y0 del JPanel su cui disegno rispetto al punto x0y0 del JFrame che lo contiene. Per chiarezza allego codice (due classi) e jar file eseguibile del applicativo di test. Il jar è il file TransformTests.zip, basta rinominarlo cambiandogli l'estensione in .jar Più tardi mi riconnetto per postare delle immagini di esempio che ho catturato, ora devo volare che ho la cresima di un cuginetto ![]() Se qualcuno si è già imbattuto in un problema analogo o ha semplicemente già affrontato in un modo diverso la questione o ancora meglio sa esattamente cosa succede e mi da consigli gliene sarò davvero molto grato. Ciao a tutti ![]() Ultima modifica di banryu79 : 02-05-2008 alle 14:14. |
![]() |
![]() |
![]() |
#2 |
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
l'esempio con le immagini
Ecco il problema:
Avvio l'applicativo/test e posiziono il puntatore del mouse esattamente nel primo pixel in alto a sinistra del JPanel su cui avviene il rendering: in pratica sto puntando alle ccordinate x0y0 del sistema di riferimento in "device space". Nei primi due JTextField sopra a sinistra leggo che non viene applicata nessuna traslazione al sistema di riferimento: sarebbe dunque lecito attendersi che le coordinate in "device space" di valore x0y0 convertite in "user space" valessero sempre x0y0 (in quanto non abbiamo applicato trasformazioni al Graphics2D). [vedi: image01.jpg] ![]() Invece si nota come le coordinate "user space" siano rimappate a x-13y-117 (internamente il remapping viene effettuato prendendo la reference alla AffineTransform del Graphics2D usandola per produrre un'altra AffineTransform equivalente all'inversa di se stessa da usare per trasformare le coordinate del punto del mouse) invece che corrispondere a x0y0, appunto. Come dicevo nel post precedente, è verosimile che quel x-13y-117 applicato come traslazione al punto x0y0 del JPanel me lo porti a combaciare col punto x0y0 del JFrame che lo contiene? A me sembra di sì... Cmq, continuando l'esempio: ora inserisco una traslazione di 100 per le X [vedi: image02.jpg] ![]() e una traslazione di 100 per le Y [vedi: image04.jpg] Esaminando le utlime voci a destra, sotto la stringa "Transform propertyes", vediamo stampati i valori della AffineTransform del Graphics2D aggiornati ai valori attuali e la traslazione è come ci aspettiamo: vale 100 per entrambe le coordinate e quindi in caso di trasformazione inversa dovrebbe andare tutto bene ma... ho inserito un repaint(e quindi un refetch) di quei valori anche quando il mouse scatena l'evento MouseEntered nel JPanel e qui "casca l'asino" [vedi: image05.jpg] ![]() Come si vede ora i valori di traslazione si sono riaggiornati ma invece di presentarci l'atteso x100y100 è compraso un bel x113y217 che pare proprio tenenre conto dell'offset visto prima (x-13y-117) e che si nota come "errore" nella conversione della posizione del mouse: nel JPanel sono sopra l'origine traslata, il che significa che se come "screen/device space" le coordinate del mouse sono x100y100 (corretto perchè abbiamno applicato al Graphics2D una traslazione di x100y100) continuano però a essere rimappate in "user space" come x-13y-117, invece che come mi aspettavo legittimamente, cioè x0y0. Ricordo che metre le coordinate stampate in "screen space" sono ricavate dal MouseEvent.getX()/.getY() le coordinate stampate in "user space" sono quelle del mouse rimappate da una AffineTransform che è l'inversa della AffineTransform del Graphics2D. La mia domanda è: sono un nubbione da chilo e ho commesso qualche fesseria palese nel codice (il link nel post precedente, la classe rilevante è quella custom che estende JPanel per fare il rendering) o c'è sotto dell'altro? Qualcuno ha idea da cosa sia dovuto il problema o in alternativa conosce delle "best practice" in materia che magari vede chiaramente non applicate qui, o ancora ci sono parti di codice nell'uso delle AffineTransform che gli "puzzano"? Vi prego: ho davvero bisogno di capire perchè si comporta così e come rimediare dato che al click del mouse io devo neccessariamente risalire alle coordinate esatte del mio modello dati per selezionare/manovrare correttamente le figure da riposizionare nella lastra: le loro coordinate finali e i relativi percorsi devono combaciare a video rispetto a come verrano poi tagliati nell'effettivo dal CN sulla lastra, non posso permettermi errori di posizionamento. Grazie mille a chiunque ha una anche minima idea della cosa e me la comunica. A proposito: buonanotte ![]() |
![]() |
![]() |
![]() |
#3 |
Senior Member
Iscritto dal: Jan 2002
Città: Napoli
Messaggi: 1727
|
non entro nei dettagli della progrmamazione swing.
in ogni a caso a mio parere stai sbagliando approccio. devi definire un modello dati delle tue forme.. e poi una funziona che disegna su un "foglio bianco" a partire dal modello. ciao
__________________
Se buttassimo in un cestino tutto ciò che in Italia non funziona cosa rimarrebbe? Il cestino. |
![]() |
![]() |
![]() |
#4 | |
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
Quote:
grazie per l'intervento, purtroppo mi è di poco aiuto in quanto un modello dati per le mie "facce" da tagliare già esiste e funziona benissimo; partendo da quello generiamo le Shapes che rappresentano i PathIterator delle "facce" nella lastra corrente [non mi addentro nei dettagli, c'è dietro un algoritmo di nesting bidimensionale che ha già scelto su che lastra e in che posizione mettere ogni faccia passata] e quelli vengono manipolati in memoria fino a che l'utente non salva la situazione che ha definito: solo a quel punto viene applicata la modifica alla faccia originaria (e tra l'altro la faccia originaria non viene mai modificata ma resta com'è, in database viene solo salvato il set di valori di trasformazione da applicarle per ricostruire esattamente il suo percorso spiazzato sulla lastra). A me serve capire solo il fenomeno che ho sottolineato qui: avviene perchè sbaglio io qualcosa di maddornale (probabilmente) o è un'anomalia (meno probabile)? Il codice linkato e il jar sono relativi solamente al test su cui sto studiando questo specifico "problema". Grazie dell'intervento cmq (almeno non mi fa sentire abbandonato ![]() Ciao ![]() P.S.: sono utili oltre che interventi, anche eventuali link a risorse online relative al campo specifico (utilizzo Java2D/Graphics2D/AffineTrasform & mapping mouse "device space" <-> "user space"). Lo chiedo perchè ho trovato tanto materiale al rigurado che sto spulciando con calma, ma magari salta fuori che qualcuno di voi ha un link a qualche risorsa succosa che non conosco. Grazie a tutti ![]() Ultima modifica di banryu79 : 02-05-2008 alle 14:18. |
|
![]() |
![]() |
![]() |
#5 | |
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
come ho risolto...
Ciao,
dato che con ulteriori indagini non sono riuscito a capire perchè la AffineTransform associata al Graphics2D del componente contenuto nel parent di massimo livello (nel mio caso la trasformazione affine del graphics di un JPanel contenuto in un JFrame) non sia corretta nei valori di traslazione di default (dovrebbero essere, o almeno così io mi aspettavo, X0Y0 in coordinate relative) ma sembri mantenere lo stesso valore di offset/posizionamento del componente rispetto al container in cui è inserito ho trovato due modi di aggirare il problema: 1) Posizionare l'oggetto su cui si renderizza in un certo modo Posizionare sempre il componente su cui si disegna con l'angolo in alto a sinistra appoggiato all'angolo in alto a sinistra del container in cui lo si inserisce. Nel caso del test qui sopra, non ho dovuto fare altro che prendere e trascinare (nell'editor visuale di NetBeans, Matisse) il pannello inferiore verso l'alto, in modo da averlo come primo componente, mentre il pannello con gli altri componenti viene posizionato sotto. Il risultato è visibile qui, e nel file del .jar che allego per completezza: in questo modo i valori della matrice di AffineTransform associati al Graphics risultano corretti (attenzione: bisogna anche che il JPanel su cui si disegna non abbia un bordo, dato che altrimenti anche questo concorre a formare una certa quantità di offset, pari allo spessore del bordo). [vedi immagine:] ![]() 2) Usare getLocation() o getBounds() per tenere conto dell'offset del Component Usando getLocation() oppure getBounds() è possibile ottenere, in coordinate relative, la posizione del component: a quel punto basta utilizzare il valore di traslazione in X e Y nei calcoli. Ci si fa dare dal component la sua trasformazione affine: da quella se ne produce una nuova identica computandole però i valori di traslazione X e Y nella quantità specificata dal getLocation() del Component, quindi da questa AffineTransform così composta si ottiene l'inversa e infine la si usa per trasformare le coordinate X e Y del mouse da "screen space" a "user space". Nell'applicazione che sto scrivendo penso userò il primo sistema dato che non ho nessuna controindicazione nel piazzare il pannello con l'interfaccia sotto al pannello dove invece avviene il rendering. Ciao ![]() P.S.: Quote:
(Chiamarlo bug ancora non mi sembra il caso). Però è strano che se chiedo le coordinate del mouse sopra il Graphics i valori prodotti di X e Y e inseriti nel MouseEvent da cui poi li posso leggere, queste siano corrette (tengono conto del posizionamento relativo del componente all'interno del massimo parent) e se invece le rimappo in "user space" queste non lo siano perchè la AffineTransform associata al Graphics del container computa esplicitamente la traslazione dal punto origine del parent contenitore... mha. Ultima modifica di banryu79 : 03-05-2008 alle 13:59. |
|
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 09:44.