2D-Grafik Ellipsenfeld

Ometi

Mitglied
Hallo zusammen!

ich kann nun "schon" Ellipsen malen. - Gut und schön... Ich habe allerdings bisher noch nicht herausbekommen, wie ich Folgendes umsetze: Ich möchte ein Feld von Objekten erzeugen, die hinterher als Ellipsen (oder von mir aus auch als Rechtecke) dargestellt werden. Diese Objekte sollen allerdings nicht nur anhand ihrer Position auswählbar sein, sondern auch anhand eines weiteren Attributes (z.B. ein Gewicht), welches ihm bei der Instantiierung zugeordnet wurde. Anhand der Position im Feld und anhand des Gewichts, möchte ich das jeweilige Objekt aus hieraus wieder entfernen.

Wie setze ich das um?

Vorab vielen Dank!
 

Flown

Administrator
Mitarbeiter
Was hast du bereits? Wo liegt das tatsächliche Problem?
Algorithmus:
Iteriere über alle Ellipsen:
- Ist der Mausklick innerhalb der Ellipse? Wenn ja, stimmt das Gewicht mit dem gewählten überein? Wenn ja, lösche es aus der Liste; sonst nächstes Element
 

Ometi

Mitglied
Nun, ich habe eine Klasse Test (inkl. Main- und paintComponent-Methode) und eine Klasse DingensEllipse, die Ellipse2D erweitert. Ich weiß, wie ich Test in der Methode paintComponent(.) einfache Ellipsen erzeuge bzw. ausgeben lasse. Aber diese Ellipsen haben ja nichts mit meiner Klasse DingensEllipse zu tun, die ja noch dieses spezifische Attribut Gewicht enthalten soll.
In paintComponent gelingt es mir allerdings nicht, mit g.draw usw. die DingensEllipse-Objekte zu erzeugen bzw. diese in einem Fenster ausgeben zu lassen...

Ich möchte ungerne mit Listen arbeiten; Ein Algorithmus soll hinterher sozusagen direkt im Feld über die x,y-Koordinaten die Dingens-Objekte auswählen und entfernen. Aber dazu wollte ich mir erst Gedanken machen, wenn ich weiß, wie ich die Dingens-Ellipsen erzeuge und sukzessive wieder entfernen kann...
 

Ometi

Mitglied
Das Problem liegt in dem Fettgeschriebenen; t gibt er mir aus, allerdings nicht t2...
public void paintComponent(Graphics gfx)
{
super.paintComponent(gfx);
Graphics2D g= (Graphics2D) gfx;


Ellipse2D.Double t =new Ellipse2D.Double(50,60,300,200);

g.setPaint(Color.red);
g.fill(t);
g.draw(t);


DingensEllipse t2= new DingensEllipse(60,90,100,120);

g.setPaint(Color.blue);
g.fill(t2);
g.draw(t2);


}
 

Ometi

Mitglied
Das ist eine gute Frage ;-)
Ich dachte, das geschieht direkt in dem Moment der Erzeugung, wenn ich in der Main frame.getContentpane().add(test) aufrufe, und der Compiler hält das schon irgendwie auseinander.

Und selbst wenn ich t weglasse und nur mit t2 arbeite gibt er mir nicht die entsprechende Ellipse aus.
 

Flown

Administrator
Mitarbeiter
Na siehst du? Du musst deine Ellipsen speichern, um sie auch wieder zu finden und zu zeichnen. Hier ein Beispiel:
Java:
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Test {
  
  public static void main(String... args) {
    SwingUtilities.invokeLater(Test::new);
  }
  
  public Test() {
    JFrame frame = new JFrame("Circles");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLocationByPlatform(true);
    frame.setSize(800, 600);
    
    MyCanvas canvas = new MyCanvas();
    canvas.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent e) {
        super.mouseClicked(e);
        if (SwingUtilities.isLeftMouseButton(e)) {
          canvas.addRectangleAt(e.getX(), e.getY());
        } else if (SwingUtilities.isRightMouseButton(e)) {
          canvas.removeRectangleAt(e.getX(), e.getY());
        }
      }
    });
    
    frame.add(canvas, BorderLayout.CENTER);
    frame.setVisible(true);
  }
  
}

class MyCanvas extends JComponent {
  private static final long serialVersionUID = 457852542350242102L;
  
  private static final double RECTANGLE_WIDTH = 50d;
  private static final double RECTANGLE_HEIGHT = 30d;
  
