package plugins.Curve;



/* import delle librerie di default *******/
import java.awt.*;

import javax.swing.*;

import com.sun.glf.util.CompositionComponent;

import java.awt.image.*;


import main.*;
import internalFrame.*;
import internalFrame.ComposizioneLivelli;

import java.awt.image.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import javax.swing.event.*;
import com.sun.glf.util.*;


/* ********** Ogni plugin per PhotoFilter DEVE estendere la classe astratta PluginsCore ******** */
public class CurvesTool extends PluginTools.PluginsCore
{
	/* la PFImageShell corrente (incapsula l'immagine correntemente visualizzata, 
	   i livelli ad essa associati, il frame dei livelli selezionabili a lato) *****/
	protected PFImageShell currentShell;
	/* *** un array di menu associato ai menu attualmente esistenti in PhotoFilter ****************/
	protected JMenu[] defaultMenus;
	
	/* ******* PhotoFilterFrame ? la classe che contiene l'intera GUI di PhotoFilter *********/
	private static PhotoFilterFrame environment;
	
	//caselle per i tre canali: r, g, b
	JCheckBox chan0, chan1, chan2;
	//campi I/O
	JSpinner Xval, Yval;
	//gestori dei numeri visualizzati dai campi
	SpinnerNumberModel Xmodel, Ymodel;
	//pannello contenente la tavola del grafico e l'immagine
	graph curve;
//	static CurvesTool prog=new CurvesTool();
	JPanel pan;
	
	PFInternalFrame f;	
	
	
	public CurvesTool()
	{
		//istanzio i campi di classe, dai relativi metodi eriditˆ della classe PluginsCore
		currentShell = this.getCurrentImageShell();
		defaultMenus = this.getDefaultMenus();
		environment = PluginTools.PluginsCore.getDefaultEnvironment();
		
		curve=new graph(this);
		pan = new JPanel();
		pan.setLayout(new BorderLayout());
		pan.add(curve, BorderLayout.CENTER);
		chan0=new JCheckBox("R", null, true);
		chan0.setFocusable(false);
		chan0.addItemListener(new ItemListener(){
							public void itemStateChanged(ItemEvent ie0)
							{
								curve.ischan0=!curve.ischan0;
								curve.filter=true;
								curve.repaint();
							}
							});
							
		chan1=new JCheckBox("G", null, true);
		chan1.setFocusable(false);
		chan1.addItemListener(new ItemListener(){
							public void itemStateChanged(ItemEvent ie1)
							{
								curve.ischan1=!curve.ischan1;
								curve.filter=true;
								curve.repaint();
							}
							});
							
		chan2=new JCheckBox("B", null, true);
		chan2.setFocusable(false);
		chan2.addItemListener(new ItemListener(){
							public void itemStateChanged(ItemEvent ie2)
							{
								curve.ischan2=!curve.ischan2;
								curve.filter=true;
								curve.repaint();
							}
							});
		
		//inizialmente i campi mostrano le coordinate del viaggiatore
		Xmodel = new SpinnerNumberModel(new Short((short)(curve.PP.getX()+2)),
		new Short((short)0), new Short((short)255), new Short((short)1));
		//i pixel assumono valori da 0 a 255, una ristretta gamma di numeri compresa in uno short
		Xval=new JSpinner(Xmodel);
		Xval.addChangeListener(new ChangeListener(){
							public void stateChanged(ChangeEvent e)
							{
								if((curve.SP.clicked)&&(Xval.isEnabled()))
								{
									//la coordinata del punto selezionato assume il valore del campo corrispondente
									curve.SP.move(Xmodel.getNumber().doubleValue(), curve.SP.yPos);
									curve.repaint();
								}
							}
							});
		//le coordinate del viaggiatore possono essere cambiate solo con il mouse
		Xval.setEnabled(false);
		JPanel pX = new JPanel();
		pX.add(new JLabel("Input"));
		pX.add(Xval);
		
		Ymodel = new SpinnerNumberModel(new Short((short)(255-curve.PP.getY()-2)), 
		new Short((short)0), new Short((short)255), new Short((short)1));
		Yval=new JSpinner(Ymodel);
		Yval.addChangeListener(new ChangeListener(){
							public void stateChanged(ChangeEvent e)
							{
								if((curve.SP.clicked)&&(Yval.isEnabled()))
								{
									curve.SP.move(curve.SP.xPos, 255-Ymodel.getNumber().doubleValue());
									curve.repaint();
								}
							}
							});
		Yval.setEnabled(false);
		pX.add(new JLabel("Output"));
		pX.add(Yval);
		pan.add(pX, BorderLayout.SOUTH);
		
		JPanel p = new JPanel();
		p.setLayout(new BorderLayout());
		p.add(pX, BorderLayout.NORTH);
		
		JPanel pC = new JPanel();
		pC.add(chan0);pC.add(chan1);pC.add(chan2);
		pan.add(pC, BorderLayout.NORTH);
		
		JPanel pB = new JPanel();
		JButton ok = new JButton("Accetta");
		ok.addActionListener(new ActionListener(){
							public void actionPerformed(ActionEvent e)
							{
								
							}
							});
		JButton annulla = new JButton("Annulla");
		annulla.addActionListener(new ActionListener(){
							public void actionPerformed(ActionEvent e)
							{
								hide(f);
							}
							});
		pB.add(ok);
		pB.add(annulla);
		p.add(pB, BorderLayout.SOUTH);
		pan.add(p, BorderLayout.SOUTH);
		
		f = new PFInternalFrame(currentShell, "Curve", true, true, true, true);
		f.setBounds(200, 200, 800, 450);
		f.getContentPane().add(pan);
		JMenuItem curveItem = new JMenuItem("Curve");
		curveItem.addActionListener(new ActionListener(){
						public void actionPerformed(ActionEvent e)
						{
							curve.Reset();
							curve.setImg(getCurrentImage());
							show(f);
						}
						});
		curveItem.setAccelerator(KeyStroke.getKeyStroke("ctrl M"));
		
		defaultMenus[4].add(curveItem);	
		
	}
		
	}
	
