Polygon und Polyline

erazor2106

Bekanntes Mitglied
Hallo,

undzwar hab ich ein Problem mit Polyline, nämlich dass man damit nicht so viel machen kann wie mit Polygon :D

Ich möchte Mouseclicks des Benutzers abspeichern mit polygon.addPoint(Mouseclick). Das klappt auch ohne Probleme. Nur möchte ich beim späteren Einzeichnen nicht die letzte Verbindungslinie zwischen Anfangs- und Endpunktes haben. Da würde Polyline ins SPiel kommen, doch dies kann ich nicht so als Objekt speichern und behandeln wie Polygon. Zudem steht die Anzahl der Clicks nicht fest, da bieten sich feste Arrays zum Speichern der Koordinaten nicht an. ArrayList wäre da flexibel wird aber irgendwie nicht akzeptiert von Polyline.

Habt ihr da vielleicht eine Idee bzw. ist es möglich auf das erste und letzte Element des Polygons zuzugreifen oder das zeichnen der letzten verbindungslinie zu unterdrücken?

Vielen Dank
 

erazor2106

Bekanntes Mitglied
Danke, da schau ich auch gerade drauf. Komme damit aber noch nicht ganz klar.
Ist das ein Objekt was nur Koordinaten abspeichert?

Es wäre also damit möglich die Mouseclicks als Koordinaten der Reihe nach in dem Pfad abzuspeichern und später diese wieder auslesen zu können?

Könntest du mir da vllt. einen Codeschnippsel dazu schreiben? Also so einen Pfad erzeugen, irgendwelche fiktiven Punkte hinzufügen und später z.b. das 3. Element abzufragen!?
 

Michael...

Top Contributor
Was ist den Polyline für eine Klasse?
Grundsätzlich kann man sich ja Punkte in einer Liste speichern und die Polylinie anhand der Liste selbst zeichnen.
 

Illuvatar

Top Contributor
Das mit dem 3. Element wird wohl etwas schwierig, weil Path2D noch etwas mehr kann...
Erzeugen: new Path2D.Double()
Hinzufügen: Path2D#moveTo
 

erazor2106

Bekanntes Mitglied
java.awt.Graphics

dort gibt es drawPolyline. Man übergibt ein Array mit x-Koordinaten, eins mit y-Koordianten und eine Integer mit der ANzahl der Punkte.
 

Marco13

Top Contributor
Fortsetzung von http://www.java-forum.org/awt-swing-swt/94203-polygon-button-2.html#post663160


Linksklicks punkte setzen
Rechtsklick Polygon fertigmachen
Mittelklick Polygon anklicken
Mac users are screwed.

Weitere Fragen werde ich nur nach erkennbarer Eigeninitiative beantworten
Java:
import java.awt.*;
import javax.swing.*;
import java.awt.geom.*;
import java.util.List;
import java.util.*;
import java.awt.event.*;


class PolylinePanel extends JPanel implements MouseListener
{
    private List<Path2D> paths = new ArrayList<Path2D>();
    private List<Point2D> currentPoints = null;

    public PolylinePanel()
    {
        addMouseListener(this);
    }

    public void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;

        for (Path2D p : paths)
        {
            g.draw(p);
        }

        if (currentPoints != null)
        {
            g.setColor(Color.RED);
            for (int i=0; i<currentPoints.size()-1; i++)
            {
                Point2D p0 = currentPoints.get(i);
                Point2D p1 = currentPoints.get(i+1);
                int x0 = (int)p0.getX();
                int y0 = (int)p0.getY();
                int x1 = (int)p1.getX();
                int y1 = (int)p1.getY();
                g.drawLine(x0,y0,x1,y1);
            }
        }
    }

    public void mouseEntered  (MouseEvent e) {}
    public void mouseExited   (MouseEvent e) {}
    public void mousePressed  (MouseEvent e) {}
    public void mouseReleased (MouseEvent e) {}
    public void mouseClicked  (MouseEvent e)
    {
        if (e.getButton() == MouseEvent.BUTTON2)
        {
            for (Path2D p : paths)
            {
                if (p.contains(e.getX(), e.getY()))
                {
                    System.out.println("Drinne...");
                }
            }
        }
        else if (e.getButton() == MouseEvent.BUTTON3)
        {
            if (currentPoints != null && currentPoints.size() > 2)
            {
                Path2D path = new Path2D.Float();
                Point2D p = currentPoints.get(0);
                path.moveTo(p.getX(), p.getY());
                for (int i=1; i<currentPoints.size(); i++)
                {
                    p = currentPoints.get(i);
                    path.lineTo(p.getX(), p.getY());
                }
                path.closePath();
                paths.add(path);
            }
            currentPoints = null;
        }
        else
        {
            if (currentPoints == null)
            {
                currentPoints = new ArrayList<Point2D>();
            }
            currentPoints.add(new Point2D.Float(e.getX(), e.getY()));
            System.out.println(currentPoints);
        }
        repaint();
    }

}


public class PolylinePainter
{
    public static void main(String args[])
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                JFrame f = new JFrame();
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.getContentPane().add(new PolylinePanel());
                f.setSize(400,400);
                f.setVisible(true);
            }
        });
    }
}
 

erazor2106

Bekanntes Mitglied
Hallo Marco,

vielen Dank. Es ist wirklich beachtlich was du da in der kurzen Zeit programmiert hast.

Das ist wie gesagt schon fast genau das was ich brauche. Nur wenn ich das richtig gesehen habe kann ich mit dem contains befehl nur schauen ob der click innerhalb der grenzen des polygons liegt.

wenn ich die funktion der rechte maustaste weglasse und das polygon offen lasse, also einfach mit dem zuletzt gemacht klick abschließe funktioniert contains nicht mehr, da dies nur für flächen geht oder?

in meinem hauptprogramm habe ich auch einen schieberegler eingebaut womit man während des zeichnens die strichdicke verändern kann. ich nehme an dies würde dann automatisch mit angepasst werden bei den clicks.

den click-effekt kann man doch dann auch simulieren indem ich die strichfarbe ändere solange die maus gedrückt nehme ich mal an

ansonsten ist das schon genau das was ich gesucht habe. und dies scheint wirklich einfacher zu sein als einen Button zu kreieren welcher die Form von Polyline hat
 

Marco13

Top Contributor
Naja. Wenn man eine waagerechte Linie malt und dann irgendwo hin klickt, was soll "contains" dann liefern? "contains" funktioniert erstmal nur richtig für geschlossene Kurven, und selbst da ist es noch manchmal nicht eindeutig. Das mit der Strichtdicke müßtest du ggf. nochmal genauer beschreiben. Man kann beim Zeichnen des Polygons einen passenden "Stroke" setzen, aber wie das beim Klick berücksichtigt werden soll, ist mir nicht ganz klar.
 

erazor2106

Bekanntes Mitglied
Ich werde nachher mal meine Lösung posten. Ich habe mich von deiner Vorgehensweise inspirieren lassen Marco und noch etwas im Internet gefunden was ich dann stark angepasst habe.

Es wird jetzt darauf hinauslaufen dass ich den Polygonzug per Mouseclicks andeute, also die Positionen abspeichere und danach genau die gleiche Linie ein paar Pixel weiter oben noch einmal zeichnen und rechts und links die beiden Linien verbinde. Dadurch erhalte ich eine abgeschlossene polygonförmige Fläche, auf welcher ich dann contains verwenden kann.

Ich hoffe dass das so klappen wird. Die Dicke der Linie lässt sich dann am Ende auch variable gestalten, da ich das horizontale Versetzen der 2. Linie mit der Variable umsetzen kann. Was hälst du (haltet ihr) davon?
 

Marco13

Top Contributor
Willst du eine anklickbare Linie? Dann solltest du lieber alle liniensgemente durchgehen, und schauen, ob der Angklickte Punkt eine Entfernung von z.B. weniger als 5 von der Linie hat. Schau mal die distance-Funktionen in der Klasse Line2D an.
 

erazor2106

Bekanntes Mitglied
Ja genau. Das ist dann wohl oben nicht ganz rübergekommen. Ich erstelle einen ungerichteten Graphen, welcher mit der Mouse gezeichnet werden kann und dessen Knoten und Kanten Buttons bzw. anklickbare Flächen sind.

Hier der Quelltext zum gefundenen Polygonförmigen Button welchen ich entsprechend angepasst habe:

Java:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;