  private Collection<Rectangle2D> rectangles = new ArrayList<>();
  
  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (g instanceof Graphics2D) {
      Graphics2D g2 = (Graphics2D) g;
      
      for (Rectangle2D circle : rectangles) {
        g2.draw(circle);
      }
    }
  }
  
  public void addRectangleAt(int x, int y) {
    rectangles.add(
        new Rectangle2D.Double(x - RECTANGLE_WIDTH / 2, y - RECTANGLE_HEIGHT / 2, RECTANGLE_WIDTH, RECTANGLE_HEIGHT));
    repaint();
  }
  
  public void removeRectangleAt(int x, int y) {
    for (Iterator<Rectangle2D> it = rectangles.iterator(); it.hasNext();) {
      Rectangle2D rectangle = it.next();
      if (rectangle.contains(x, y)) {
        it.remove();
        break;
      }
    }
    repaint();
  }
}
Es zeichnet Rectangle2D mit Linksklick und löscht es mit Rechtsklick, wenn es innerhalb eines Rectangles ist.
 

Ometi

Mitglied
Hui, riesen Dank schon mal für die Mühe...!

Ich muss mir dafür erstmal Zeit nehmen, um da durch zu steigen...
Vorab:
public static void main(String... args) {
SwingUtilities.invokeLater(Test::new);
}
So kenne ich das nicht, was passiert da?
 

Flown

Administrator
Mitarbeiter
String... gibts schon seit Java 5 und nennt sich "variable arguments": HIER
Test::new gibts seit Java 8 und nennt sich method/constructor references und ist Teil von Lambdaexpressions: HIER

Letzteres kann auch so geschrieben werden:
Java:
SwingUtilities.invokeLater(new Runnable() {
  @Override
  public void run() {
    new Test();
  }
});
 

Ometi

Mitglied
Ok,
soweit blicke ich wohl durch;
und ich nehme an, es ist nun auch leicht umsetzen, den Rechtecken jeweils ein Gewicht zuzuordnen?
 

Ometi

Mitglied
OK, Du kommst Dir bestimmt vor, wie ich mir vorkäme, wenn mich jemand zur for-Schleifen ausquetschen würde, aber bitte, nicht ganz so knapp ... ;)

Mein Problem, mir das Ganze vorzustellen, liegt darin, dass offenbar ja irgendwie immer nur die eine paintComponent-Methode aufgerufen wird. Da lege ich doch die Farbe fest. Wie ändere ich die denn dann?
Wenn Du sagst, ich muss die Farbe als Attribut hinzufügen, dann bedeutet das ja, dass ich eine eigene Rectangle-Klasse schreiben muss (was ich ich ja auch vor hatte, was Du allerdings nicht gemacht, wenn ich das richtig sehe...)

Soll ich nun eine Klasse Dingens-Ellipse aufmachen, die Rectangle2D erweitert? Wenn ja, wozu sind die ganze Methoden da, die ich implementieren bzw. überschreiben muss?
 

Ometi

Mitglied
Ich habe nun die Klasse Dingens erstellt, und entsprechend Deinen Code modifiziert. Wie ich es mir gedacht hatte, wird nun nichts mehr insF enster gemalt.

Java:
class MyCanvas extends JComponent {
 private static final long serialVersionUID = 457852542350242102 L;
 private static final double RECTANGLE_WIDTH = 50 d;
 private static final double RECTANGLE_HEIGHT = 30 d;
 private Collection < Dingens > rectangles = new ArrayList < > ();
 @Override
 protected void paintComponent(Graphics g) {
  super.paintComponent(g);

  if (g instanceof Graphics2D) {
   Graphics2D g2 = (Graphics2D) g;

   for (Dingens circle: rectangles) {
    g2.setColor(circle.farbe);
    g2.fill(circle);
    g2.draw(circle);
   }
  }
 }
 public void addRectangleAt(int x, int y) {
  rectangles.add(new Dingens(x - RECTANGLE_WIDTH / 2, y - RECTANGLE_HEIGHT / 2, Color.BLACK));

  repaint();
 }



 import java.awt.Color;
 import java.awt.Rectangle;
 import java.awt.geom.Rectangle2D;


 public class Dingens extends Rectangle2D {
  double x, y;
  double gewicht;
  Color farbe;
  public Dingens(double x, double y, Color f) {
   this.x = x;
   this.y = y;
   this.farbe = f;

  }


  public void setFarbe(Color f) {
   this.farbe = f;
  }
  @Override
  public Rectangle2D createIntersection(Rectangle2D r) {
   // TODO Auto-generated method stub
   return null;
  }

  @Override
  public Rectangle2D createUnion(Rectangle2D r) {
   // TODO Auto-generated method stub
   return null;
  }