class graph extends JPanel
{
	
	GeneralPath curva;
	//FirstPoint e LastPoint punti alle estremità della curva, SelectedPoint è il punto selezionato
	Vertice FP, LP, SP;
	//gli Arrayist contenenti tutti i punti della curva, Cv serve per ordinare il contenuto di Cp, mentre quest'ultimo viene usato nel metodo paint()
	ArrayList<Vertice> Cv, Cp;
	//hitp è true se il punto è stato cliccato, hith è true se una maniglia è stata selezionata. Servono stabilire se il punto è modificabile
	//filter serve ad evitare che l'immagine venga calcolata quando il viaggiatore cambia coordinate, showPP fa sì che il viaggiatore sia disegnato
	//ischan abilitano il canale corrispondente ad assumere i valori della curva
	boolean hitp=false, hith=false, filter=true, showPP=false, ischan0=true, ischan1=true, ischan2=true;
	//X è la coordinata orizzontale di un generico punto sulla curva, vx è il parametro dell'equazione parametrica della curva
	double vx=0, X;
	//Simg è l'immagine di partenza, img è l'immagine visualizzata
	BufferedImage Simg, img, Sgr, gr;
	//PP è il viaggiatore
	Rectangle2D PP, gradRectangle;
	
	CurvesTool prog;
	
	private final static long serialVersionUID = 42;
	