public class PolygonButton extends JButton
 {
   public Polygon polygonzug;   // definieren des Polygons

   public PolygonButton(Polygon uebergebenesPolygon)     // Aufruf der Klasse und übergeben des Polygons
    {
        if(uebergebenesPolygon != null)
         {
             polygonzug = uebergebenesPolygon;    // damit wird das übergebene Polygon absgespeichert
         }
        else
         {
         }
        setContentAreaFilled(false);    // Hintergrund des Buttons wird nicht ausgemalt
    }

 // ----------------------------------------------------------------------------------------------------------------

    protected void paintComponent(Graphics g)         // zeichnen des Polygons
     {
        if (getModel().isArmed())      // wenn der Button angeklickt wird ändert er seine Farbe
         {
             g.setColor(Color.lightGray);    // Farbe wird auf Grau gesetzt
         }
        else
         {
             g.setColor(getBackground());    // Farbe des Buttons entspricht der Hintergrundfarbe
         }
        g.fillPolygon(polygonzug);       // übergeben des Polygons an das Grafikobjekt
        super.paintComponent(g);         // zeichnen des ausgemalten Polygons
    }

 // ----------------------------------------------------------------------------------------------------------------

    protected void paintBorder(Graphics g)         // zeichnen der Kante des Buttons
     {
        g.setColor(getForeground());
        g.drawPolygon(polygonzug);
     }

 // ----------------------------------------------------------------------------------------------------------------

    public boolean contains(int x, int y)     // überprüfen ob der Mouseclick innerhalb der Buttonfläche ist
     {
        return polygonzug.contains(x, y);
     }
}

und hier die zugehörige Verwendung im Hauptprogramm:

Java:
          int Punkteanzahl = KantenArray.size()*2;   // Anzahl der Knotenpunke wird verdoppelt, da der Straßenzug verschoben wieder rückwärts geht
          int[] xWerte = new int[Punkteanzahl];      // Array um die aktuellen x-Werte abzuspeichern der Punkte
          int[] yWerte = new int[Punkteanzahl];      // Array um die aktuellen y-Werte abzuspeichern der Punkte

          for (int i = 0; i < KantenArray.size(); i = i + 1)  // hier werden die Koordinaten aus der flexiblen Arrayliste ausgelesen
           {                                                  // die erste Schleife dient den Originalwerten
             Point t = KantenArray.get(i);              // auslesen des Knotenpunktes an der Stelle i
             xWerte[i] = t.x;
             yWerte[i] = t.y;
           }

          for (int i = 0; i < KantenArray.size(); i = i + 1)  // die zweite Schleife füllt das Array mit dem verschobenen Straßenzug in umgekehrter Reihenfolge auf
           {
             Point t = KantenArray.get(KantenArray.size()-1-i);
             xWerte[KantenArray.size()+i] = t.x+7;
             yWerte[KantenArray.size()+i] = t.y+7;
           }

          Polygon polygonzug = new Polygon(xWerte, yWerte, Punkteanzahl);   // erstellen eines Polygon aus den abgespeicherten x-Werten, y-Werten und der Anzahl der Paare
          polygonKante = new PolygonButton(polygonzug);               // erstellen eines neuen dummyButtons für die Straße am Ende

          polygonKante.addActionListener(new ActionListener()  
           {                                                  
             public void actionPerformed(ActionEvent evt)
              {
                polygonKante_ActionPerformed(evt);
              }
           }
                              );
          ButtonKantenArray.add(polygonKante);

das ist noch nicht ganz ausgereift, aber es funktioniert schon. Die Kante wird gezeichnet und verschoben davon noch einmal um das Polygon zu schließen
 

erazor2106

