package tirateima.gui.variaveis; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import javax.swing.JComponent; /** * * Desenha a seta que sai do ponteiro. * * @author Vinícius * */ public class Seta extends JComponent { private static final long serialVersionUID = 101L; String nome;/**< Nome do ponteiro ao qual pertence a seta*/ Point posicaoPartida; /**< Posição de origem da seta*/ Point posicaoApontada;/**< Posição de destino da seta*/ Point posicaoOriginal;/**< Localização da seta no conteiner pai (tudo)*/ protected double proporcao = 1.0; public Seta(){ super(); } public Seta(String nome, Point posicaoApontada) { this.nome = nome; this.posicaoApontada = posicaoApontada; } public void setProporcao(double prop) { if (prop <= 0) throw new AssertionError("Proporção negativa ou nula."); proporcao = prop; this.setSize(getTamanhoParaProporcao()); this.validate(); } /** * Cria um quadrado com os lados tendo o dobro do tamanho máximo da seta. * @return */ private Dimension getTamanhoParaProporcao() { //soma a largura (tamanho no eixo x) mais uma margem para o triângulo da seta Integer largura = (int)((double)Math.abs(posicaoApontada.x - posicaoPartida.x) * proporcao) + (int)(10*proporcao); //soma a altura (tamanho no eixo y) mais uma pargem para o triângulo da seta Integer altura = (int)((double)Math.abs(posicaoApontada.y - posicaoPartida.y) * proporcao) + (int)(10*proporcao); return new Dimension(largura,altura); } /** * Método que desenha a seta. * * Para se entender como ela é desenhada, veja: * * Divida a variável pelo centro em quatro na horizontal e na vertical. * Imaginem-se quatro quadrantes, de modo que o primeiro esteja na parte inferior direita, * o segundo na inferior esquerda, o terceiro, na superior esquerda e o quarto na * superior direita. Para calcular o quadrante em que ela estará, basta olhar para qual é * maior, o x ou o y de origem. Lembre-se que no swing o y aponta para baixo. Fica assim: * * x apontado > x partida e y apontado >= y partida - primeiro quadrante * x apontado <= x partida e y apontado > y partida - segundo quadrante * x apontado < x partida e y apontado <= y partida - terceiro quadrante * x apontado >= x partida e y apontado < y partida - quarto quadrante * * Nos casos especiais (x apontado = x partida ou y apontado = y partida) é necessário * também haver um tratamento especial, por conta da distorção causada no triângulo da * seta. Não só nesses casos, mas também quando está muito próximo desses casos limites * deve-se arredondar para as posições coincidentes com os eixos, para evitar disorções. * * Dessa forma, suas fronteiras (que serão um retângulo) deverá ser posicionado de modo que * duas de suas laterais encostem em nos eixos que cortam a variável no meio. Isso se dá * para que a seta possa ser desenhada. * * Além disso, de acordo com cada quadrante, a posição de origem da seta será diferente, * pois a seta é desenhada relativamente ao conteiner dela, não do conteiner no qual ela * está inserido. Será dessa forma. * * Se estiver no primeiro quadrante, ela terá seu início no canto superior esquerdo. * Se estiver no segundo quadrante, ela terá seu início no canto superior direito. * Se estiver no terceiro quadrante, ela terá seu início no canto inferior direito. * Se estiver no quarto quadrante, ela terá seu início no canto inferior esquerdo. * @param v * @return */ //TODO: tratar os casos de seta coincidindo com os eixos. @Override public void paint(Graphics g){ //prepara o desenho Graphics2D g2d = (Graphics2D) g; //desenha a seta de acordo com o quadrante em que ela se encontra. Point posicaoPartidaRelativa; Point posicaoApontadaRelativa; Integer tamanhoSeta = 0; Double anguloRotacaoSeta; //primeiro quadrante if (posicaoApontada.x > posicaoPartida.x && posicaoApontada.y >= posicaoPartida.y){ //caso geral if(Math.abs(posicaoApontada.y - posicaoPartida.y) > 5){ //calcula a posição de partida da seta, considerando-se a proporção posicaoPartidaRelativa = new Point(0, 0); //calcula a posição apontada pela seta, considerando-se a proporção posicaoApontadaRelativa = new Point((int)((double)(posicaoApontada.x - posicaoPartida.x)*proporcao), (int)((double)(posicaoApontada.y - posicaoPartida.y)*proporcao)); //calcula o tamanho da seta tamanhoSeta = calculaTamanhoSeta(posicaoPartidaRelativa, posicaoApontadaRelativa); //calcula o angulo de rotação da seta anguloRotacaoSeta = calculaAnguloRotacaoSeta( posicaoPartidaRelativa, posicaoApontadaRelativa); //translada a seta dado o quadrante g2d.translate(0,0); //gira a seta no ângulo de rotação adequado g2d.rotate(anguloRotacaoSeta); } //caso especial em que a seta coincide com ou se aproxima do eixo x else{ //calcula a posição de partida da seta, considerando-se a proporção posicaoPartidaRelativa = new Point(0, 0); //calcula a posição apontada pela seta, considerando-se a proporção posicaoApontadaRelativa = new Point((int)((double)(posicaoApontada.x - posicaoPartida.x)*proporcao), (int)((double)(posicaoApontada.y - posicaoPartida.y)*proporcao)); //calcula o tamanho da seta tamanhoSeta = calculaTamanhoSeta(posicaoPartidaRelativa, posicaoApontadaRelativa); //calcula o angulo de rotação da seta anguloRotacaoSeta = calculaAnguloRotacaoSeta( posicaoPartidaRelativa, posicaoApontadaRelativa); //translada a seta dado o quadrante g2d.translate(0,(int)(5*proporcao)); //gira a seta no ângulo de rotação adequado g2d.rotate(0); } } //segundo quadrante else if (posicaoApontada.x <= posicaoPartida.x && posicaoApontada.y > posicaoPartida.y){ //caso geral if(Math.abs(posicaoApontada.x - posicaoPartida.x) > 5){ //parte do canto superior direito posicaoPartidaRelativa = new Point((int)((double)(posicaoPartida.x - posicaoApontada.x)*proporcao), 0); //chega até o canto inferior esquerdo posicaoApontadaRelativa = new Point(0, (int)((double)(posicaoApontada.y - posicaoPartida.y)*proporcao)); //calcula o tamanho da seta tamanhoSeta = calculaTamanhoSeta(posicaoPartidaRelativa, posicaoApontadaRelativa); //calcula o angulo de rotação da seta anguloRotacaoSeta = calculaAnguloRotacaoSeta( posicaoPartidaRelativa, posicaoApontadaRelativa); //coloca a seta na posição correta g2d.translate(posicaoPartidaRelativa.x, 0); g2d.rotate(Math.PI+anguloRotacaoSeta); } //caso especial em que a seta coincide com o eixo y else{ //parte do centro superior posicaoPartidaRelativa = new Point(5, 0); //chega até o centro inferior posicaoApontadaRelativa = new Point(5, (int)((double)(posicaoApontada.y - posicaoPartida.y)*proporcao)); //calcula o tamanho da seta tamanhoSeta = calculaTamanhoSeta(posicaoPartidaRelativa, posicaoApontadaRelativa); //calcula o angulo de rotação da seta anguloRotacaoSeta = Math.PI/2; //coloca a seta na posição correta g2d.translate(posicaoPartidaRelativa.x, 0); g2d.rotate(anguloRotacaoSeta); } } //terceiro quadrante else if (posicaoApontada.x < posicaoPartida.x && posicaoApontada.y <= posicaoPartida.y){ if(Math.abs(posicaoApontada.y - posicaoPartida.y) > 5){ //parte do canto inferior direito posicaoPartidaRelativa = new Point((int)((double)(posicaoPartida.x-posicaoApontada.x)*proporcao), (int)((double)(posicaoPartida.y-posicaoApontada.y)*proporcao)); //chega até a origem posicaoApontadaRelativa = new Point(0, 0); //calcula o tamanho da seta tamanhoSeta = calculaTamanhoSeta(posicaoPartidaRelativa, posicaoApontadaRelativa); //calcula o angulo de rotação da seta anguloRotacaoSeta = calculaAnguloRotacaoSeta( posicaoPartidaRelativa, posicaoApontadaRelativa); //coloca a seta na posição correta g2d.translate(posicaoPartidaRelativa.x, posicaoPartidaRelativa.y); g2d.rotate(Math.PI+anguloRotacaoSeta); } else{ //parte do canto inferior direito posicaoPartidaRelativa = new Point((int)((double)(posicaoPartida.x-posicaoApontada.x)*proporcao), 0); //chega até a origem posicaoApontadaRelativa = new Point(0, 0); //calcula o tamanho da seta tamanhoSeta = calculaTamanhoSeta(posicaoPartidaRelativa, posicaoApontadaRelativa); //calcula o angulo de rotação da seta anguloRotacaoSeta = calculaAnguloRotacaoSeta( posicaoPartidaRelativa, posicaoApontadaRelativa); //coloca a seta na posição correta g2d.translate(posicaoPartidaRelativa.x, (int)(5*proporcao)); g2d.rotate(Math.PI); } } //quarto quadrante else if (posicaoApontada.x >= posicaoPartida.x && posicaoApontada.y < posicaoPartida.y){ //caso geral if(Math.abs(posicaoApontada.x - posicaoPartida.x) > 5){ //parte do canto inferior esquerdo posicaoPartidaRelativa = new Point(0, (int)((double)(posicaoPartida.y-posicaoApontada.y)*proporcao)); //chega até o canto superior direito posicaoApontadaRelativa = new Point((int)((double)(posicaoApontada.x-posicaoPartida.x)*proporcao), 0); //calcula o tamanho da seta tamanhoSeta = calculaTamanhoSeta(posicaoPartidaRelativa, posicaoApontadaRelativa); //calcula o angulo de rotação da seta anguloRotacaoSeta = calculaAnguloRotacaoSeta( posicaoPartidaRelativa, posicaoApontadaRelativa); //coloca a seta na posição correta g2d.translate(0, posicaoPartidaRelativa.y); g2d.rotate(anguloRotacaoSeta); } //caso particular em que a seta coincide com o eixo x else{ //parte do canto inferior esquerdo posicaoPartidaRelativa = new Point(0, (int)((double)(posicaoPartida.y-posicaoApontada.y)*proporcao)); //chega até o canto superior direito posicaoApontadaRelativa = new Point((int)((double)(posicaoApontada.x-posicaoPartida.x)*proporcao), 0); //calcula o tamanho da seta tamanhoSeta = calculaTamanhoSeta(posicaoPartidaRelativa, posicaoApontadaRelativa); //calcula o angulo de rotação da seta anguloRotacaoSeta = calculaAnguloRotacaoSeta( posicaoPartidaRelativa, posicaoApontadaRelativa); //coloca a seta na posição correta g2d.translate(5, posicaoPartidaRelativa.y); g2d.rotate(anguloRotacaoSeta); } } //desenha a linha g2d.drawLine(0, 0, tamanhoSeta, 0); //desenha o triângulo Polygon trianguloSeta = new Polygon(); trianguloSeta.addPoint(tamanhoSeta,0); trianguloSeta.addPoint(tamanhoSeta-(int)(15*proporcao),-(int)(5*proporcao)); trianguloSeta.addPoint(tamanhoSeta-(int)(15*proporcao),(int)(5*proporcao)); g2d.fillPolygon(trianguloSeta); g2d.setColor(Color.BLACK); g2d.dispose(); } /** * Método que recebe uma variável e retorna um ponto a partir do qual ela será desenhada. * Este ponto será sempre o centro da variável e deverá ser depois setado na * posicaoOriginal para que ele funcione corretamente. * De acordo com o quadrante em que a variável se encontra, a posição da origem irá variar. * Veja: * * Divida a variável pelo centro em quatro na horizontal e na vertical. * Imaginem-se quatro quadrantes, de modo que o primeiro esteja na parte inferior direita, * o segundo na inferior esquerda, o terceiro, na superior esquerda e o quarto na * superior direita. Para calcular o quadrante em que ela estará, basta olhar para qual é * maior, o x ou o y de origem. Lembre-se que no swing o y aponta para baixo. Fica assim: * * x apontado > x partida e y apontado >= y partida - primeiro quadrante * x apontado <= x partida e y apontado > y partida - segundo quadrante * x apontado < x partida e y apontado <= y partida - terceiro quadrante * x apontado >= x partida e y apontado < y partida - quarto quadrante * * Nos casos especiais (x apontado = x partida ou y apontado = y partida) é necessário também * haver um tratamento especial, por conta da distorção causada no triângulo da seta. * * Dessa forma, suas fronteiras (que serão um retângulo) deverá ser posicionado de modo que * duas de suas laterais encostem em nos eixos que cortam a variável no meio. Isso se dá * para que a seta possa ser desenhada. * * Além disso, de acordo com cada quadrante, a posição de origem da seta será diferente, * pois a seta é desenhada relativamente ao conteiner dela, não do conteiner no qual ela * está inserido. Será dessa forma. * * Se estiver no primeiro quadrante, ela terá seu início no canto superior esquerdo. * Se estiver no segundo quadrante, ela terá seu início no canto superior direito. * Se estiver no terceiro quadrante, ela terá seu início no canto inferior direito. * Se estiver no quarto quadrante, ela terá seu início no canto inferior esquerdo. * @param Variavel v * @return Point posicaoOriginal */ public Point calculaPosicaoOriginal(Variavel v) { //Pega a largura e altura reais da variavel Integer larguraReal = v.getRealSize().width; Integer alturaReal = v.getRealSize().height; //Inicializa o que for necessario (larguraReal, alturaReal e proporcao) if (larguraReal == 0){ larguraReal = v.dimensao.width; } if (alturaReal == 0){ alturaReal = v.dimensao.height; } if (v.proporcao == 0.0){ v.proporcao = 1.0; } //Calcula o centro da variável Point centroVariavel = new Point(v.posicao.x + larguraReal/2, v.posicao.y + alturaReal/2 + (int)Math.round(15 * v.proporcao)); //Assume de início que a posicao original da seta é o seu centro posicaoPartida = centroVariavel; //Calcula a dimensão da seta Dimension dimensaoSeta = new Dimension(Math.abs((int)((double)(posicaoApontada.x - centroVariavel.x)*proporcao)), Math.abs((int)((double)(posicaoApontada.y - centroVariavel.y)*proporcao))); //Calcula a posicao original de acordo com cada quadrante //Primeiro quadrante. if (posicaoApontada.x > posicaoPartida.x && posicaoApontada.y >= posicaoPartida.y){ //caso geral if(Math.abs(posicaoApontada.y - posicaoPartida.y) > 5){ posicaoOriginal = new Point(centroVariavel.x, centroVariavel.y); } //caso especial em que a seta coincide com o eixo x else{ posicaoOriginal = new Point(centroVariavel.x, centroVariavel.y-(int)(5*proporcao)); } } //Segundo quadrante else if (posicaoApontada.x <= posicaoPartida.x && posicaoApontada.y > posicaoPartida.y){ //caso especial em que a seta coincide com o eixo y if(Math.abs(posicaoApontada.x - posicaoPartida.x) > 5){ posicaoOriginal = new Point(centroVariavel.x - dimensaoSeta.width, centroVariavel.y); } //caso geral else{ posicaoOriginal = new Point(centroVariavel.x - (int)(5*proporcao), centroVariavel.y); } } //Terceiro quadrante else if (posicaoApontada.x < posicaoPartida.x && posicaoApontada.y <= posicaoPartida.y){ //caso geral if(Math.abs(posicaoApontada.y - posicaoPartida.y) > 5){ posicaoOriginal = new Point(centroVariavel.x - dimensaoSeta.width, centroVariavel.y - dimensaoSeta.height); } //caso particular em que a seta coincide com o eixo y else{ posicaoOriginal = new Point(centroVariavel.x - dimensaoSeta.width, centroVariavel.y - (int)(5*proporcao)); } } //Quarto quadrante else if (posicaoApontada.x >= posicaoPartida.x && posicaoApontada.y < posicaoPartida.y){ //caso geral if(Math.abs(posicaoApontada.x - posicaoPartida.x) > 5){ posicaoOriginal = new Point(centroVariavel.x, centroVariavel.y - dimensaoSeta.height); } //caso particular em que a seta coincide com o eixo x else{ posicaoOriginal = new Point(centroVariavel.x-(int)(5*proporcao), centroVariavel.y - dimensaoSeta.height); } } return new Point(posicaoOriginal); } /** * Método que calcula o tamanho da seta. Esse tamanho é dado pela fórmula da distância * entre dois pontos sqrt((x-x0)^2+(y-yo)^2). * * @param posicaoPartidaRelativa * @param posicaoApontadaRelativa * @return Integer tamanho da seta */ private Integer calculaTamanhoSeta(Point posicaoPartidaRelativa, Point posicaoApontadaRelativa) { Double distanciaSeta = Math.sqrt(Math.pow(posicaoApontadaRelativa.x-posicaoPartidaRelativa.x, 2)+ Math.pow(posicaoApontadaRelativa.y-posicaoPartidaRelativa.y,2)); return (int)Math.round(distanciaSeta.doubleValue()); } /** * Calcula o ângulo, em radianos, de rotação da seta, de acordo com a fórmula geométrica * arctg((y-yo)/(x-x0)). * * @param posicaoPartidaRelativa * @param posicaoApontadaRelativa * @return */ private Double calculaAnguloRotacaoSeta(Point posicaoPartidaRelativa, Point posicaoApontadaRelativa) { Double anguloRotacaoSeta; anguloRotacaoSeta = Math.atan( ((double)(posicaoApontadaRelativa.y-posicaoPartidaRelativa.y)/ (double)(posicaoApontadaRelativa.x-posicaoPartidaRelativa.x)) ); return anguloRotacaoSeta; } public Seta criarCopia() { Seta setaCopia = new Seta(this.nome,this.posicaoApontada); return setaCopia; } }