	public graph(CurvesTool PROG)
	{
		
		//inizializzazioni
		prog=PROG;
		FP = new Vertice(0, 255); 	
		FP.hndlsx.setLocation(FP.xPos-30, FP.yPos+30); 
		FP.hndldx.setLocation(FP.xPos+30, FP.yPos-30);
		
		LP = new Vertice(255, 0);	
		LP.hndlsx.setLocation(LP.xPos-30, LP.yPos+30); 
		LP.hndldx.setLocation(LP.xPos+30, LP.yPos-30);
		
		PP = new Rectangle2D.Double(-2, 253, 4, 4);
		
		SP=LP;			//questa istruzione evita alcune eccezioni
		
		curva = new GeneralPath();
		curva.moveTo((float)FP.xPos, (float)FP.yPos);
		curva.lineTo((float)LP.xPos, (float)LP.yPos);
		
		Cv = new ArrayList<Vertice>();
		Cv.add(FP);
		Cv.add(LP);
		
		Cp = new ArrayList<Vertice>();
		Cp.add(FP);
		Cp.add(LP);
		
		Sgr = Toolbox.loadImage(new File("plugins"+File.separator+"Curve"+File.separator+"gradient.cc"), BufferedImage.TYPE_3BYTE_BGR);
		gr = Toolbox.loadImage(new File("plugins"+File.separator+"Curve"+File.separator+"gradient.cc"), BufferedImage.TYPE_3BYTE_BGR);
		
		gradRectangle = new Rectangle2D.Double(-1, 273, Sgr.getHeight()+1, Sgr.getWidth()+1);
		
		//fine inizializzazioni
		addMouseListener(new MouseAdapter(){
				public void mousePressed(MouseEvent e)
				{
					for(short i=0; i < Cv.size(); i++)
					{
						//se il tasto centrale del mouse viene premuto, viene mostrato o nascosto il viaggiatore
						if(e.getModifiersEx() == MouseEvent.BUTTON2_DOWN_MASK)
						{
							showPP = !showPP;
							repaint();
							//il controllo sugli altri punti è inutile
							break;
						}
						SP = (Vertice)Cv.get(i);
						
						if((SP.setClickedhndl(e.getX()-40, e.getY()-10)))
						{
							hith = true;
							hitp=true;
							//il controllo sugli altri punti è inutile
							break;
						}
						SP.setClicked(e.getX()-40, e.getY()-10); 
							
						if(SP.clicked)
						{
							hitp = true;
							//i campi assumono le coordinate del punto selezionato e diventano editabili
							prog.Xval.setValue(SP.xPos);
							prog.Xval.setEnabled(true);
							prog.Yval.setValue(255-SP.yPos);
							prog.Yval.setEnabled(true);
							
							//se viene premuto il tasto destro il punto viene eliminato
							if((e.getModifiersEx() == MouseEvent.BUTTON3_DOWN_MASK)&&(i>1))
							{
								Cv.remove(i);
								Cp.remove(SP);
								
								prog.Xval.setEnabled(false);
								prog.Xval.setValue((short)PP.getX()+2);
								prog.Yval.setEnabled(false);
								prog.Yval.setValue(255-(short)PP.getY()-2);
								
								filter=true;
								repaint();
							}
							
							//viene fatto un controllo sui rimanenti punti, per evitare che sia selezionato più di un punto
							for(short g=(short)(i+1); g<Cv.size(); g++)
							{
								Vertice R = (Vertice)Cv.get(g);
								R.setClicked(e.getX()-40, e.getY()-10);
							}
							
							break;
						}
						//se non è stato selezionato nessun punto
						else
						{
							if(i==Cv.size()-1)
							{
								prog.Xval.setEnabled(false);
								prog.Xval.setValue((short)PP.getX()+2);
								prog.Yval.setEnabled(false);
								prog.Yval.setValue(255-(short)PP.getY()-2);
								
								Rectangle2D ras=new Rectangle2D.Double(e.getX()-43, e.getY()-13, 6, 6);
								for (short k=0; k < Cp.size()-1; k++)
				           		{
									Vertice p1=((Vertice)Cp.get(k));
									Vertice p2=((Vertice)Cp.get(k+1));
									//crea un punto se il puntatore del mouse è distante circa 3px dalla curva
									if((e.getX()-43<p2.xPos)&&(e.getX()-37>p1.xPos))
									{
										for(byte q=-3; q<4; q++)
										{
											if(ras.contains(e.getX()-40+q, findY(e.getX()-40+q, p1, p2)))
											{
											//	System.out.println(ras.contains(e.getX()-8+q, findY(e.getX()-8+q, p1, p2)));
												Vertice nw = new Vertice(e.getX()-40, e.getY()-10);
			                   					Cv.add(nw);
			                   					//dopo aver creato ed aggiunto all'array di confronto il nuovo punto, si svuota l'altro array e gli aggiunto il punto all'estremità sinistra della curva
												Cp.clear();
			                   					Vertice xk = (Vertice)Cv.get(0);
			                   					Cp.add(xk);
			                   					//aggiunge all'array Cp il punto distante un passo, due passi, tre passi, ecc.ecc. dal punto FP
			                   					for(short b=1; b<Cv.size(); b++)
			                   					{
			                   						for(short j=1; j<Cv.size(); j++)
				                   					{
			    	               						short vfat=1;
			        	           						double dist1=Math.abs(((Vertice)Cv.get(j)).xPos-xk.xPos);
			        	           						
			            	       						for(short u=1; u<Cv.size(); u++)
			                      						{
			                   	   							double dist2=Math.abs(((Vertice)Cv.get(u)).xPos-xk.xPos);
			                   								if(dist1>dist2)
			                   									vfat++;
			                   							}
			                   							
			                   						if(vfat==b)
			                   							Cp.add(Cv.get(j));
			                   						}
			                   					}
											break;
											}
										}
									break;
									}
								}
							}
						}
					}
					
					filter=true;
					repaint();
				}
				public void mouseReleased(MouseEvent e)
				{
					hitp=false;
					hith=false;
				}
				});
			
			addMouseMotionListener(new MouseMotionAdapter(){
				public void mouseDragged(MouseEvent e)
				{
					//il punto cambia coordinate se è stato appena selezionato ed il mouse sta trascinando
					if(hitp)
					{
						//una maniglia si può spostare se è stata selezionata 
						if(hith)
							SP.movehndl(e.getX()-40, e.getY()-10);
						else 
							SP.move(e.getX()-40, e.getY()-10);
							
						//mentre si modifica la forma della curva, il viaggiatore cambia di conseguenza la propria ordinata
						X=(short)PP.getX()+2;
						for (short k=0; k < Cp.size()-1; k++)
                    	{
							Vertice p1=((Vertice)Cp.get(k));
							Vertice p2=((Vertice)Cp.get(k+1));
							//se il viaggiatore si trova tra i punti p1 e p2, cambia ordinata
							if((X<=p2.xPos)&&(X>=p1.xPos))
								PP.setFrame(X-2, Math.round(findY(X, p1, p2))-2, 4, 4);
							
						}
						
						filter=true;
						repaint();
					}
				}
				});
			addMouseWheelListener(new MouseWheelListener(){
   							public void mouseWheelMoved(MouseWheelEvent e)
   							{	
								//procedura per spostare il viaggiatore sulla curva
								X = PP.getX()+2;
								X-=e.getWheelRotation(); 
								for (short k=0; k < Cp.size()-1; k++)
	    	                	{
									Vertice p1=((Vertice)Cp.get(k));
									Vertice p2=((Vertice)Cp.get(k+1));
									
									if((X<=p2.xPos)&&(X>=p1.xPos))
										PP.setFrame(X-2, Math.round(findY(X, p1, p2))-2, 4, 4);
									//se il viaggiatore oltrepassa un'estremità riparte dall'estremo opposto della curva
									else if(X<FP.xPos)	PP.setFrame(LP.xPos-2, LP.yPos-2, 4, 4);
									else if(X>LP.xPos)	PP.setFrame(FP.xPos-2, FP.yPos-2, 4, 4);
								}
								//se non c'è nessun punto selezionato aggiorna i campi
								if(!SP.clicked)
								{ 
									prog.Xval.setValue((short)PP.getX()+2);
									prog.Yval.setValue(255-(short)PP.getY()-2);
								}
								//in questo caso è sprecato calcolare il valore dei pixel dell'immagine perchè la curva non subisce mutazioni
								filter=false;
   								repaint();					
   							}
   							});	
   							
	}
	