Bekanntes Mitglied
Noch eine kurze Zwischenfrage. Laufen tut das Ganze ja jetzt bei mir. Allerdings ist mir aufgefallen, dass wenn ich auf eine der Kanten, also der PolygonButtons klicke, geht die CPU-Auslastung auf 100 % hoch ;(

SIeht einer von euch ob das vielleicht am obigen Quelltext lesen könnte, vielleicht an contains?
 

erazor2106

Bekanntes Mitglied
Also ich habe gesehn dass mein Programm eine Endlosschleife erzeugt welche die CPU dann so auslastet.
Das Programm funktioniert so das es an einer Stelle den Button mit dem Polygon erzeugt und in einem Array abspeichert. In einer Methode welche für das Zeichnen zuständig ist, wird dann das Array mit einer For-Schleife durchlaufen, ein dummy wird mit dem aktuellen Button belegt und eingezeichnet.
Das Problem ist nun, das sobald ich mit der Maus auch nur über den Button gehe, ständig diese For-Schleife ausgeführt wird. Wenn ich die Maus weg bewege hört es auf. Sollte ich den Button aber anklicken, habe ich eine Endlosschleife, die ich erst wieder beenden kann wenn ich irgendwo anders hinklicke
 

erazor2106

Bekanntes Mitglied
Ok, hier ein "Minimimalbeispiel" wo alle wichtigen Sachen dabei sind:

Java:
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.net.Socket;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import javax.swing.*;

public class Landkarte extends JFrame
{
    public Image image;                     // in diesem Objekt wird das Hintergrundbild gespeichert
    public ImagePanel kartenPanel;          // hierauf wird dann gezeichnet
    public boolean darfzeichnen = false;    // ist false wenn keine neue Kante gezeichnet werden darf und true wenn eine neue eingezeichnet wird
    public int clickZaehler = 0;            // speichert wie oft der Button Neue Kante erstellen gedrückt wurde um die Beschriftung anzupassen
    public int anfangPolygon = 0;           // Prüfvariable ob ich gerade angefangen habe das Polygon zu zeichnen
    public ArrayList<Point> KnotenArray = new ArrayList<Point>();;   // hier werden die Koordinaten der Knotenpunkte abgespeichert
    public ArrayList<Point> KantenArray = new ArrayList<Point>();;   // hier werden die Koordinaten der Punkte abgespeichert welche zu einer Kante (Polygonzug) gehören
    public ArrayList<JButton> ButtonArray          = new ArrayList<JButton>();     // Array das die Knoten, also die Buttons dort komplett abspeichert
    public ArrayList<JButton> ButtonKantenArray    = new ArrayList<JButton>();     // Array das die Straßen, also die Buttons der Straßen dort komplett abspeichert
    public JButton neueKanteButton;
    public JButton neuerButton;
    public JButton polygonKante;             // hier wird die Straße welche die Form eines Polygonzuges hat in einem Button gespeichert
    public JButton neuZeichnenButton1;       // Zwischenspeicher für die einzuzeichnenden Knotenpunkte (Buttons)
    public JButton neuZeichnenButton2;       // Zwischenspeicher für die einzuzeichnenden Straßenzüge (Buttons)
    public JPanel panelWest;
    
  public Landkarte(String title)
  {
        super(title);
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        this.setBounds(0, 0, 800, 800);
        this.setLocationRelativeTo(null);
        image = null;
        try
          {
             image = new ImageIcon(this.getClass().getResource("bilder/weiss.jpg")).getImage();
          }
        catch (NullPointerException exc)
          {
             exc.printStackTrace();
          }
        
        panelWest = new JPanel();
        panelWest.setLayout(null);
        panelWest.setPreferredSize(new Dimension(200, 900));
        this.getContentPane().add(panelWest, BorderLayout.WEST);
        neueKanteButton = new JButton("Neue Kante erstellen");
        neueKanteButton.setToolTipText("Hiermit wird eine neue Kante erstellt");
        neueKanteButton.setFont(new Font("MS Sans Serif", Font.BOLD, 13));
        neueKanteButton.addActionListener(new ActionListener()
         {
           public void actionPerformed(ActionEvent evt)
            {
              neueKanteButton_ActionPerformed(evt);
            }
         }
                                      );
        neueKanteButton.setBounds(10, 215, 180, 60);  // (x-Wert, y-Wert, Breite, Höhe)
        panelWest.add(neueKanteButton);

        kartenPanel = new ImagePanel();
        kartenPanel.setBackground(Color.WHITE);
        this.getContentPane().add(kartenPanel, BorderLayout.CENTER);
        
        kartenPanel.setPreferredSize(new Dimension(4807,2296));      // hier legen wir die Größe der Mal-Fläche fest, damit wir Scrollen können
        kartenPanel.addMouseListener(new MouseAdapter()      // abfangen und behandeln des Mouseclicks
         {
            @Override
            public void mouseClicked(MouseEvent e)
             {
                Point aktuellerClick = new Point();
                
                if (darfzeichnen == true)     // es wird nur auf die Mouseclicks reagiert wenn auch gezeichnet werden darf
                 {
                     aktuellerClick = e.getPoint();       // der Mouseclick wird abgefangen und in einen Pointer gespeichert welcher den x-Wert und den y-Wert des Mouseclicks erhält
                     ArraysVerwalten(aktuellerClick) ;    // die Position des Clicks wird an die Methode ArraysVerwalten weitergegeben um den Click zu behandeln
                 }
             }
         }
                                     );
        setResizable(true);         // Größe des gesamten Fensters ist veränderbar
        setVisible(true);           // gesamtes Fenster ist sichtbar
  }

// ------------------------------------------------------------------------------------------------------------
 
    public void neueKanteButton_ActionPerformed(ActionEvent evt)
    {
       clickZaehler++;      // zählt wie oft auf den Button gedrückt wurde
       if (anfangPolygon == 1)
        {
           anfangPolygon = 0;   // auf 0 setzen damit ich bei neuer Kante später wieder neue Auswahl treffen kann
           darfzeichnen = false;
           KnotenArray.add(KantenArray.get(KantenArray.size()-1));   // fügt dem Knotenarray den letzten Eintrag der Kante hinzu
           neuerButton = new JButton();                         // neuer Button wird an den anfang der kante gezeichnet
           neuerButton.addActionListener(new ActionListener()    // es wird ein ActionListener für alle Buttons erzeugt, da alle Buttons die Eigenschaften auf der rechten Seite verändern können soll
              {                                                   // zudem soll nicht für jeden einzelnen Button eine extra Methode mit ActionPerformed erzeugt werden
                public void actionPerformed(ActionEvent evt)
                 {
                    neuerButton_ActionPerformed(evt);
                 }
              }
                                           );
           ButtonArray.add(neuerButton);
           kartenPanel.repaint();
           neueKanteButton.setText("Neue Kante erstellen");
           clickZaehler = 0;     // auf 0 setzen damit manuelles drücken auf den neue Kante Button funktioniert
           neueKanteButton.setFont(new Font("MS Sans Serif", Font.BOLD, 13));
           neueKanteButton.setForeground(Color.BLACK);
        }

       if (clickZaehler == 1)    // einmal geklickt --> es dürfen Knoten und Straßen eingezeichnet werden
        {
          darfzeichnen = true;
          neueKanteButton.setText("Kante fertigstellen");
          neueKanteButton.setFont(new Font("Arial", Font.BOLD, 16));
          neueKanteButton.setForeground(Color.RED);
          kartenPanel.repaint();
        }
       else                      // mehr als einmal geklickt --> es dürfen keine Knoten und Straßen mehr eingezeichnet werden
        {
          darfzeichnen = false;
          neueKanteButton.setText("Neue Kante erstellen");
          clickZaehler = 0;
          neueKanteButton.setFont(new Font("MS Sans Serif", Font.BOLD, 13));
          neueKanteButton.setForeground(Color.BLACK);
          kartenPanel.repaint();

          int Punkteanzahl = KantenArray.size()*2;   // Anzahl der Knotenpunke wird verdoppelt, da der Straßenzug verschoben wieder rückwärts geht
          int[] xWerte = new int[Punkteanzahl];      // Array um die aktuellen x-Werte abzuspeichern der Punkte
          int[] yWerte = new int[Punkteanzahl];      // Array um die aktuellen y-Werte abzuspeichern der Punkte

          for (int i = 0; i < KantenArray.size(); i = i + 1)
           {                                                  // die erste Schleife dient den Originalwerten
             Point t = KantenArray.get(i);              // auslesen des Knotenpunktes an der Stelle i
             xWerte[i] = t.x;
             yWerte[i] = t.y;
           }

          for (int i = 0; i < KantenArray.size(); i = i + 1)  // die zweite Schleife füllt das Array mit dem verschobenen Straßenzug in umgekehrter Reihenfolge auf
           {
             Point t = KantenArray.get(KantenArray.size()-1-i);
             xWerte[KantenArray.size()+i] = t.x+7;
             yWerte[KantenArray.size()+i] = t.y+7;
           }

          Polygon polygonzug = new Polygon(xWerte, yWerte, Punkteanzahl);   // erstellen eines Polygon aus den abgespeicherten x-Werten, y-Werten und der Anzahl der Paare
          polygonKante = new PolygonButton(polygonzug);               // erstellen eines neuen dummyButtons für die Straße am Ende
          polygonKante.addActionListener(new ActionListener()  // es wird ein ActionListener für alle Buttons erzeugt, da alle Buttons die Eigenschaften auf der rechten Seite verändern können soll
           {                                                   // zudem soll nicht für jeden einzelnen Button eine extra Methode mit ActionPerformed erzeugt werden
             public void actionPerformed(ActionEvent evt)
              {
                polygonKante_ActionPerformed(evt);
              }
           }
                              );
          ButtonKantenArray.add(polygonKante);      // fügt den Button dem Array hinzu welches die Straßenbuttons verwaltet
          KantenArray.clear();      // alle Koordinaten der zuletzt gezeichneten Straße werden gelöscht um Platz zu machen für die nächste
        }
    }

    public void neuerButton_ActionPerformed(ActionEvent evt)
    {
       int gefunden = 0;
       JButton suchen = (JButton) evt.getSource();
       for (int i = 0; i < ButtonArray.size(); i++ )     // hier wird das Buttonarray auf der suche nach dem gerade gedrückten Button durchsucht
         {
           if ( suchen == ButtonArray.get(i) )
            {
              gefunden = i;
              break;
            }
         }

       if (darfzeichnen == true) // man befindet sich im Modus eine neue Kante einzeichnen zu können
        {
           anfangPolygon++;     // wird um eins erhöht wenn auf den Button geklickt wurde, nun Unterscheidung ob es der Anfangs- oder Endpunkt der Kante wird
           if (anfangPolygon == 1)     // hierbei handelt es sich um den Anfang einer Kante welche mit einem bereits existierenden Button beginnt
           { }
           else if (anfangPolygon == 2)   // hierbei handelt es sich um das Ende einer Kante welche mit einem bereits existierenden Button endet
           {
               neueKanteButton.setText("Neue Kante erstellen");
               clickZaehler = 0;     // auf 0 setzen damit manuelles drücken auf den neue Kante Button funktioniert
               neueKanteButton.setFont(new Font("MS Sans Serif", Font.BOLD, 13));
               neueKanteButton.setForeground(Color.BLACK);
               darfzeichnen = false;
               anfangPolygon = 0;   // auf 0 setzen damit ich bei neuer Kante später wieder neue Auswahl treffen kann
           }
        }
    }
    
    public void polygonKante_ActionPerformed(ActionEvent evt)       // zuständig um die Eigenschaften der Straßen im Array abzuspeichern
    {
       int gefunden = 0;
       JButton suchen = (JButton) evt.getSource();
       for (int i = 0; i < ButtonKantenArray.size(); i++ )         // hier wird das Buttonarray auf der suche nach dem gerade gedrückten Button durchsucht
         {
           if ( suchen == ButtonKantenArray.get(i) )
            {
              gefunden = i;
              break;
            }
         }
    }

    public void ArraysVerwalten(Point Click)
    {
        Point aktuellerClick = new Point();
        aktuellerClick = Click;                // übernehmen des übergebenen Mouseclicks
        
        if (anfangPolygon == 0)    // 0 bedeutet dass eine neue Kante noch keinen Anfang und kein Ende hat
         {
             anfangPolygon = 1;    // nun besitzt die Kante einen Anfang
             KantenArray.add(aktuellerClick);
             KnotenArray.add(aktuellerClick);
             neuerButton = new JButton();                         // neuer Button wird erzeugt
             neuerButton.addActionListener(new ActionListener()   // es wird ein ActionListener für alle Buttons erzeugt, da alle Buttons die Eigenschaften auf der rechten Seite verändern können soll
              {                                                   // zudem soll nicht für jeden einzelnen Button eine extra Methode mit ActionPerformed erzeugt werden
                public void actionPerformed(ActionEvent evt)
                 {
                    neuerButton_ActionPerformed(evt);
                 }
              }
                                           );
             ButtonArray.add(neuerButton);
             validate();
             kartenPanel.repaint();
         }
        else if (anfangPolygon == 1)    // die Kante besitzt bereits einen Anfangsknoten aber noch kein Ende, dieses muss erstellt werden
         {
             KantenArray.add(aktuellerClick);
             kartenPanel.repaint();        // damit aktuelle Kante gleich eingezeichnet wird
         }
     }

    public static void main(String[] args)
    {
        new Landkarte("Route festlegen");
    }

 // ---------------------------------------------------------------------------

   class ImagePanel extends JPanel
    {
        @Override
        public void paintComponent(Graphics g)
        {
            super.paintComponent(g);
            if (image != null)
             {
                g.drawImage(image, 0, 0, null);
                g.setColor(Color.RED);
                
                if (KnotenArray.size()>=1)   // damit die Knoten gezeichnet werden muss erst einmal mindestens einer davon abgspeichert sein
                 {
                    for (int i = 0; i < KnotenArray.size(); i = i + 1)     // einzeichnen der Knotenpunkte als Buttons
                     {
                       Point altPunkt = KnotenArray.get(i);        // hier wird die Position des ursprünglichen Mausklicks eingelesen
                       neuZeichnenButton1 = ButtonArray.get(i);                         // aktueller Button wird aus dem Array eingelesen
                       neuZeichnenButton1.setBounds(altPunkt.x-9,altPunkt.y-9,18,18);   // holt sich hier die Koordinaten des abgespeicherten Mausklicks und legt damit die Position und Größe des Buttons fest
                       neuZeichnenButton1.setBackground(Color.red);                // der Button soll rot sein
                       neuZeichnenButton1.setBorderPainted(false);                 // damit werden die Ränder der Buttons auf der Karte nicht gezeichnet
                       kartenPanel.add(neuZeichnenButton1);                        // fügt den Button optisch auf der Karte hinzu
                     }
                 }
                 
                if (ButtonKantenArray.size()>=0 | darfzeichnen == false)
                 {
                    for (int i = 0; i < ButtonKantenArray.size(); i = i + 1)     // einzeichnen der Straßen als Buttons
                     {
                       System.out.println(i);
                       neuZeichnenButton2 = ButtonKantenArray.get(i);                         // aktueller Button wird aus dem Array eingelesen
                       neuZeichnenButton2.setBounds(10, 10, 1800, 1800);
                       neuZeichnenButton2.setBackground(Color.blue);                // der Button soll rot sein
                       neuZeichnenButton2.setBorderPainted(true);                 // damit werden die Ränder der Buttons auf der Karte nicht gezeichnet
                       kartenPanel.add(neuZeichnenButton2);                        // fügt den Button optisch auf der Karte hinzu
                     }
                 }
                 
                for (int i = 1; i < KantenArray.size(); i = i + 1)        // vorübergehend werden die Kanten sofort als Linien eingezeichnet, damit der Benutzer sehen kann wie die aktuelle Straße bisher aussieht
                 {
                   Graphics2D g2 = (Graphics2D) g;                  // wandeln eindimensionales Objekt in ein 2dimensionales um damit die Strichdicke geändert werden kann
                   g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);    // dadurch werden die Kanten sauber gezeichnet ob Treppen-effekt
                   Point alterPunkt = KantenArray.get(i-1);
                   Point neuerPunkt = KantenArray.get(i);
                   g.setColor(Color.BLUE);                  // die Verbindungslinien sollen blau sein
                   g.drawLine(alterPunkt.x, alterPunkt.y, neuerPunkt.x, neuerPunkt.y);      // hier werden die Verbindungslinien eingezeichnet
                 }
              }
          }
    }
}