  @Override
  public int outcode(double x, double y) {
   // TODO Auto-generated method stub
   return 0;
  }

  @Override
  public void setRect(double x, double y, double w, double h) {
   // TODO Auto-generated method stub

  }

  @Override
  public double getHeight() {
   // TODO Auto-generated method stub
   return 0;
  }

  @Override
  public double getWidth() {
   // TODO Auto-generated method stub
   return 0;
  }

  @Override
  public double getX() {
   // TODO Auto-generated method stub
   return 0;
  }

  @Override
  public double getY() {
   // TODO Auto-generated method stub
   return 0;
  }

  @Override
  public boolean isEmpty() {
   // TODO Auto-generated method stub
   return false;
  }
 }
 
Zuletzt bearbeitet von einem Moderator:

Flown

Administrator
Mitarbeiter
Du solltest eher eine eigene Klasse schreiben. Ich hab dir mal mein Beispiel modifiziert, damit du eine Ahnung hast wies gehen könnte:
Java:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Test {
  
  public static void main(String... args) {
    SwingUtilities.invokeLater(Test::new);
  }
  
  public Test() {
    JFrame frame = new JFrame("Circles");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLocationByPlatform(true);
    frame.setSize(800, 600);
    
    MyCanvas canvas = new MyCanvas();
    canvas.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent e) {
        super.mouseClicked(e);
        if (SwingUtilities.isLeftMouseButton(e)) {
          canvas.addRectangleAt(e.getX(), e.getY());
        } else if (SwingUtilities.isRightMouseButton(e)) {
          canvas.removeRectangleAt(e.getX(), e.getY());
        }
      }
    });
    
    frame.add(canvas, BorderLayout.CENTER);
    frame.setVisible(true);
  }
  
}

class MyCanvas extends JComponent {
  private static final long serialVersionUID = 457852542350242102L;
  
  private static final int RECTANGLE_WIDTH = 50;
  private static final int RECTANGLE_HEIGHT = 30;
  
  private Collection<MyRectangle> rectangles = new ArrayList<>();
  
  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    for (MyRectangle rectangle : rectangles) {
      rectangle.draw(g);
    }
  }
  
  public void addRectangleAt(int x, int y) {
    rectangles.add(new MyRectangle(x - RECTANGLE_WIDTH / 2, y - RECTANGLE_HEIGHT / 2, RECTANGLE_WIDTH, RECTANGLE_HEIGHT,
        new Color((float) Math.random(), (float) Math.random(), (float) Math.random(), (float) Math.random())));
    repaint();
  }
  
  public void removeRectangleAt(int x, int y) {
    for (Iterator<MyRectangle> it = rectangles.iterator(); it.hasNext();) {
      MyRectangle rectangle = it.next();
      if (rectangle.contains(x, y)) {
        it.remove();
        break;
      }
    }
    repaint();
  }
}

class MyRectangle {
  private int x, y;
  private int width, height;
  private Color color;
  
  public MyRectangle(int x, int y, int width, int height, Color color) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.color = color;
  }
  
  public int getX() {
    return x;
  }
  
  public void setX(int x) {
    this.x = x;
  }
  
  public int getY() {
    return y;
  }
  
  public void setY(int y) {
    this.y = y;
  }
  
  public int getWidth() {
    return width;
  }
  
  public void setWidth(int width) {
    this.width = width;
  }
  
  public int getHeight() {
    return height;
  }
  
  public void setHeight(int height) {
    this.height = height;
  }
  
  public Color getColor() {
    return color;
  }
  
  public void setColor(Color color) {
    this.color = color;
  }
  
  public boolean contains(int x, int y) {
    return this.x <= x && x <= this.x + this.width && this.y <= y && y <= this.y + this.height;
  }
  
  public void draw(Graphics g) {
    Color old = g.getColor();
    g.setColor(color);
    g.fillRect(x, y, width, height);
    g.setColor(old);
  }
}
 

Ometi

Mitglied
Danke!
Ich habe in der Zeit auch nochmal ein wenig gebastelt, und folgendes herausbekommen:
Er malt nun bei Linksklick schwarze Ovale (wir haben bald alle Formen durch ^^);
Aber dafür bekomme ich das entfernen nicht hin; woran liegt das?

Java:
//
 protected void paintComponent(Graphics g) 
  {
    super.paintComponent(g);
   
   
      Graphics g2 =  g;
     
      for (Dingens circle : rectangles)
      {
        g2.setColor(circle.farbe);
        g2.fillOval(circle.x, circle.y, 50, 50);
      }
   
  }
  public void addRectangleAt(int x, int y)
  {
    rectangles.add(new Dingens(x,y, Color.BLACK));
   
    repaint();
  }
  public void removeRectangleAt(int x, int y) 
  {
     for (Iterator<Dingens> it = rectangles.iterator();it.hasNext();) 
       {
         Dingens rectangle = it.next();
         if (rectangle.contains(x, y)) 
         {
           it.remove();
           
           break;
         }
   
    repaint();
       }
  }
}
 