	public void setImg(BufferedImage I)
	{
		Simg = I;
		img = null;
	}
	
	public void Reset()
	{
		FP = new Vertice(0, 255); 	
		FP.hndlsx.setLocation(FP.xPos-30, FP.yPos+30); 
		FP.hndldx.setLocation(FP.xPos+30, FP.yPos-30);
		
		LP = new Vertice(255, 0);	
		LP.hndlsx.setLocation(LP.xPos-30, LP.yPos+30); 
		LP.hndldx.setLocation(LP.xPos+30, LP.yPos-30);
		
		PP = new Rectangle2D.Double(-2, 253, 4, 4);
		
		Cv.clear();
		Cv.add(FP);
		Cv.add(LP);
		
		Cp.clear();
		Cp.add(FP);
		Cp.add(LP);
		
		curva.reset();
		curva.moveTo((float)FP.xPos, (float)FP.yPos);
		curva.lineTo((float)LP.xPos, (float)LP.yPos);
	}

	//metodo per trovare le intersezioni tra la curva ed una retta di equazione {x=i}
	public double findY(double i, Vertice p1, Vertice p2)
	{
		//variabile che memorizza l'intersezione
		double ris=0;
		//se l'ascissa corrisponde a quella del punto p2 si prende direttamente l'ordinata del punto come risultato
		if(i==p2.xPos)	ris=p2.yPos;
		else
		{
			double a,b,c,d,q,r,s,t,dum,discrim,u;
			//coefficienti ricavati dalle equazioni cubiche di Bezier
			a=p2.xPos-3*(p2.hndlsx.getX())+3*(p1.hndldx.getX())-p1.xPos;
			b=3*(p2.hndlsx.getX())-6*(p1.hndldx.getX())+3*p1.xPos;
			c=3*(p1.hndldx.getX())-3*p1.xPos;
			d=p1.xPos-i;
			//se l'ascissa corrisponde a quella del punto p1 si prende direttamente l'ordinata del punto come risultato
			if(d==0) 	ris=p1.yPos;
			else
			{
				//se il coefficiente a è nullo il risultato si ricava da un'equazione di secondo grado
				if(a==0)
				{
					//se anche il coefficiente b è nullo il risultato si ricava da un'equazione lineare
					if(b==0)
						vx=-d/c;
					else if(c==0)
					{
						dum=-d/b;	
						if(dum>=0)
							vx=Math.sqrt(dum); 
					}
					else
					{
						c/=2*b;		
						d/=b;
						discrim=-d+c*c;	//==c*c-4*b*d
						
						if (c<0)
						{  
							c = -c;  
							b = c + Math.sqrt(discrim); 
						}
						else
						{  
							b = c + Math.sqrt(discrim);  
							b = -b; 
						}
						//prima soluzione dell'equazione di secondo grado
						vx = d/b;
						if((vx>1)||(vx<0))
							//seconda soluzione dell'equazione di secondo grado
							vx = b;
					}
				}
				else
				{	
					b/=a;c/=a;d/=a; 
					q=(3*c-(b*b))/9;
					r=-(27*d)/54+b*(9*c-2*(b*b))/54;
					discrim=q*q*q+r*r;
										
					if(discrim<0)
					{
						q = -q;
						dum = Math.acos(r/Math.sqrt(q*q*q));
						u = 2.0*Math.sqrt(q);
						//prima soluzione dell'equazione di terzo grado
						vx=-b/3 + u*Math.cos((dum + 4*Math.PI)/3);
						if((vx>1)||(vx<0)) 
							//seconda soluzione dell'equazione di terzo grado
							vx=-b/3 + u*Math.cos((dum + 2*Math.PI)/3);
						if((vx>1)||(vx<0)) 
							//terza soluzione dell'equazione di terzo grado
							vx=-b/3 + u*Math.cos(dum/3);
					}
					if(discrim>0)
					{
						s=r+Math.sqrt(discrim);
						
						if(s<0)
							s=-Math.pow(-s, 1.0/3.0); 
						else
							s=Math.pow(s, 1.0/3.0);
							
						t=r-Math.sqrt(discrim);
						if(t<0) 
							t=-Math.pow(-t, 1.0/3.0); 
						else 
							t=Math.pow(t, 1.0/3.0);
						vx=-b/3+s+t;
											
					}
					if(discrim==0)
					{
						if(r<0)
							u=-Math.pow(-r,(1.0/3.0));
						else
							u=Math.pow(r,(1.0/3.0));
						vx = -(u+b/3);
					}
				}
				//inserisco il parametro vx nell'equazione di Bezier (parametrica di terzo grado) dele ordinate
				ris=Math.pow((1-vx), 3)*p1.yPos+3*vx*Math.pow((1-vx), 2)*
				(p1.hndldx.getY())+3*vx*vx*(1-vx)*(p2.hndlsx.getY())+vx*vx*vx*p2.yPos;
			}
		}
		return ris;
	}
	