und der zugehörige PolygonButton

Java:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;

public class PolygonButton extends JButton
 {
   public Polygon polygonzug;   // definieren des Polygons

   public PolygonButton(Polygon uebergebenesPolygon)     // Aufruf der Klasse und übergeben des Polygons
    {
        if(uebergebenesPolygon != null)
         {
             polygonzug = uebergebenesPolygon;    // damit wird das übergebene Polygon absgespeichert
         }
        else
         {
         }
        setContentAreaFilled(false);    // Hintergrund des Buttons wird nicht ausgemalt
    }

 // ----------------------------------------------------------------------------------------------------------------

    protected void paintComponent(Graphics g)         // zeichnen des Polygons
     {
        if (getModel().isArmed())      // wenn der Button angeklickt wird ändert er seine Farbe
         {
             g.setColor(Color.lightGray);    // Farbe wird auf Grau gesetzt
         }
        else
         {
             g.setColor(getBackground());    // Farbe des Buttons entspricht der Hintergrundfarbe
         }
        g.fillPolygon(polygonzug);       // übergeben des Polygons an das Grafikobjekt
        super.paintComponent(g);         // zeichnen des ausgemalten Polygons
    }

 // ----------------------------------------------------------------------------------------------------------------

    protected void paintBorder(Graphics g)         // zeichnen der Kante des Buttons
     {
        g.setColor(getForeground());
        g.drawPolygon(polygonzug);
     }

 // ----------------------------------------------------------------------------------------------------------------

    public boolean contains(int x, int y)     // überprüfen ob der Mouseclick innerhalb der Buttonfläche ist
     {
        return polygonzug.contains(x, y);
     }
}

Es müsste allerdings ein anderes Bild gewählt werden zum testen, wusste jetzt nicht wie man ein BIld per URL einbindet, da im Programm auf das Bild gezeichnet wird.
Zum Fehler. EInmal auf den Button "Neue Kante erstellen" klicken, dann mit ein paar Clicks eine Kante einzeichnen und wieder links auf den Button klicken um die Kante abzuschließen. Danach das gleiche noch einmal für eine zweite Kante. Nun müsst ihr/du mal auf eine der blauen Kanten klicken und dann im Ausgabefenster auf die Ausgabe meiner Schleifenvariable i achten. Dort ist dann die Endlosschleife (Zeile 272)

