Zeichnen

Hallo.
Ich bastel ja gerade an einem kleinen zeichen programm, aber irgendwie bugt der zeichnenalgorithmus manchmal … ein bisschen.
Der sieht so aus:

code
[spoiler]

double m = 0;
int width = draggedX - mouseX;
int height = draggedY - mouseY;
if(draggedX - mouseX != 0){
	m = (double) (draggedY - mouseY) / (double) (draggedX - mouseX);
}
if(width > 0){
	for(int i = 0; i < width; i++) {
	    double x = mouseX + i;
	    double y = mouseY + (m * i);
	    g.fillOval((int) x, (int) y, 5, 5);
	}
}
else if(width < 0){
	for(int i = -width; i > 0; i--) {
	    double x = mouseX + i;
	    double y = mouseY + (m * i);
	    g.fillOval((int) x, (int) y, 5, 5);
	}
}
else{
	if(height > 0){
		for(int i = 0; i < height; i++){
			g.fillOval(mouseX, (int) i + mouseY, 5, 5);
		}
	}
	else if(height < 0){
		for(int i = -height; i > 0; i--){
			g.fillOval(mouseX, (int) i + mouseY, 5, 5);
		}
	}
}

[/spoiler]

Ich kanns nicht genau beschreiben, aber naja, es werden manche stellen einfach übersprungen und manchmal wird irgendwo gezeichnet wo es nicht sein sollte.
einer eine idee?

*** edit ***

Wers ausprobieren will, kompletter code in einer klasse:

code
[spoiler]

package de.skysoldier.paint;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;

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

class Gui {
	
	JFrame frame;
	DrawPane drawPane;
	JPanel menuCont;
	JPanel layerBarCont;
	JPanel toolsCont;
	JPanel infoCont;
	
	public Gui(DrawPane drawPane){
		build(drawPane);
	}
	
	public void build(DrawPane drawPane){
		frame = new JFrame();
		frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLayout(new BorderLayout());
		drawPane = new DrawPane();
		this.drawPane = drawPane;
		this.menuCont = buildMenu();
		this.layerBarCont = buildLayerBar();
		this.toolsCont = buildTools();
		this.infoCont = buildInfo();
		frame.add(menuCont, BorderLayout.NORTH);
		frame.add(layerBarCont, BorderLayout.WEST);
		frame.add(toolsCont, BorderLayout.EAST);
		frame.add(infoCont, BorderLayout.SOUTH);
		frame.add(drawPane, BorderLayout.CENTER);
		frame.setVisible(true);
		drawPane.init();
	}
	
	private JPanel buildMenu(){
		menuCont = new JPanel();
		menuCont.setBackground(Color.BLACK);
		menuCont.setPreferredSize(new Dimension(0, 30));
		return menuCont;
	}
	
	private JPanel buildLayerBar(){
		layerBarCont = new JPanel();
		layerBarCont.setBackground(Color.GRAY);
		layerBarCont.setPreferredSize(new Dimension(150, 0));
		return layerBarCont;
	}
	
	private JPanel buildTools(){
		toolsCont = new JPanel();
		toolsCont.setBackground(Color.GRAY);
		toolsCont.setPreferredSize(new Dimension(150, 0));
		return toolsCont;
	}
	
	private JPanel buildInfo(){
		infoCont = new JPanel();
		infoCont.setBackground(Color.BLACK);
		infoCont.setPreferredSize(new Dimension(0, 20));
		return infoCont;
	}
}
class DrawPane extends JPanel {

	private boolean loaded;
	private int draggedX;
	private int draggedY;
	private int mouseX;
	private int mouseY;
	private States state;
	private BufferedImage contentImg;
	
	public DrawPane(){
	}
	
	public void paintComponent(Graphics g){
		super.paintComponent(g);
		g.setColor(Color.LIGHT_GRAY);
		g.fillRect(0, 0, getWidth(), getHeight());
		g.drawImage(contentImg, 0, 0, this);
	}
	