	public void paint(Graphics g)
	{
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D)g;
		//traslazione del contesto grafico per non attaccarlo ad un bordo della finestra
		g2.translate(40, 10);
		g2.setPaint(new Color(125, 4, 189));
		//rettangolo di contenimento per i punti della curva
		g2.draw(new Rectangle2D.Double(-3, -3, 261, 261));
		
		//griglia
		g2.setPaint(Color.lightGray);
		for(double k=0;k<256; k+=25.5)	
		{
			g2.draw(new Line2D.Double(0, k, 255, k));
			g2.draw(new Line2D.Double(k, 0, k, 255));
		}
		//aggiornamento dei valori nei pixel dell'immagine
		if(filter)
		{
			//array adatto a modificare immagini a 3 canali 
			byte nc=(byte)Simg.getColorModel().getNumComponents();
			byte[][] lookup = new byte[nc][256];
			//variabile che memorizza l'ordinata della curva
			double f=0;
			//ciclo per ogni pixel dell'immagine
			for(short i=0; i<256; i++) 
			{ 
				//se alcuni pixel non sono compresi tra le estremità, il loro valore sarà comunque quello dell'estremità più vicina
				if(i<FP.xPos)
					f=FP.yPos;
				else if(i>LP.xPos)
					f=LP.yPos;
				else
				{
					for (short k=0; k < Cp.size()-1; k++)
	           		{
						Vertice p1=((Vertice)Cp.get(k));
						Vertice p2=((Vertice)Cp.get(k+1));
						
						if((i<=p2.xPos)&&(i>=p1.xPos))
							f=findY(i, p1, p2);
					}
				}
				//assegna i valori della curva all'array
				if(ischan0)
					lookup[0][i] = (byte)Math.round(255-f);
				else
				//l'immagine non subisce i mutamenti dati dalla curva
					lookup[0][i] = (byte)i;
				if(ischan1)
					lookup[1][i] = (byte)Math.round(255-f);
				else
					lookup[1][i] = (byte)i;
				if(ischan2)
					lookup[2][i] = (byte)Math.round(255-f);
				else
					lookup[2][i] = (byte)i;
				if(Simg.getColorModel().hasAlpha())		lookup[3][i] = (byte)255;
					
			}
			//filtra l'immagine con l'array appena riempito
			img = new LookupOp(new ByteLookupTable(0, lookup), null).filter(Simg, img);
			byte[][] lookupGr = new byte[3][256];
			lookupGr[0]=lookup[0];
			lookupGr[1]=lookup[1];
			lookupGr[2]=lookup[2];
			gr = new LookupOp(new ByteLookupTable(0, lookupGr), null).filter(Sgr, gr);
			
		}
		g2.drawImage(img, null, 320, 0);
		g2.drawImage(gr, null, -29, 0);
		