Ich hoffe ihr könnt mir da helfen :-(
 

Michael...

Top Contributor
Weiss zwar nicht, was das werden soll und einfach zu lesen ist der Code auch nicht unbedingt.

Das Verändern von Objekten und hinzufügen zum ImagePanel in dessen paintComponent ist ganz schön suspekt!! Muss das sein? Sowas hat darin eigentlich gar nichts verloren. In der paintComponent wird nur gezeichnet - und dass so schnell wie möglich.

Warum werden die Buttons aus der Liste überhaupt bei jeden Zeichnen neu hinzugefügt, die werden doch schon beim ersten mal hinzugefügt. Ich kann nur erkennen, dass neue Buttons hinzugefügt werden können aber nie werden welche rausgenommen oder die Liste geleert.

Also warum jedesmal den gesamten Inhalt der Liste zusätzlich hinzufügen und dann auch noch in der paintComponent?

Werde mal die alten Posts lesen, vielleicht wird mir dann klar was Du vorhast.
 

erazor2106

Bekanntes Mitglied
Ja ich weiß, die Umsetzung ist noch nicht ausgereift. Ist auch das erste mal das ich an einem Programm mit Zeichenfläche arbeite. Dies ist nur eine abgspeckte Variante meines Programmes. Das hat so um die 1300 Zeilen, so dass vielleicht nicht immer klar ist warum hier dieses oder jenes da ist.

Du hast sicher recht das es da einiges an Verbesserungspotential gibt, aber wenn ich wüsste wo und wie hätte ich das schon gemacht :D

Das mit dem ImagePanel war mir auch von Anfang an nicht so super, aber ich war froh das ich es zum Laufen gebracht habe. Was genau findest du suspekt?
ICh find es auch nicht so gut das jedesmal alle Objekte neugezeichnet werden wenn etwas dazu kommt, aber sonst wurden die bisherigen Objekte immer gelöscht wenn ich etwas neues zeichne oder waren halt nicht sofort sichtbar wenn der Benutzer einen Button oder eine Kante neueingezeichnet hat.
Das entfernen von Buttons aus der Zeichenfläche soll das später umgesetzt werden, dazu war ich noch nicht gekommen
 

Michael...

Top Contributor
Wie gesagt, das mit add... innerhalb der paintComponent() ist nicht ideal, man kann die Buttons auch zeichnen ohne Sie per add hinzufügen. Wenn Sie das Graphics Objekt des Panels bekommen, können Sie sich auch selbst zeichnen.
 

erazor2106

Bekanntes Mitglied
Sich auch selbst zeichnen? wie meinst du das? kannst du da mal ein Beispiel geben?

ach und ich glaube, um auf das eigentlich Problem zurückzukommen, die Endlosschleife wird durch contains in der Klasse PolygonButton erzeugt. aber wie könnte man das richtig gestalten?
 

Michael...

Top Contributor
Wenn ich Deine Absichten richtig verstehe willst Du einfach nur eine Polyline zeichnen, die man clicken kann?

Hier mal ein recht simples Bsp. Es besteht aus der Klasse Polyline, die eine Polylinie zeichnen kann und überprüfen kann, ob sich ein Punkt auf der Linie befindet (inkl. Toleranz)
Einem PolylinePanel welches die Linien verwaltet und darstellt. Es hat noch einen MouseMotionAdapter um Linien beim Überfahren zu highlighten.
Einem Frame PolylineDemo welcher das Panel und eine ToggleButton verwendet. Er weisst dem Panel zusätzlich noch einen MouseListener zu, um zum einen neue Linien zu zeichnen und zum anderen um zu erkennen, ob auf eine Linie geklickt wurde.
Um in den Zeichnungsmodus zu wechseln einfach den ToggleButton aktivieren.
Java:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToggleButton;

public class PolylineDemo extends JFrame {
	
	public static void main(String[] args) {
		JFrame frame = new PolylineDemo();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setBounds(0, 0, 400, 400);
		frame.setVisible(true);
	}
	
	private JToggleButton button;
	private PolylinePanel linePanel;
	private JLabel statusLabel;
	private Polyline currentLine;
	
	public PolylineDemo() {
		linePanel = new PolylinePanel();
		linePanel.addMouseListener(new MouseAdapter() {

			public void mouseClicked(MouseEvent e) {
				if(button.isSelected()) {
					if (currentLine==null) {
						currentLine = new Polyline(null, 10);
						linePanel.addLine(currentLine);
					}
					currentLine.add(e.getPoint());
					linePanel.repaint();
				}
				else {
					int index = linePanel.getLineIndexFor(e.getPoint());
					if (index >=0)
						statusLabel.setText("You clicked on line: " + index);
					else
						statusLabel.setText("To draw a line press the button and click into the panel");
				}
			}
		});
		button = new JToggleButton("Activate Drawing");
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				currentLine = null;
				if (button.isSelected()) {
					button.setText("Drawing...");
					statusLabel.setText("Press the button when you want to finish drawing");
				}
				else {
					button.setText("Activate Drawing");
					statusLabel.setText("To draw a line press the button and click into the panel");
				}
			}
		});
		statusLabel = new JLabel();
		statusLabel.setBackground(Color.WHITE);
		statusLabel.setOpaque(true);
		statusLabel.setBorder(BorderFactory.createEtchedBorder());
		statusLabel.setText("To draw a line press the button and click into the panel");
		this.getContentPane().add(linePanel, BorderLayout.CENTER);
		this.getContentPane().add(button, BorderLayout.NORTH);
		this.getContentPane().add(statusLabel, BorderLayout.SOUTH);
	}
	
	class PolylinePanel extends JPanel {
		private ArrayList<LineContainer> lines;
		private Color[] color;
		
		public PolylinePanel() {
			lines = new ArrayList<LineContainer>();
			color = new Color[] {Color.BLACK, Color.ORANGE, Color.BLUE};
			this.addMouseMotionListener(new MouseMotionAdapter() {
				public void mouseMoved(MouseEvent evt) {
					for (LineContainer line : lines) {
						if (line.getStatus()!=2);
							line.setStatus(0);
					}
					
					int index = getLineIndexFor(evt.getPoint());
					if (index >= 0)
						lines.get(index).setStatus(1);
					repaint();
				}
			});
		}
		
		public void addLine(Polyline line) {
			this.lines.add(new LineContainer(line, 0));
		}
		
		public int getLineIndexFor(Point p) {
			for (int i=lines.size()-1 ; i>=0; i--) {
				LineContainer lineContainer = lines.get(i);
				if (lineContainer.getLine().contains(p)) {
					return i;
				}
			}
			return -1;
		}
		
		public void paintComponent(Graphics g) {
			super.paintComponent(g);
			Graphics2D g2 = (Graphics2D)g.create();
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			for(LineContainer lineContainer : lines) {
				g2.setColor(color[lineContainer.getStatus()]);
				g2.setStroke(new BasicStroke(lineContainer.getStrokeWidth(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER));
				lineContainer.getLine().paint(g2);
			}
			g2.dispose();
		}
		
		class LineContainer {
			private Polyline line;
			private int status;
			
			public LineContainer(Polyline line, int status) {
				this.line = line;
				this.status = status;
			}
			
			public Polyline getLine() {
				return line;
			}
			
			public int getStrokeWidth() {
				return line.getStrokeWidth();
			}
			
			public void setStatus(int status) {
				this.status = status;
			}
			
			public int getStatus() {
				return status;
			}
		}
	}
	
	class Polyline  {
		private ArrayList<Point> points;
		private Rectangle boundingRect;
		private int strokeWidth;
		private float tolerance;
		
		public Polyline(ArrayList<Point> points) {
			this(points, 0);
		}
		
		public Polyline(ArrayList<Point> points, int width) {
			if (points==null)
				this.points = new ArrayList<Point>();
			else
				this.points = points;
			this.setStrokeWidth(width);
		}
		
		public void add(Point p) {
			this.points.add(p);
			this.calculateBounds();
		}
		
		public void setStrokeWidth(int width) {
			this.strokeWidth = width;
			this.tolerance = width/2f;
			if(tolerance < .5f)
				tolerance = .5f;
			this.calculateBounds();
		}
		
		public int getStrokeWidth() {
			return this.strokeWidth;
		}
		
		private void calculateBounds() {
			if (points.size()<=0) {
				boundingRect = new Rectangle(0, 0, 0, 0);
				return;
			}
			Point p = points.get(0);
			int xMin, xMax, yMin, yMax;
			xMin = xMax = p.x;
			yMin = yMax = p.y;
			for(int i=1; i<points.size(); i++) {
				p = points.get(i);
				xMin = (p.x<xMin)?p.x:xMin;
				yMin = (p.y<yMin)?p.y:yMin;
				xMax = (p.x>xMax)?p.x:xMax;
				yMax = (p.y>yMax)?p.y:yMax;
			}
			int intTol = (int)tolerance;
			boundingRect = new Rectangle(xMin-intTol, yMin-intTol,
					(xMax-xMin)+ 2*intTol +1, (yMax-yMin) + 2*intTol +1);
		}
		
		public Rectangle getBounds() {
			return this.boundingRect;
		}

		public boolean contains(Point p) {
			return this.contains(p.x, p.y);
		}
		
		public boolean contains(double x, double y) {
			if (this.boundingRect.contains(x, y)) {
				if (points.size()<2)
					return true;
				Point p1 = points.get(0);
				for(int i=1; i<points.size(); i++) {
					Point p2 = points.get(i);
					if (isBetween(p1, p2, x, y))
						return true;
					p1 = p2;
				}
			}
			return false;
		}
		
		private boolean isBetween(Point p1, Point p2, double x, double y) {
			double p1X = p1.x, p1Y = p1.y;
			double p2X = p2.x, p2Y = p2.y;
			double t = tolerance;
			//Checking if values are the coordinates of one of the points
			if ((p1X-t <= x && p1X+t >= x && p1Y-t <= y && p1Y+t >= y) ||
					(p2X-t <= x && p2X+t >= x && p2Y-t <= y && p2Y+t >= y))
				return true;
			
			//Checking if values are between both points by calculating the distances
			double distance = Math.sqrt(Math.pow(p1X - p2X, 2) + Math.pow(p1Y - p2Y, 2));
			double distanceValue = Math.sqrt(Math.pow(x - p1X, 2) + Math.pow(y - p1Y, 2))
				+ Math.sqrt(Math.pow(x - p2X, 2) + Math.pow(y - p2Y, 2));
			
			int minDist = (int)((distance-t)*100);
			int maxDist = (int)((distance+t)*100);
			int calcDist = (int)((distanceValue)*100);
			
			return (minDist<=calcDist && calcDist<=maxDist);
		}
		
		public void paint(Graphics2D g) {
			if (points.size()>1) {
				Point p1 = points.get(0);
				for(int i=1; i<points.size(); i++) {
					Point p2 = points.get(i);
					g.drawLine(p1.x, p1.y, p2.x, p2.y);
					p1 = p2;
				}
			}
		}
	}
}
 

erazor2106

Bekanntes Mitglied
wow, das sieht super aus. Vielen Dank. :applaus:
Ich hatte es auch anfangs mit Polyline versucht, aber das ließ sich irgendwie nicht als Objekt im Array speichern wie das Polygon. Deswegen hatte ich es dann sein lassen.

Aber dein Programm leistet jetzt genau das was ich noch einbinden wollte. Hast du das jetzt selbst geschrieben?

Muss jetzt irgendwie versuchen das einzubinden. Aber nochmals vielen Dank. Muss mich da jetzt erstm mal einarbeiten um das in meinem Minimalbeispiel zum laufen zu kriegen
 

Michael...

Top Contributor
Ich hatte es auch anfangs mit Polyline versucht, aber das ließ sich irgendwie nicht als Objekt im Array speichern wie das Polygon. Deswegen hatte ich es dann sein lassen.
Machbar ist das schon. Nur hast Du dann immer noch das Problem zu erkennen ob ein Mausklick die Linie getroffen hat. Deswegen habe ich mir eine eigene Klass Polyline geschrieben.
Aber dein Programm leistet jetzt genau das was ich noch einbinden wollte. Hast du das jetzt selbst geschrieben?
Klar ist das selbst geschrieben. Nachdem ich mir gestern die contains() von Polygon angeschaut habe und mir gedacht hab für eine Polyline muss das doch einfacher gehen, da mich ja nur Klicks auf der Linie selbst interessieren, hatte ich heute morgen in der U-Bahn eine kurzes Hoch, das ich dann halt auch per Code bestätigt haben wollte. ;-)

Aber wenn Du Dich mit dem Thema Zeichnen intensiver beschäftigen willst, würde ich Dir empfehlen sich ein bisschen ins Thema einzulesen, um ein Grundverständnis aufzubauen: z.B. JavaInsel#Grafikprogrammierung
Man muss ja nicht gleich alles auf einmal lesen ;-)
 

erazor2106

Bekanntes Mitglied
Also durchgestiegen wie dein Programm funktioniert bin ich leider noch nicht :(
Dazu müsste ich glaub ich eine ganze Weile U-Bahn fahren ;-)

Mir geht es in erster Linie jetzt darum, meine alte Zeichenfläche die in dem "Minimalbeispiel" von mir ist, durch dein Panel zu ersetzen. D.h. dass ich in dem Programm dann zeichnen kann, auf dem untergelegten Hintergrundbild.
DIe Verwaltung der Linien, speichern, ... krieg ich dann sicher schon hin, nur das einbinden des Panels in mein Programm klappt nicht so wirklich
 

Michael...

Top Contributor
Kannst ja statt Deinem ImagePanel mein PolylinePanel nehmen, dem kannst Du im Konstruktor eine Referenz deines Bildes übergeben und in einer Instanzvariablen (z.B. namens "image") speichern. Dann noch in der paintComponent(...) folgende zwei Zeilen am besten hinter dem super.paint... einfügen:
Java:
if (image != null)
    g.drawImage(image, 0, 0, null);
 