	private MouseMotionListener mouseListener = new MouseMotionListener(){
		public void mouseDragged(MouseEvent e) {
			if(loaded){
				draggedX = e.getX();
				draggedY = e.getY();
				if(state == States.PEN){
					Graphics2D g = (Graphics2D) contentImg.getGraphics();
					g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
					g.setColor(Color.BLACK);
					double m = 0;
					int width = draggedX - mouseX;
					int height = draggedY - mouseY;
					if(draggedX - mouseX != 0){
						m = (double) (draggedY - mouseY) / (double) (draggedX - mouseX);
					}
					if(width > 0){
						for(int i = 0; i < width; i++) {
						    double x = mouseX + i;
						    double y = mouseY + (m * i);
						    g.fillOval((int) x, (int) y, 5, 5);
						}
					}
					else if(width < 0){
						for(int i = -width; i > 0; i--) {
						    double x = mouseX + i;
						    double y = mouseY + (m * i);
						    g.fillOval((int) x, (int) y, 5, 5);
						}
					}
					else{
						if(height > 0){
							for(int i = 0; i < height; i++){
								g.fillOval(mouseX, (int) i + mouseY, 5, 5);
							}
						}
						else if(height < 0){
							for(int i = -height; i > 0; i--){
								g.fillOval(mouseX, (int) i + mouseY, 5, 5);
							}
						}
						else{
							g.fillOval(mouseX, mouseY, 5, 5);
						}
					}
					repaint();
					mouseMoved(e);
				}
		
			}
		}
		public void mouseMoved(MouseEvent e) {
			mouseX = e.getX();
			mouseY = e.getY();
		}
	};
	
	public void init(){
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				create();
			}
		});
	}
	
	public void create(){
		state = States.PEN;
		contentImg = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
		loaded = true;
		addMouseMotionListener(mouseListener);
	}
	
	public enum States {
		PEN
	}
}
class Paint {

	DrawPane drawPane;
	
	public Paint(){
		new Gui(drawPane);
	}
}
public class Hole {
	public static void main(String[] args) {
		new Paint();
	}
}

[/spoiler]

Das liegt daran dass die Maus sich eben schnell bewegen kann und dein Listener nicht mehr mitkommt. Einfacher gehts, wenn du die Points von deinem MouseEvent einfach einem Path2D Shape hinzufügst und das dann zeichnest. Dann werden die Punkte automatisch verbunden und du kannst auch noch mit einem Stroke diverse Strichmuster und Farben einstellen. Kannst dir mal anschaun wie ich das bei meinem Paint gemacht habe. Ist aber nicht der schönste code (hab es stückweise erweitert und es ist Spaghetticode draus geworden)

ähm… deswegen zeichne ich ja auch linien von mouseXY to dragXY. das hat dann nix mehr mit dem listener zu tun

Hab’s nicht im Detail gelesen, aber vielleicht interessiert dich http://wiki.byte-welt.net/wiki/Malen_in_Swing_Teil_2:_ein_einfaches_Malprogramm (ggf. auch Teil 1)

Du machst Dir aber auch das Leben schwer. Zeichen doch echte Linien statt vieler Kreise.