Ometi

Mitglied
Also habe nun ein Feld, dem ich Rechtecke hinzufügen, und die wieder entfernen bzw. umfärben kann.
Jetzt möchte ich mit dem Feld so wie ich es erstellt habe nicht selbst etwas machen, sondern ein Algorithmus das Umfärben übernehmen.
Ich stelle mir das so vor: Nachdem ich das Fenster mit dem erstellten Feld geschlossen habe, soll der Algorithmus anfangen, und mir dann in einem neuen Fenster das Ergebnis ausgeben... Geht sowas?
 

Ometi

Mitglied
Ok :)

Ich habe ein wenig ausprobiert;
Java:
//
[...]
    frame.add(feld);
       frame.setVisible(true);
      
      
       Feld feld2= entferneRot(feld);
    
          JFrame frame2 = new JFrame("Circles");
       frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       frame2.setLocationByPlatform(true);
       frame2.setSize(800, 600);
      
       frame2.add(feld2);
      
       frame2.setVisible(true);
    
      
      
      
    }
  
  
  

    private Feld entferneRot(Feld feld)
    {
        for(Truemmer t: feld.rectangles)
        {
            if(t.getColor()==Color.red)
            {
                t.setColor(Color.cyan);
            }
        }
      
        return feld;
      
    }

Ergebnis: Er macht mir direkt zwei Fenster aus, in dem ersten kann ich wie gehabt die Ellipsen einfügen, in dem anderen kann ich gar nichts machen. Wenn ich ein Fenster schließe, schließt sich automatisch das zweite. Muss ich hier etwa mit Threads arbeiten?
 
Zuletzt bearbeitet:

Flown

Administrator
Mitarbeiter
Bei dem ersten JFrame ist die setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) gesetzt worden und das bedeutet, sobald das Fenster geschlossen wird, wird das ganze laufende Programm geschlossen. Du brauchst eher JFrame.DISPOSE_ON_CLOSE oder du lässt die Zeile weg, denn dieser Wert ist der Standard.
 

Ometi

Mitglied
gut, nun kann ich aber nicht mit dem Feld nicht weiter arbeiten, so wie´s erschaut.
Z.B. passiert hierbei
Java:
//
   frame.add(feld);
       frame.setVisible(true);
       
       for(int i=0;i<feld.rectangles.size();i++)
       {
          System.out.println("blalala");
       }
[...]
nichts.
 

Flown

Administrator
Mitarbeiter
So ich zeig dir mal wie das mit dem MVC-Pattern funktioniert. Bei Fragen bitte erst mal die Dokumentation durchforsten oder auch die offiziellen Tutorials!
Auch hab ich noch ein Observer- und Adapter-Pattern miteingebaut.

Starte den Code und sieh ihn dir in Ruhe an. Wenn es dir hilft, dann Teile die Klassen doch auf und tu jede in ein einzelnes File. Dann geh es Schritt für Schritt durch und dann komm bitte wieder zurück mit konkreten Fragen + Code.

Java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
import java.util.Observable;
import java.util.Observer;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Test {
  public static void main(String... args) {
    SwingUtilities.invokeLater(Test::new);
  }
  
  public Test() {
    JFrame frame = new JFrame("MyRectangles");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLocationByPlatform(true);
    frame.setSize(800, 600);
    MyRectangleModel model = new MyRectangleModel();
    JPanel mainPanel = new JPanel(new GridLayout(1, 2));
    MyEditingView editingView = new MyEditingView(model);
    editingView.setBorder(BorderFactory.createLineBorder(Color.BLACK));
    mainPanel.add(editingView);
    MyBlackWhiteView blackWhiteView = new MyBlackWhiteView(model);
    blackWhiteView.setBorder(BorderFactory.createLineBorder(Color.BLACK));
    mainPanel.add(blackWhiteView);
    frame.add(mainPanel);
    frame.setVisible(true);
  }
}

final class MyEditingView extends JComponent implements Observer {
  private static final long serialVersionUID = 125470717104438986L;
  
  private static final int RECTANGLE_WIDTH = 50;
  private static final int RECTANGLE_HEIGHT = 30;
  
  private final MyRectangleModel model;
  