erazor2106

Bekanntes Mitglied
Ok, ich hab jetzt mal schnell versucht das in das Beispiel einzubinden, aber das zeichnen geht noch nicht:

Java:
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.net.Socket;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import javax.swing.*;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;


public class Landkarte extends JFrame
{
    public Image image;                     // in diesem Objekt wird das Hintergrundbild gespeichert
    public PolylinePanel kartenPanel;          // hierauf wird dann gezeichnet
    public boolean darfzeichnen = false;    // ist false wenn keine neue Kante gezeichnet werden darf und true wenn eine neue eingezeichnet wird
    public int clickZaehler = 0;            // speichert wie oft der Button Neue Kante erstellen gedrückt wurde um die Beschriftung anzupassen
    public int anfangPolygon = 0;           // Prüfvariable ob ich gerade angefangen habe das Polygon zu zeichnen
    public ArrayList<Point> KnotenArray = new ArrayList<Point>();;   // hier werden die Koordinaten der Knotenpunkte abgespeichert
    public ArrayList<Point> KantenArray = new ArrayList<Point>();;   // hier werden die Koordinaten der Punkte abgespeichert welche zu einer Kante (Polygonzug) gehören
    public ArrayList<JButton> ButtonArray          = new ArrayList<JButton>();     // Array das die Knoten, also die Buttons dort komplett abspeichert
    public ArrayList<JButton> ButtonKantenArray    = new ArrayList<JButton>();     // Array das die Straßen, also die Buttons der Straßen dort komplett abspeichert
    public JButton neueKanteButton;
    public JButton neuerButton;
    public JButton polygonKante;             // hier wird die Straße welche die Form eines Polygonzuges hat in einem Button gespeichert
    public JButton neuZeichnenButton1;       // Zwischenspeicher für die einzuzeichnenden Knotenpunkte (Buttons)
    public JButton neuZeichnenButton2;       // Zwischenspeicher für die einzuzeichnenden Straßenzüge (Buttons)
    public JPanel panelWest;

    public JToggleButton button;
    public PolylinePanel linePanel;
    public JLabel statusLabel;
    public Polyline currentLine;

    
  public Landkarte(String title)
  {
        super(title);
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        this.setBounds(0, 0, 800, 800);
        this.setLocationRelativeTo(null);
        image = null;
        try
          {
             image = new ImageIcon(this.getClass().getResource("bilder/weiss.jpg")).getImage();
          }
        catch (NullPointerException exc)
          {
             exc.printStackTrace();
          }
        
        panelWest = new JPanel();
        panelWest.setLayout(null);
        panelWest.setPreferredSize(new Dimension(200, 900));
        this.getContentPane().add(panelWest, BorderLayout.WEST);
        neueKanteButton = new JButton("Neue Kante erstellen");
        neueKanteButton.setToolTipText("Hiermit wird eine neue Kante erstellt");
        neueKanteButton.setFont(new Font("MS Sans Serif", Font.BOLD, 13));
        neueKanteButton.addActionListener(new ActionListener()
         {
           public void actionPerformed(ActionEvent evt)
            {
              neueKanteButton_ActionPerformed(evt);
            }
         }
                                      );
        neueKanteButton.setBounds(10, 215, 180, 60);  // (x-Wert, y-Wert, Breite, Höhe)
        panelWest.add(neueKanteButton);

        kartenPanel = new PolylinePanel();
        kartenPanel.setBackground(Color.WHITE);
        this.getContentPane().add(kartenPanel, BorderLayout.CENTER);
        
        kartenPanel.setPreferredSize(new Dimension(4807,2296));      // hier legen wir die Größe der Mal-Fläche fest, damit wir Scrollen können
        kartenPanel.addMouseListener(new MouseAdapter()      // abfangen und behandeln des Mouseclicks
         {
            @Override
            public void mouseClicked(MouseEvent e)
             {
                Point aktuellerClick = new Point();
                
                if (darfzeichnen == true)     // es wird nur auf die Mouseclicks reagiert wenn auch gezeichnet werden darf
                 {
                     aktuellerClick = e.getPoint();       // der Mouseclick wird abgefangen und in einen Pointer gespeichert welcher den x-Wert und den y-Wert des Mouseclicks erhält
                     ArraysVerwalten(aktuellerClick) ;    // die Position des Clicks wird an die Methode ArraysVerwalten weitergegeben um den Click zu behandeln
                 }
             }
         }
                                     );
        setResizable(true);         // Größe des gesamten Fensters ist veränderbar
        setVisible(true);           // gesamtes Fenster ist sichtbar
        
  // neuer Quelltext
        linePanel = new PolylinePanel();
        linePanel.addMouseListener(new MouseAdapter() {

            public void mouseClicked(MouseEvent e) {
                if(button.isSelected()) {
                    if (currentLine==null) {
                        currentLine = new Polyline(null, 10);
                        linePanel.addLine(currentLine);
                    }
                    currentLine.add(e.getPoint());
                    linePanel.repaint();
                }
                else {
                    int index = linePanel.getLineIndexFor(e.getPoint());
                    if (index >=0)
                        statusLabel.setText("You clicked on line: " + index);
                    else
                        statusLabel.setText("To draw a line press the button and click into the panel");
                }
            }
        });
        button = new JToggleButton("Activate Drawing");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                currentLine = null;
                if (button.isSelected()) {
                    button.setText("Drawing...");
                    statusLabel.setText("Press the button when you want to finish drawing");
                }
                else {
                    button.setText("Activate Drawing");
                    statusLabel.setText("To draw a line press the button and click into the panel");
                }
            }
        });
        statusLabel = new JLabel();
        statusLabel.setBackground(Color.WHITE);
        statusLabel.setOpaque(true);
        statusLabel.setBorder(BorderFactory.createEtchedBorder());
        statusLabel.setText("To draw a line press the button and click into the panel");
        this.getContentPane().add(linePanel, BorderLayout.CENTER);
        this.getContentPane().add(button, BorderLayout.NORTH);
        this.getContentPane().add(statusLabel, BorderLayout.SOUTH);
        
  }