		g2.setPaint(new GradientPaint((float)gradRectangle.getMinX()+1, (float)gradRectangle.getCenterY(), Color.black,
					(float)gradRectangle.getMaxX()-1,  (float)gradRectangle.getCenterY(), Color.white));
		g2.fill(gradRectangle);
		
		g2.setPaint(new Color(125, 4, 189));
		g2.draw(new Rectangle2D.Double(-30, -1, Sgr.getWidth()+1, Sgr.getHeight()+1));
		g2.draw(gradRectangle);
		
		//disegna la curva ed i suoi punti
		curva.reset();
		curva.moveTo(FP.xPos, FP.yPos);
		for (short k=0; k < Cp.size()-1; k++)
        {
			Point2D p1=((Vertice)Cp.get(k)).hndldx;
			Vertice p2=((Vertice)Cp.get(k+1));
			
			curva.curveTo((float)p1.getX(), (float)p1.getY(),
			(float)(p2.hndlsx).getX(), (float)(p2.hndlsx).getY(),
			(float)p2.xPos, (float)p2.yPos);
			
			p2.paint(g2);
		}
		FP.paint(g2);
		g2.setPaint(Color.black);	
		g2.draw(curva);	
		
		g2.draw(new Line2D.Double(0, FP.yPos, FP.xPos, FP.yPos));
		g2.draw(new Line2D.Double(255, LP.yPos, LP.xPos, LP.yPos));
		//disegna il viaggiatore
		if(showPP)
		{
			g2.setPaint(Color.red);
			g2.draw(PP);
		}
	}
}