  public MyEditingView(MyRectangleModel model) {
    this.model = model;
    this.model.addObserver(this);
    addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent e) {
        super.mouseClicked(e);
        if (SwingUtilities.isLeftMouseButton(e)) {
          MyRectangle rectangle = new MyRectangle(e.getX() - RECTANGLE_WIDTH / 2, e.getY() - RECTANGLE_HEIGHT / 2,
              RECTANGLE_WIDTH, RECTANGLE_HEIGHT,
              new Color((float) Math.random(), (float) Math.random(), (float) Math.random(), (float) Math.random()));
          model.add(rectangle);
        } else if (SwingUtilities.isRightMouseButton(e)) {
          MyRectangle rectangle = model.getRectangleAt(e.getX(), e.getY());
          if (rectangle != null) {
            model.remove(rectangle);
          }
        }
      }
    });
  }
  
  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    for (Drawable rectangle : model.getRectangles()) {
      rectangle.draw(g);
    }
  }
  
  @Override
  public void update(Observable o, Object arg) {
    repaint();
  }
}

final class MyBlackWhiteView extends JComponent implements Observer {
  private static final long serialVersionUID = 4962950651952911221L;
  
  private final MyRectangleModel model;
  
  public MyBlackWhiteView(MyRectangleModel model) {
    this.model = model;
    this.model.addObserver(this);
  }
  
  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    for (MyRectangle rectangle : model.getRectangles()) {
      BlackAndWhiteAdapter.adapt(rectangle).draw(g);
    }
  }
  
  @Override
  public void update(Observable o, Object arg) {
    repaint();
  }
  
}

interface Drawable {
  void draw(Graphics g);
}

final class MyRectangleModel extends Observable {
  private final Collection<MyRectangle> rectangles;
  
  public MyRectangleModel() {
    this.rectangles = new ArrayList<>();
  }
  
  public void add(MyRectangle rectangle) {
    rectangles.add(rectangle);
    setChanged();
    notifyObservers(rectangle);
  }
  
  public void remove(MyRectangle rectangle) {
    rectangles.remove(rectangle);
    setChanged();
    notifyObservers(rectangle);
  }
  
  public Collection<MyRectangle> getRectangles() {
    return new ArrayList<>(rectangles);
  }
  
  public MyRectangle getRectangleAt(int x, int y) {
    for (MyRectangle rectangle : rectangles) {
      if (rectangle.contains(x, y)) {
        return rectangle;
      }
    }
    return null;
  }
  
}

final class MyRectangle implements Drawable {
  private final int x, y;
  private final int width, height;
  private final Color color;
  
  public MyRectangle(int x, int y, int width, int height, Color color) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.color = color;
  }
  
  public int getX() {
    return x;
  }
  
  public int getY() {
    return y;
  }
  
  public int getWidth() {
    return width;
  }
  
  public int getHeight() {
    return height;
  }
  
  public Color getColor() {
    return color;
  }
  
  public boolean contains(int x, int y) {
    return this.x <= x && x <= this.x + this.width && this.y <= y && y <= this.y + this.height;
  }
  
  @Override
  public void draw(Graphics g) {
    Color old = g.getColor();
    g.setColor(color);
    g.fillRect(x, y, width, height);
    g.setColor(old);
  }
  
  @Override
  public int hashCode() {
    return Objects.hash(x, y, width, height, color);
  }
  
  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (getClass() != obj.getClass()) {
      return false;
    }
    MyRectangle other = (MyRectangle) obj;
    if (!Objects.equals(color, other.color)) {
      return false;
    }
    if (height != other.height) {
      return false;
    }
    if (width != other.width) {
      return false;
    }
    if (x != other.x) {
      return false;
    }
    if (y != other.y) {
      return false;
    }
    return true;
  }
  
  @Override
  public String toString() {
    return "MyRectangle [x=" + x + ", y=" + y + ", width=" + width + ", height=" + height + ", color=" + color + "]";
  }
  
}

final class BlackAndWhiteAdapter {
  
  private BlackAndWhiteAdapter() {
  }
  
  public static Drawable adapt(MyRectangle rectangle) {
    return new Drawable() {
      @Override
      public void draw(Graphics g) {
        g.setColor(Color.BLACK);
        g.fillRect(rectangle.getX(), rectangle.getY(), rectangle.getWidth(), rectangle.getHeight());
      }
    };
    /*
     * Könnte auch so aussehen mit Lambdaexpressions:
     * return e -> {
     *   g.setColor(Color.BLACK);
     *   g.fillRect(rectangle.getX(), rectangle.getY(), rectangle.getWidth(), rectangle.getHeight());
     * };
     */
  }
}
 

Neue Themen


Oben