Was die Rechts-nach-links-Schwäche angeht:

                            for (int i = -width; i > 0; i--) {
                                double x = mouseX - i;
                                double y = mouseY - (m * i);
                                g.fillOval((int) x, (int) y, 5, 5);
                            }```
statt
```                        } else if (width < 0) {
                            for (int i = -width; i > 0; i--) {
                                double x = mouseX + i;
                                double y = mouseY + (m * i);
                                g.fillOval((int) x, (int) y, 5, 5);
                            }```

Für die Vertikale fehlt mir leider die Zeit.

ou… danke ^^ da kommt man schonmal durcheinander :stuck_out_tongue: aber wenn ich viele linien zeichne… wie soll ich das dann „abrunden“ o.O

Machs mit einem Path2D der übernimmt dir das alles :D. Solang die Maus gedrückt ist, fügst du die Punkte dem Path Objekt hinzu und bei released malst du es auf dein Bild. Schau was man da für tolle Sachen damit machen kann: http://www.jhlabs.com/java/java2d/strokes/

ne sry, will ich nicht ^^
will es möglichst „allein“ machen :slight_smile:

Abrunden? Meinst Du die Linienenden bzw. -verbindungen?

((Graphics2D)g).setStroke(stroke);

ich mein wenn ich 5 lines neben einander mal, dann sollen die beiden enden abgerundet sein.
Es geht jetzt eigentlich, nur links und rechts klappt nicht… (links: geht, rechts: nein)

[SPOILER]

double m = 0;
int width = draggedX - mouseX;
int height = draggedY - mouseY;
if(draggedX - mouseX != 0){
	m = (double) (draggedY - mouseY) / (double) (draggedX - mouseX);
}
if(width > 0){
	for(int i = 0; i < width; i++) {
	    double x = mouseX + i;
	    double y = mouseY + (m * i);
	    g.fillOval((int) x, (int) y, 5, 5);
	}
}
else if(width < 0){
	for(int i = -width; i > 0; i--) {
	    double x = mouseX - i;
	    double y = mouseY - (m * i);
	    g.fillOval((int) x, (int) y, 5, 5);
	}
}
else{
	if(height > 0){
		for(int i = 0; i < height; i++){
			g.fillOval(mouseX, (int) i + mouseY, 5, 5);
		}
	}
	else if(height < 0){
		for(int i = -height; i > 0; i--){
			g.fillOval(mouseX, (int) i - mouseY, 5, 5);
		}
	}
	else{
		g.fillOval(mouseX, mouseY, 5, 5);
	}
}

[/SPOILER]

will es möglichst „allein“ machen
In dem Fall kann ich dir zu Assembler raten, aber nicht schummeln und Interrupts benutzen.

Im ernst, deine if/else Schlange springt dir nach width >0 oder width<0 raus und die height zweige werden nicht gecheckt. Deine Klammern und Einrückung sind da etwas schlecht gemacht.

hahaok ich verbesser mich: ich will es möglichst allein mithilfe der basis java pakete machen :wink:
was sind interrupts? ^^

hmmm
du meinst also height wird gar nicht gecheckt? Das prüf ich mal

*** Edit ***

doch der height zweig wird jedesmal ausgeführt. da scheint was anderes nicht zu stimmen.
Was gefällt dir an meinen einrückungen und klammern nicht? :slight_smile:

hahaok ich verbesser mich: ich will es möglichst allein mithilfe der basis java pakete machen

Das was bert0r dir gelinkt hat, gehört zur Standard-Java-Bibliothek. ^^

[QUOTE=mymaksimus]ich mein wenn ich 5 lines neben einander mal, dann sollen die beiden enden abgerundet sein.
Es geht jetzt eigentlich, nur links und rechts klappt nicht… (links: geht, rechts: nein)
[/QUOTE]

Nicht mehrere Linien nebeneinander, sondern eine breite Linie. Wie man das Ende abrundet steht oben (BasicStroke.CAP_ROUND).

Falls es doch ovals sein sollen: :slight_smile:

Ein Problem in den width-Blöcken ist, dass sich die Schleife an der x-Differenz orientiert. Wenn die y-Differenz aber betragsmäßig größer ist, gibt es Lücken. Orientiere Dich an der größeren Differenz. Aus dieser Differenz ermittelst Du nicht ein m, sondern ein mX und ein mY. Anschließend reicht eine Schleife für die ovals. Keine weiteren ifs nötig.

wie berechne ich dann mY ?

bzw unterschied mx my?

mX und mY sind vielleicht blöde Bezeichung, die ich da gewählt habe, aber ich bleib jetzt mal dabei:

diffRelevant = Maximum von (Betrag von (width); Betrag von (height))
mX = width / diffRelevant
mY = height / diffRelevant

Deine Ifs sehen so aus:

else if(width < 0){}
else if(height > 0){}
else if(height < 0){}
else{}

=> zu den height abfragen kommst du nur wenn width == 0 ist. Falls das so gewünscht ist mein Fehler, kommt mir aber komisch vor.

Naja anscheinend ist das schwachsinn. Ich versuchs mal so wie di gesagt hast.
Und wenn width == height, ist es doch egal dann geht soch beides, oder?

*** Edit ***

Also, ich hab jetzt das hier (siehe unten ) - danke dir papoy.
Aber eins geht immer noch nicht:
wenn ich GENAU nach links gehe (mit robot getestet) oder eben GENAU nach oben.
Irgendwie muss ich ja jetzt den fall 0 behandeln. Wie mach ich das am besten?

int width = draggedX - mouseX;
int height = draggedY - mouseY;
double relevantDistance = width > height ? width:height;
double mX = width / relevantDistance;
double mY = height / relevantDistance;
relevantDistance = relevantDistance > 0 ? relevantDistance:-relevantDistance;
int plus = relevantDistance > 0 ? 1:-1;
for(int i = 0; i < relevantDistance; i += plus){
	g.fillOval((int) (mX * i) + mouseX, (int) (mY * i) + mouseY, 20, 20);
}		

Wirf die Zeilen 7 und 8 weg und nimm für relevantDistance in Zeile 4 das Maximum der Beträge, nicht einfach das Maximum.

Getestet habe ich es, indem ich das Zeichnen der “Linie” in eine Methode ausgelagert habe, der ich Start- und Endpunkt übergebe.

Hab ich ja auch, aber ich wollt es mit der maus simulieren :wink: was meinst du mit maximum der betraege?