class Vertice
	{
		//coordinate del punto e dimensione
		float xPos, yPos;
		byte dim;
		//booleane per sapere se il punto è selezionato e su quale maniglia si è cliccato
		boolean clicked = false, left;
		Color pigment;
		//coordinate delle maniglie
		Point2D hndlsx, hndldx;
		//componenti grafiche
		Line2D sg;
		Ellipse2D hndl;
		Rectangle2D rtg;
		
		public Vertice(double x, double y)
		{
			//inizializzazioni
			xPos = (float)x;
			yPos = (float)y;
			dim = 6;
			//ogni punto creato ha le maniglie orizzontali e a distanza 30 dal punto
			hndlsx=new Point2D.Double(xPos-30, yPos);
			hndldx=new Point2D.Double(xPos+30, yPos);
			
			pigment=Color.black;
			rtg=new Rectangle2D.Double(xPos - dim/2, yPos - dim/2, dim, dim);
			sg=new Line2D.Double(hndlsx.getX()+dim/2, hndlsx.getY(), hndldx.getX()-dim/2, hndldx.getY());
			hndl=new Ellipse2D.Double(hndlsx.getX()-dim/2, hndlsx.getY()-dim/2, dim, dim);
			//fine inizializzazioni
		}
		//controlla se le coordinate passate in input sono contenute nel punto, se il punto è selezionato, diventa blu e mostra le maniglie
		public void setClicked(double x, double y)
		{
			if (rtg.contains(x, y))
			{
				clicked=true;
				pigment=Color.blue;
			}
			else
			{
				clicked=false;
				pigment=Color.black;
			}
		}
		
		public boolean setClickedhndl(double x, double y)
		{
			//muove le maniglie solo se il punto è selezionato
			if(clicked)
			{
				if ((x>=hndlsx.getX()-dim/2) && (x<=hndlsx.getX()+dim/2) && (y>=hndlsx.getY()-dim/2) && (y<=hndlsx.getY()+dim/2))
				{
					left=true;
					clicked=true;
					pigment=Color.blue;
					return true;
				}	
				else if ((x>=hndldx.getX()-dim/2) && (x<=hndldx.getX()+dim/2) && (y>=hndldx.getY()-dim/2) && (y<=hndldx.getY()+dim/2))
				{
					left=false;
					clicked=true;
					pigment=Color.blue;
					return true;
				}
				else return false;
			}
			else return false;
		}
		public void move(double x, double y)
		{
			//muove il punto solo se questo è selezionato
			if(clicked)
			{
				//limiti di movimento per i punti
				if((x>255)||(x<0))
					x=xPos;
				if((y>255)||(y<0))
					y=yPos;
					
				hndlsx.setLocation(hndlsx.getX()+x-xPos, hndlsx.getY()+y-yPos);
				hndldx.setLocation(hndldx.getX()+x-xPos, hndldx.getY()+y-yPos);
				
				xPos += (x-xPos);
				yPos += (y-yPos);
				
				rtg.setFrame(xPos - dim/2, yPos - dim/2, dim, dim);
				
			}
		}
		public void movehndl(double x, double y)
		{
			if(left)
			{
				//limiti di movimento delle maniglie, non possono oltrepassare il punto
				if(x>xPos)
					x=hndlsx.getX();
				
					hndlsx.setLocation(x, y);
					hndldx.setLocation(2*xPos-x, 2*yPos-y);
				
			}
			else 
			{
				if(x<xPos)
					x=hndldx.getX();
					
				hndldx.setLocation(x, y);
				hndlsx.setLocation(2*xPos-x, 2*yPos-y);
			}
		}
		
		public void paint(Graphics2D g2)
		{
			g2.setPaint(pigment);
			g2.draw(rtg);
			
			if(clicked)
			{
				sg.setLine(hndlsx.getX(), hndlsx.getY(), hndldx.getX(), hndldx.getY());
				g2.draw(sg);
				g2.setPaint(new Color(10, 170, 50));
				hndl.setFrame(hndlsx.getX()-dim/2, hndlsx.getY()-dim/2, dim, dim);
				g2.draw(hndl);
				hndl.setFrame(hndldx.getX()-dim/2, hndldx.getY()-dim/2, dim, dim);
				g2.draw(hndl);
			}
		}		
	}

	
	
	/* *****************************************************************************************************
	 * DI SEGUITO UN ELENCO DEI PRINCIPALI METODI EREDITATI DA PluginsCore CON RELATIVA BREVE DESCRIZIONE: *
	 * 																									   *
	 * 																									   *
	 * 																									   *
	 * 																									   *
	 * 																									   *
	 * - setCurrentImage(PFImageShell currImg)														       *
	 * 			imposta come immagine corrente la PFImageShell passatagli come argomento				   *
	 * 																									   *
	 * 																									   *
	 * - addPFInternalFrame(PFInternalFrame pfi)														   *
	 * 			aggunge al environment un nuovo PFInternalFrame, passatogli come argomento				   *
	 * 																									   *
	 * 																									   *
	 * - show(PFInternalFrame pfi)																		   *
	 * 			rende visibile un PFIntenrnalFrame, passatogli come argomento							   *
	 *																									   *
	 *																									   *
	 * - hide(PFInternalFrame pfi)																		   *
	 * 			nasconde un PFInternalFrame, passatogli come argomento									   *
	 * 																									   *
	 *																									   *
	 * 																									   *
	 * 																									   *
	 *******************************************************************************************************/
	

