Selecionar linha desenhada por Graphics2D. draw() com o mouse

5 respostas
E

Ola amigos esse é meu primeiro post aqui nesse fórum, eu estou desenvolvendo um pequeno aplicativo de simulação de AF (Autômatos Finitos), para meu curso de Engenha de Computação, como pode ser visto na foto abaixo e testado com o *.jar em anexo.

Estou usando JPanel’s para representar (conter o desenho da circulo) cada estado e por isso tenho como capturar os eventos do mouse associados a eles, o que simplifica bastante as questões de seleção e movimentação! Contudo as linhas que ligam os vários estados em um grafo de autômatos, chamadas de linhas de transições não são representadas por JPanel e sim são desenhas pelo JPanel que contém todos os estados partindo do centro de um estado até o centro de outro estado!
O MEU PROBLEMA É: Como selecionar essas linhas de transições com o mouse uma vez que elas são apenas desenhos gráficos feitos com Graphics2D. draw() ???

// Desenhar linha ou arco
    public void DesenharLinha(Graphics2D g2d) {
    // Se os estados forem diferentes
        if(!transicao.getEstadoAnterior().equals(transicao.getNovoEstado())) {
            // Desenha linha!
            g2d.draw(getLinha());
.
.
.

Eu pensei em capturar o clic do mouse e então verificar qual a linha que passa mais próximo de onde ocorreu o clic (levando em consideração um margem de proximidade máxima é claro!) e então assim eu poderia saber qual a linha foi clicada e então marcar ela para edição… Mas eu gostaria de sugestões e comentário ou que alguém me dia a “Forma Correta” de fazer isso!
Desde já agradeço a ajuda dos amigos!

5 Respostas

E

É isso mesmo cara, você tem que manter um set com suas linhas e em cada uma você usa o método estático disTo() da propria Line2D- é um nome assim, ctrl+espaço resolve-, a que retornar a menor distância é a que você procura, só tem que ter o cuidado de filtrar alguns caso especias, como quando o clique é em cima dos circulos.

O “ruim” desse método é que todo e qualquer clique por mais distante que seja de qualquer linha, vai selecionar pelo uma delas. Outros método de maior precisão é o uso do métodos contains da interface shape, assim toda vez que o mouse clicar bem cima da linha, você usa o ponto como parâmetro dessa função, e se retornar em alguma linha, você seleciona ela. Contudo, a desvantagem é que a precisão exigida vai ser muito grande, chegando muitas vezes a ter que clicar em alguns ponto diferentes da linha pra poder funcionar.

Um meio termo, seria criar um Retangulo que se estendesse sobre e linha, e que tivesse uma largura maior, coisa de 5px. Se o usuário clicasse perto da linha, bastava passar o método contains nos rentagulos e se alguns retornasse true, selecionava a linha que coincidia com ele. Claro que o esforço computacional seria maior, além de ser bem mais complexo criar retangulos que concidissem com as linhas, ainda mais porque o java só produz retangulos na vertical ou horizontal, sendo necesária uma matriz de tranformação pra fazer a rotação no angulo certo.

Então,é melhor usar o primeiro método, que é mais simples e o que tem menos desvantagens. Depois conta pra gente se funcionou.

E

Obrigado: elissonandrade

Foi de grande ajuda suas explicações, esse ainda é um protótipo para o sistema que estou pensando. No momento estou adquirindo o know-how para o desenvolvimento do mesmo de modo que não quero ter mudar completamente implementação bem no meio do projeto pq descobrir que estou “fazendo do jeito errado” então estou procurando me focar em aprender como resolver os maiores problemas que posso encontrar com relação a parte gráfica… como movimentação, seleção, transformações e coisas afins. Novamente grato pela ajuda!

B

Você precisa fazer as coisas do zero ou pode usar uma biblioteca para desenhar sua simulação? Se puder usar uma biblioteca (ou pelo menos quiser estudar como ela funciona - isso acho muito importante, até para aprender “melhores práticas”) pode tentar algo como o JGraph:

http://www.jgraph.com/

T

EngJorgeAugusto
Parece estar bem interessante seu aplicativo, parabéns. Estou passando por problemas "similares" em um aplicativo de grafos. Eis uma solução que estou aplicando (é similar ao explicado pelo elissonandrade):

import javax.swing.*;
import java.util.*;
import java.awt.geom.*;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.BorderLayout;
import java.awt.Color;

public class TesteClick extends JFrame {
	ArrayList<Linha> linhas = new ArrayList<Linha>();
	DrawPanel painel = new DrawPanel();

    public TesteClick() {
    	setLayout(new BorderLayout());
    	setResizable(false);
    	setSize(800,600);
    	setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    	getContentPane().add(painel, BorderLayout.CENTER);

    	linhas.add(new Linha(10, 10, 50, 50));
    	linhas.add(new Linha(60, 50, 300, 90));
    	linhas.add(new Linha(500, 500, 267, 337));
    }

    public static void main(String args[]){
    	new TesteClick().setVisible(true);
    }

	private void selecionarLinha(Point ponto, boolean ctrlDown){
		for (Linha linha : linhas)
			if (linha.contains(ponto))
				linha.selecionar(true);
			else if (!ctrlDown) // se a tecla ctrl não estiver selecionada, desmarca a seleção da linha
				linha.selecionar(false);

		painel.repaint();
	}

    class DrawPanel extends JPanel implements MouseListener{

    	public DrawPanel(){
    		addMouseListener(this);
    	}

    	protected void paintComponent(Graphics g){
			super.paintComponent(g);
	
	    	Graphics2D g2 = (Graphics2D) g.create();

	    	g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

	    	for (Linha linha : linhas)
	    		linha.desenhar(g2);
	    }

	    @Override
		public void mouseClicked(MouseEvent evtmouse) {}
	
		@Override
		public void mouseEntered(MouseEvent evtmouse) {}
	
		@Override
		public void mouseExited(MouseEvent evtmouse) {}
	
		@Override
		public void mousePressed(MouseEvent evtmouse) {
			selecionarLinha(evtmouse.getPoint(), evtmouse.isControlDown());
	    }

	    @Override
		public void mouseReleased(MouseEvent evtmouse) {
		}
    }

    class Linha{
    	private Line2D.Float desenho;
    	private boolean selecionada;

    	public Linha(float x1, float y1, float x2, float y2){
    		desenho = new Line2D.Float(x1, y1, x2, y2);
    		selecionada = false; // inicialmente, não selecionada
    	}

    	public void selecionar(boolean selecionar){
    		selecionada = selecionar;
    	}

    	public void desenhar(Graphics2D g2){
    		if (selecionada)
    			g2.setPaint(Color.BLUE); // cor azul para selecionada
    		else
    			g2.setPaint(Color.BLACK); // cor preta para não selecionada
    			
    		g2.draw(desenho);
    	}

    	public boolean contains(Point ponto){
    		return (desenho.ptSegDist(ponto.x, ponto.y) < 1);
    	}
    }
}

Uma referência importante que acho que vale a pena você considerar é o JFlap ([url]http://www.jflap.org/[/url]), tanto para a parte de desenho (tem uns algoritmos interessantes para as curvas e textos) quanto para a parte de autômatos. Dá para baixar tanto o aplicativo quanto o fonte. Claro, se seu foco não for o desenho em si, mas sim a funcionalidade, vale correr atrás do JGraph, como citado pelo bezier curve, que já tudo isso pronto e muito mais. Mas acho que vale a pena fazer na mão (desde que não comprometa seus prazos), pelo aprendizado de java2d.

Abraço.

E

Muito obrigado: bezier curve
Estou de olho nessa lib agora mesmo… parece algo muito promissor, vou estuda-la e procurar aplica-la ou no mínimo utilizar seus conceitos!

e obrigado também: TerraSkilll o JFLAP é minha referência mais consultada, estou de olho no código dele fonte tbm e sei que conseguirei algumas ótimas ideias lá!!!

Criado 9 de abril de 2012
Ultima resposta 10 de abr. de 2012
Respostas 5
Participantes 4