// ------------------------------------------------------------------------------------------------------------
 
    public void neueKanteButton_ActionPerformed(ActionEvent evt)
    {
       clickZaehler++;      // zählt wie oft auf den Button gedrückt wurde
       if (anfangPolygon == 1)
        {
           anfangPolygon = 0;   // auf 0 setzen damit ich bei neuer Kante später wieder neue Auswahl treffen kann
           darfzeichnen = false;
           KnotenArray.add(KantenArray.get(KantenArray.size()-1));   // fügt dem Knotenarray den letzten Eintrag der Kante hinzu
           neuerButton = new JButton();                         // neuer Button wird an den anfang der kante gezeichnet
           neuerButton.addActionListener(new ActionListener()    // es wird ein ActionListener für alle Buttons erzeugt, da alle Buttons die Eigenschaften auf der rechten Seite verändern können soll
              {                                                   // zudem soll nicht für jeden einzelnen Button eine extra Methode mit ActionPerformed erzeugt werden
                public void actionPerformed(ActionEvent evt)
                 {
                    neuerButton_ActionPerformed(evt);
                 }
              }
                                           );
           ButtonArray.add(neuerButton);
           kartenPanel.repaint();
           neueKanteButton.setText("Neue Kante erstellen");
           clickZaehler = 0;     // auf 0 setzen damit manuelles drücken auf den neue Kante Button funktioniert
           neueKanteButton.setFont(new Font("MS Sans Serif", Font.BOLD, 13));
           neueKanteButton.setForeground(Color.BLACK);
        }

       if (clickZaehler == 1)    // einmal geklickt --> es dürfen Knoten und Straßen eingezeichnet werden
        {
          darfzeichnen = true;
          neueKanteButton.setText("Kante fertigstellen");
          neueKanteButton.setFont(new Font("Arial", Font.BOLD, 16));
          neueKanteButton.setForeground(Color.RED);
          kartenPanel.repaint();
        }
       else                      // mehr als einmal geklickt --> es dürfen keine Knoten und Straßen mehr eingezeichnet werden
        {
          darfzeichnen = false;
          neueKanteButton.setText("Neue Kante erstellen");
          clickZaehler = 0;
          neueKanteButton.setFont(new Font("MS Sans Serif", Font.BOLD, 13));
          neueKanteButton.setForeground(Color.BLACK);
          kartenPanel.repaint();

          int Punkteanzahl = KantenArray.size()*2;   // Anzahl der Knotenpunke wird verdoppelt, da der Straßenzug verschoben wieder rückwärts geht
          int[] xWerte = new int[Punkteanzahl];      // Array um die aktuellen x-Werte abzuspeichern der Punkte
          int[] yWerte = new int[Punkteanzahl];      // Array um die aktuellen y-Werte abzuspeichern der Punkte

          for (int i = 0; i < KantenArray.size(); i = i + 1)
           {                                                  // die erste Schleife dient den Originalwerten
             Point t = KantenArray.get(i);              // auslesen des Knotenpunktes an der Stelle i
             xWerte[i] = t.x;
             yWerte[i] = t.y;
           }

          for (int i = 0; i < KantenArray.size(); i = i + 1)  // die zweite Schleife füllt das Array mit dem verschobenen Straßenzug in umgekehrter Reihenfolge auf
           {
             Point t = KantenArray.get(KantenArray.size()-1-i);
             xWerte[KantenArray.size()+i] = t.x+7;
             yWerte[KantenArray.size()+i] = t.y+7;
           }

          Polygon polygonzug = new Polygon(xWerte, yWerte, Punkteanzahl);   // erstellen eines Polygon aus den abgespeicherten x-Werten, y-Werten und der Anzahl der Paare
          polygonKante = new PolygonButton(polygonzug);               // erstellen eines neuen dummyButtons für die Straße am Ende
          polygonKante.addActionListener(new ActionListener()  // es wird ein ActionListener für alle Buttons erzeugt, da alle Buttons die Eigenschaften auf der rechten Seite verändern können soll
           {                                                   // zudem soll nicht für jeden einzelnen Button eine extra Methode mit ActionPerformed erzeugt werden
             public void actionPerformed(ActionEvent evt)
              {
                polygonKante_ActionPerformed(evt);
              }
           }
                              );
          ButtonKantenArray.add(polygonKante);      // fügt den Button dem Array hinzu welches die Straßenbuttons verwaltet
          KantenArray.clear();      // alle Koordinaten der zuletzt gezeichneten Straße werden gelöscht um Platz zu machen für die nächste
        }
    }

    public void neuerButton_ActionPerformed(ActionEvent evt)
    {
       int gefunden = 0;
       JButton suchen = (JButton) evt.getSource();
       for (int i = 0; i < ButtonArray.size(); i++ )     // hier wird das Buttonarray auf der suche nach dem gerade gedrückten Button durchsucht
         {
           if ( suchen == ButtonArray.get(i) )
            {
              gefunden = i;
              break;
            }
         }

       if (darfzeichnen == true) // man befindet sich im Modus eine neue Kante einzeichnen zu können
        {
           anfangPolygon++;     // wird um eins erhöht wenn auf den Button geklickt wurde, nun Unterscheidung ob es der Anfangs- oder Endpunkt der Kante wird
           if (anfangPolygon == 1)     // hierbei handelt es sich um den Anfang einer Kante welche mit einem bereits existierenden Button beginnt
           { }
           else if (anfangPolygon == 2)   // hierbei handelt es sich um das Ende einer Kante welche mit einem bereits existierenden Button endet
           {
               neueKanteButton.setText("Neue Kante erstellen");
               clickZaehler = 0;     // auf 0 setzen damit manuelles drücken auf den neue Kante Button funktioniert
               neueKanteButton.setFont(new Font("MS Sans Serif", Font.BOLD, 13));
               neueKanteButton.setForeground(Color.BLACK);
               darfzeichnen = false;
               anfangPolygon = 0;   // auf 0 setzen damit ich bei neuer Kante später wieder neue Auswahl treffen kann
           }
        }
    }
    
    public void polygonKante_ActionPerformed(ActionEvent evt)       // zuständig um die Eigenschaften der Straßen im Array abzuspeichern
    {
       int gefunden = 0;
       JButton suchen = (JButton) evt.getSource();
       for (int i = 0; i < ButtonKantenArray.size(); i++ )         // hier wird das Buttonarray auf der suche nach dem gerade gedrückten Button durchsucht
         {
           if ( suchen == ButtonKantenArray.get(i) )
            {
              gefunden = i;
              break;
            }
         }
    }

    public void ArraysVerwalten(Point Click)
    {
        Point aktuellerClick = new Point();
        aktuellerClick = Click;                // übernehmen des übergebenen Mouseclicks
        
        if (anfangPolygon == 0)    // 0 bedeutet dass eine neue Kante noch keinen Anfang und kein Ende hat
         {
             anfangPolygon = 1;    // nun besitzt die Kante einen Anfang
             KantenArray.add(aktuellerClick);
             KnotenArray.add(aktuellerClick);
             neuerButton = new JButton();                         // neuer Button wird erzeugt
             neuerButton.addActionListener(new ActionListener()   // es wird ein ActionListener für alle Buttons erzeugt, da alle Buttons die Eigenschaften auf der rechten Seite verändern können soll
              {                                                   // zudem soll nicht für jeden einzelnen Button eine extra Methode mit ActionPerformed erzeugt werden
                public void actionPerformed(ActionEvent evt)
                 {
                    neuerButton_ActionPerformed(evt);
                 }
              }
                                           );
             ButtonArray.add(neuerButton);
             validate();
             kartenPanel.repaint();
         }
        else if (anfangPolygon == 1)    // die Kante besitzt bereits einen Anfangsknoten aber noch kein Ende, dieses muss erstellt werden
         {
             KantenArray.add(aktuellerClick);
             kartenPanel.repaint();        // damit aktuelle Kante gleich eingezeichnet wird
         }
     }

    public static void main(String[] args)
    {
        new Landkarte("Route festlegen");
    }

 // ---------------------------------------------------------------------------

    class PolylinePanel extends JPanel {
        private ArrayList<LineContainer> lines;
        private Color[] color;

        public PolylinePanel() {
            lines = new ArrayList<LineContainer>();
            color = new Color[] {Color.BLACK, Color.ORANGE, Color.BLUE};
            this.addMouseMotionListener(new MouseMotionAdapter() {
                public void mouseMoved(MouseEvent evt) {
                    for (LineContainer line : lines) {
                        if (line.getStatus()!=2);
                            line.setStatus(0);
                    }

                    int index = getLineIndexFor(evt.getPoint());
                    if (index >= 0)
                        lines.get(index).setStatus(1);
                    repaint();
                }
            });
        }

        public void addLine(Polyline line) {
            this.lines.add(new LineContainer(line, 0));
        }

        public int getLineIndexFor(Point p) {
            for (int i=lines.size()-1 ; i>=0; i--) {
                LineContainer lineContainer = lines.get(i);
                if (lineContainer.getLine().contains(p)) {
                    return i;
                }
            }
            return -1;
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (image != null)
               g.drawImage(image, 0, 0, null);
            Graphics2D g2 = (Graphics2D)g.create();
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            for(LineContainer lineContainer : lines) {
                g2.setColor(color[lineContainer.getStatus()]);
                g2.setStroke(new BasicStroke(lineContainer.getStrokeWidth(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER));
                lineContainer.getLine().paint(g2);
            }
            g2.dispose();
        }

        class LineContainer {
            private Polyline line;
            private int status;

            public LineContainer(Polyline line, int status) {
                this.line = line;
                this.status = status;
            }

            public Polyline getLine() {
                return line;
            }

            public int getStrokeWidth() {
                return line.getStrokeWidth();
            }

            public void setStatus(int status) {
                this.status = status;
            }

            public int getStatus() {
                return status;
            }
        }
    }

    class Polyline  {
        private ArrayList<Point> points;
        private Rectangle boundingRect;
        private int strokeWidth;
        private float tolerance;

        public Polyline(ArrayList<Point> points) {
            this(points, 0);
        }

        public Polyline(ArrayList<Point> points, int width) {
            if (points==null)
                this.points = new ArrayList<Point>();
            else
                this.points = points;
            this.setStrokeWidth(width);
        }

        public void add(Point p) {
            this.points.add(p);
            this.calculateBounds();
        }

        public void setStrokeWidth(int width) {
            this.strokeWidth = width;
            this.tolerance = 0.5f;

            this.calculateBounds();
        }

        public int getStrokeWidth() {
            return this.strokeWidth;
        }

        private void calculateBounds() {
            if (points.size()<=0) {
                boundingRect = new Rectangle(0, 0, 0, 0);
                return;
            }
            Point p = points.get(0);
            int xMin, xMax, yMin, yMax;
            xMin = xMax = p.x;
            yMin = yMax = p.y;
            for(int i=1; i<points.size(); i++) {
                p = points.get(i);
                xMin = (p.x<xMin)?p.x:xMin;
                yMin = (p.y<yMin)?p.y:yMin;
                xMax = (p.x>xMax)?p.x:xMax;
                yMax = (p.y>yMax)?p.y:yMax;
            }
            int intTol = (int)tolerance;
            boundingRect = new Rectangle(xMin-intTol, yMin-intTol,
                    (xMax-xMin)+ 2*intTol +1, (yMax-yMin) + 2*intTol +1);
        }

        public Rectangle getBounds() {
            return this.boundingRect;
        }

        public boolean contains(Point p) {
            return this.contains(p.x, p.y);
        }

        public boolean contains(double x, double y) {
            if (this.boundingRect.contains(x, y)) {
                if (points.size()<2)
                    return true;
                Point p1 = points.get(0);
                for(int i=1; i<points.size(); i++) {
                    Point p2 = points.get(i);
                    if (isBetween(p1, p2, x, y))
                        return true;
                    p1 = p2;
                }
            }
            return false;
        }

        private boolean isBetween(Point p1, Point p2, double x, double y) {
            double p1X = p1.x, p1Y = p1.y;
            double p2X = p2.x, p2Y = p2.y;
            double t = tolerance;
            //Checking if values are the coordinates of one of the points
            if ((p1X-t <= x && p1X+t >= x && p1Y-t <= y && p1Y+t >= y) ||
                    (p2X-t <= x && p2X+t >= x && p2Y-t <= y && p2Y+t >= y))
                return true;

            //Checking if values are between both points by calculating the distances
            double distance = Math.sqrt(Math.pow(p1X - p2X, 2) + Math.pow(p1Y - p2Y, 2));
            double distanceValue = Math.sqrt(Math.pow(x - p1X, 2) + Math.pow(y - p1Y, 2))
                + Math.sqrt(Math.pow(x - p2X, 2) + Math.pow(y - p2Y, 2));

            int minDist = (int)((distance-t)*100);
            int maxDist = (int)((distance+t)*100);
            int calcDist = (int)((distanceValue)*100);

            return (minDist<=calcDist && calcDist<=maxDist);
        }

        public void paint(Graphics2D g) {
            if (points.size()>1) {
                Point p1 = points.get(0);
                for(int i=1; i<points.size(); i++) {
                    Point p2 = points.get(i);
                    g.drawLine(p1.x, p1.y, p2.x, p2.y);
                    p1 = p2;
                }
            }
        }
    }
}

und

Java:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;

public class PolygonButton extends JButton
 {
   public Polygon polygonzug;   // definieren des Polygons

   public PolygonButton(Polygon uebergebenesPolygon)     // Aufruf der Klasse und übergeben des Polygons
    {
        if(uebergebenesPolygon != null)
         {
             polygonzug = uebergebenesPolygon;    // damit wird das übergebene Polygon absgespeichert
         }
        else
         {
         }
        setContentAreaFilled(false);    // Hintergrund des Buttons wird nicht ausgemalt
    }

 // ----------------------------------------------------------------------------------------------------------------

    protected void paintComponent(Graphics g)         // zeichnen des Polygons
     {
        if (getModel().isArmed())      // wenn der Button angeklickt wird ändert er seine Farbe
         {
             g.setColor(Color.lightGray);    // Farbe wird auf Grau gesetzt
         }
        else
         {
             g.setColor(getBackground());    // Farbe des Buttons entspricht der Hintergrundfarbe
         }
        g.fillPolygon(polygonzug);       // übergeben des Polygons an das Grafikobjekt
        super.paintComponent(g);         // zeichnen des ausgemalten Polygons
    }

 // ----------------------------------------------------------------------------------------------------------------

    protected void paintBorder(Graphics g)         // zeichnen der Kante des Buttons
     {
        g.setColor(getForeground());
        g.drawPolygon(polygonzug);
     }

 // ----------------------------------------------------------------------------------------------------------------

    public boolean contains(int x, int y)     // überprüfen ob der Mouseclick innerhalb der Buttonfläche ist
     {
        return polygonzug.contains(x, y);
     }
}
 

Michael...

Top Contributor
Ok, ich hab jetzt mal schnell versucht das in das Beispiel einzubinden
Der Fehler liegt in dem schnell ;-)
Grundsätzlich gilt klein anfangen und langsam ausbauen. Die Klasse PolygonButton brauchst Du jetzt ja garnicht mehr und was ist mit dem Button "Neue Kante" erstellen? Und diese Listen für Buttons?

Ich weiß zwar immer noch nicht was Du genau vorhast, aber im wesentlichen hat doch meine Demo das gleiche gemacht wie Deine. Nimm diese halt her und baue Sie Stück für Stück aus.
 

erazor2106

Bekanntes Mitglied
ja, das habe ich nicht rausgelöscht weil es im moment sowieso nicht gebraucht wird. :oops:

das Programm dient wie gesagt dazu einen ungerichteten graphen zeichnen zu können. wenn es klappt dein Panel funktionstüchtig einzubauen, brauche ich die restlichen 1000 zeilen quelltext nicht auf dein Programm anzupassen, verstehst du? das wäre denke ich mal ein großer Mehraufwand.
ich denke halt dass beim obigen Programm nur eine Kleinigkeit fehlt oder falsch ist, so dass gezeichnet werden kann. dann wäre mein problem eigentlich schon fertig
 

Michael...

Top Contributor
Achso um Graphen geht's. Und die Buttons in der Liste sollen dann wohl die Knoten sein? Die würde ich dann aber auch selbst zeichnen.
Soll das Ding dann einfach nur Graphen zeichnen oder soll damit dann auch was gemacht werden, z.B. bereits angelegte Graphen editieren...

Unter dem Gesichtspunkt solltest Du Dir Dein Konzept vielleicht nochmal überdenken und ein bisschen Logik und Objektorientierung mit reinbringen, dabei helfen solche Fragestellungen wie:
- Was ist ein Graph?
- Woraus besteht er? (Nicht aus Buttons und Polygonen oder Polylinien)
- In welcher Beziehung stehen die Bestandteile zueinander
- Welche Eigenschaften haben diese Bestandteile
...

Ein Fehler ist übrigens, dass Du zwei Komponenten (linePanel & kantenPanel) in den NORTH Bereichs des Frames setzt.
Unschön ist auch, dass Du den Frame sichtbar machst bevor das Layout steht.
 

erazor2106

Bekanntes Mitglied
mensch mensch mensch, das wird ja langsam ganz schön peinlich für mich :oops:

nochmal zur grundidee: ich möchte einen ungerichtete graphen mit der maus zeichnen können, wobei man neue Knotenpunkte erstellen kann, Verbindungslinien, also Kanten dazwischen, und später auch diese Elemente wieder entfernen können. zudem wäre es super wenn man z.b eine neue Kante einzeichnen möchte und der endpunkt auf einer bereits existierenden Kante liegt, dort ein neuer Knotenpunkt eingezeichnet wird und die Linie quasi gesplittet.

ist der Graph oder zumindest ein teil davon gezeichnet, soll in einer im Ostteil des Frames gelegene Menüleiste werte der Knoten und kanten geändert werden können. also ich klick z.B. auf den Knoten mit der Nummer 2 und kann dann rechts verschiedene werte dazu eintragen und in einer textdatei abspeichern, wie z.b. name und wert. das gleiche soll auch für die kanten gelten. das ist der grund für die ganze umständliche sache. da sich dies nur realsiieren lässt wenn die elemente sich anklicken lassen.
 

erazor2106

Bekanntes Mitglied
danke nochmal.
ich habe es jetzt einigermaßen gut (für meine Verhältnisse :) ) in mein bestehendes Programm eingebunden, so dass fast alle FUnktionen von vorher nun damit laufen.
Muss nur noch mal intensiver deine VOrgehensweise studieren und schauen wo und die wie Kantenobjekte gespeichert sind, damit ich später auf sie über das array zugreifen kann und wie ich von außen noch einen abschließenden Punkt zum Pfad hinzufügen kann
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
K draw Polyline will nicht wie Polygon Java Basics - Anfänger-Themen 2
B Objete von Polygon mit TreeSet verwalten Java Basics - Anfänger-Themen 1
I Vererbung Polygon erweitern ? Java Basics - Anfänger-Themen 4
F Polygon vergrößern Java Basics - Anfänger-Themen 8
S Polygon contains - Erläuterung Java Basics - Anfänger-Themen 3
S Speicherbedarf Pixel vs. Polygon? Java Basics - Anfänger-Themen 7
M Polygon umkreisen? Java Basics - Anfänger-Themen 47
K Methoden contains()-Methode für Punkt in Polygon Java Basics - Anfänger-Themen 5
R Polygon erweitern Java Basics - Anfänger-Themen 10
M Polygon Punkte im Uhrzeigersinn sortieren Java Basics - Anfänger-Themen 2
G Polygon in Frame zeichnen Java Basics - Anfänger-Themen 3
K Polygon Java Basics - Anfänger-Themen 14
C Polygon um Figur bestimmen Java Basics - Anfänger-Themen 10
K Polygon in Java3D (Java 3D) zeichnen Java Basics - Anfänger-Themen 4
T Polygon.contains Fehler Java Basics - Anfänger-Themen 2
Rene_Meinhardt Polygon.Contains() funktioniert nicht richtig? Java Basics - Anfänger-Themen 3
0 problem beim Polygon zeichnen Java Basics - Anfänger-Themen 3
G bild in polygon zeichnen Java Basics - Anfänger-Themen 6
G Polygon Java Basics - Anfänger-Themen 7
I drehendes polygon Java Basics - Anfänger-Themen 4
J MouseListener fuer gezeichnete Polyline Java Basics - Anfänger-Themen 3
B polyline auf buffered image malen Java Basics - Anfänger-Themen 2

Ähnliche Java Themen

Neue Themen


Oben