Etwas dickere Linie zeichnen

Hallo nochmal,
ich möchte eine etwas dickere Linie zeichnen (ca. 10pixel), die durch eine Strecke mit zwei 2D-Punkten gegeben ist, dazu möchte ich ein Rechteck füllen. Dazu möchte ich die „Winkelsteigung“ berechnen und zu dieser 90grad addieren:

	private Canvas canvas = new Canvas() {

		private static final long serialVersionUID = 1L;

		private double produkt(int x0, int y0, int x1, int y1) {
			return x0 * y0 + x1 * y1;
		}

		private double betrag(int x0, int y0) {
			return Math.sqrt(x0 * y0 + x0 * y0);
		}

		private void small_line(Graphics2D g2d, int x0, int y0, int x1, int y1) {
			double a = Math.acos(produkt(x0, y0, x1, y1) / (betrag(x0, y0) * betrag(x1, y1)));
			System.out.println(a);
		}

		@Override
		public void paint(Graphics g) {
			super.paint(g);
			Graphics2D g2d = (Graphics2D) g;
			int w = this.getWidth();
			int h = this.getHeight();
			int wh = Math.min(w, h);
			small_line(g2d, 0, 0, 1, 1);
			small_line(g2d, 0, 0, 1, 1000);
		}

	};

Leider ist die „Winkelsteigung“ momentan NaN. Bitte um Hilfe.

g2d.setStroke(new BasicStroke(dick...)); ist keine Option?

Moin,

möchte das selber über ein Rechteck zeichnen…

Hier ist die Zeichnung dazu:
unknown

Die Punkte A und B sind gegeben, der Kreis hat den Radius 1 und ich suche den Winkel der Geraden AD und AC.

		private void small_line(Graphics2D g2d, int x0, int y0, int x1, int y1) {
			double x2 = 0;
			double y2 = 1;
			double x3 = (x1 - x0) / betrag(x1 - x0, y1 - y0);
			double y3 = (y1 - y0) / betrag(x1 - x0, y1 - y0);
			double a = Math.toDegrees(Math.acos(produkt(x2, y2, x3, y3) / (betrag(x2, y2) * betrag(x3, y3))));
			System.out.println(x3 + ", " + y3);
			System.out.println(a);
		}

		@Override
		public void paint(Graphics g) {
			super.paint(g);
			Graphics2D g2d = (Graphics2D) g;
			int w = this.getWidth();
			int h = this.getHeight();
			int wh = Math.min(w, h);
			small_line(g2d, 0, 0, 50, 50);
			small_line(g2d, 0, 0, 1, 500);
		}

Hier sollte der Winkel also 45 Grad und etwas weniger als 1 Grad sein… (ist er aber nicht)

In dem Bild sehe ich nichts, was mit „45°“ oder „1°“ zu tun haben könnte. Wäre https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#atan2-double-double- hilfreich?

@Marco13 Mit g2d.setStroke(new BasicStroke(...)); ist es doch wesentlich angenehmer zu hantieren, als selber die Orthogonalen zur Geraden/Strecke zu finden und ein Rechteck zu füllen…

Oder anders: Ich hab den Ansatz wieder verworfen. Ich bin zu blöd. :frowning_face:

Ach, ich bin doch nicht so „blöd“… Man muss nur etwas „systematischer“ an die Sache herangehen. Was zeichnet orthogonale Geraden aus? Genau, deren Steigungen sind multipliziert -1:


	public static void main(String[] args) {
		JFrame jf = new JFrame();
		Canvas c = new Canvas() {
			private static final long serialVersionUID = 1L;
			@Override
			public void paint(Graphics g) {
				Strecke s1 = new Strecke(new int[] { 50, 50 }, new int[] { 100, 150 });
				Strecke s2 = s1.orthogonale_a(20);
				Strecke s3 = s1.orthogonale_a(-40);
				Graphics2D g2d = (Graphics2D) g;
				s1.zeichne(g2d);
				s2.zeichne(g2d);
				s3.zeichne(g2d);
			}
		};
		jf.add(c);
		jf.setSize(400, 400);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setVisible(true);
	}
}

class Strecke {
	int[] vektor_anker;
	int[] vektor_richtung;

	Strecke(int[] vektor_anker, int[] vektor_richtung) {
		this.vektor_anker = vektor_anker;
		this.vektor_richtung = vektor_richtung;
	}

	Strecke orthogonale_a(int distanz) {
		double m1 = (double) vektor_richtung[1] / (double) vektor_richtung[0];
		double m2 = -1 / m1;
		double len = Math.sqrt(1 + m2 * m2);
		double[] richtung = { 1.0 / len * distanz, m2 / len * distanz };
		return new Strecke(vektor_anker, new int[] { (int) Math.round(richtung[0]), (int) Math.round(richtung[1]) });
	}

	void zeichne(Graphics2D g2d) {
		int bx = vektor_anker[0] + vektor_richtung[0];
		int by = vektor_anker[1] + vektor_richtung[1];
		g2d.drawLine(vektor_anker[0], vektor_anker[1], bx, by);
	}
}

Ergebnis:
https://ibb.co/N1WdNm3

Wenn es nur darum geht, einen Vektor mit Richtung (x,y) um 90° zu drehen, dann nimmt man (-y,x). Was auch immer du da nun in orthogonale_a machst.

(Manchmal hat man einen Knoten im Kopf und „versickert“ so in etwas, dass man eine einfache Lösung übersieht. Aber ich nehme bei so ziemlich allem, was du postest, an, dass da auch ein bißchen Verarschung dabei ist. (Und wenn du das nicht magst, nur der Hinweis: Ich könnte meine Beobachtungen auch anders erklären. Und normalerweise berücksichtige ich Hanlons Razor. Aber bei dir … naja…))

1 Like

Hmpf. Ja das wäre noch einfacher, so es denn immer funktionierte.

Was ich da mache? m1 ist die Steigung der Geraden/Strecke. m2 soll die neue Steigung sein. Wie gesagt, für zwei Strecken gilt: m1*m2=-1 <=> m2=-1/m1. Wenn ich die neue Steigung berechnet habe, dann wäre der Richtungsvektor: (1,m2). Daraus berechne ich den Einheitsvektor (Vektor der Länge 1). Den multipliziere ich mit der Distanz und gebe damit dann die neue Gerade/Strecke zurück.

Ist das zu „verknotet“ gedacht? So lernte ich es mal.

Das soll niemanden veräppeln.

Wie hättest du die neue Gerade denn um… zum Beispiel 37,5° gedreht?

Das hatte ich befürchtet, ja. Es ist ungeheuer frustrierend, und schwierig, sich zurückzuhalten. Ich weiß ja, wie man sich verhalten sollte. Wenn einem ein 4-jähriges Kind ein Bild gibt, und sagt: „Guck mal, ich habe ein Bild gemalt“, dann darf man nicht fragen: „Was soll’n das sein?“, oder es kommentarlos in den Kamin werfen. Aber das ist ungeheuer anstrengend.

Vergib vernünftige Namen, verdammt nochmal!!!
Statt double[2] oder int[2] (entscheide dich mal!) eher Point2D oder Point
Drehen um +/-90°? Je nachdem, einfach newX=-y, newY=x
Drehen allgemein? AffineTransform

Ich schreibe das hier, wissend, dass ich meine Zeit verschwende.

4 Likes

Hm, ja ich könnte dieses int[] weglassen, und es noch mal schreiben, aber ich weiß nicht, ob dann der Blutdruck nicht zu sehr hochschnellen würde. :smile:

Ich hab hier auch noch ein paar Bilderberge… :roll_eyes: Willst eins abhaben? :smile:

Was wird denn jetzt von mir erwartet?

Und was soll
l
das?? -----------------^

Wenn dieses Thema wirklich so „unerträglich“ ist, dann löscht es doch wieder…

Hallo

Ich will eine „dickere Linie“ zeichnen. Eigentlich ein Rechteck, aber eben entlang einer Linie ausgerichtet. Hier ist mal skizziert, was ich meine (mit MS Paint, sorry, ich hoffe, es ist erkennbar):

Linie

Die Punkte A und B sind gegeben. Ich brauche die Punkte A0, A1 und B0, B1, für eine gegebene Liniendicke, damit ich das Rechteck bestehend aus diesen 4 Punkten zeichnen kann.

Wie kann ich die gesuchten Punkte aus den gegebenen Punkten ausrechnen?

Hier ist mal das Gerüst, das ich bisher habe:

package bytewelt;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;

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

public class ThisLineThingy
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel p = new JPanel()
        {
            @Override
            protected void paintComponent(Graphics gr)
            {
                Graphics2D g = (Graphics2D) gr;
                Point p0 = new Point(50, 270);
                Point p1 = new Point(300, 90);
                g.setColor(Color.BLACK);
                paintThatLine(g, p0, p1);
            };
        };
        f.getContentPane().add(p);
        f.setSize(400, 400);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    protected static void paintThatLine(Graphics2D g, Point p0, Point p1)
    {
        g.drawLine(p0.x, p0.y, p1.x, p1.y);
    }
}

Eigentlich nicht so schwer. Aber offenbar zu viel verlangt.

1 Like

Tada! Besser?:

		private void darawLine(Graphics2D g2d, Point a, Point b, int thickness) {
			Point2D.Double c = new Point2D.Double(-(b.y - a.y), (b.x - a.x));
			double len = Math.sqrt(c.x * c.x + c.y * c.y);
			c.x /= len;
			c.y /= len;
			Point a0 = new Point((int) (a.x + c.x * thickness), (int) (a.y + c.y * thickness));
//			g2d.drawString("0", a0.x, a0.y);
			Point a1 = new Point((int) (b.x + c.x * thickness), (int) (b.y + c.y * thickness));
//			g2d.drawString("1", a1.x, a1.y);
			Point a2 = new Point((int) (b.x + c.x * -thickness), (int) (b.y + c.y * -thickness));
//			g2d.drawString("2", a2.x, a2.y);
			Point a3 = new Point((int) (a.x + c.x * -thickness), (int) (a.y + c.y * -thickness));
//			g2d.drawString("3", a3.x, a3.y);
			g2d.fillPolygon(new int[] { a0.x, a1.x, a2.x, a3.x }, new int[] { a0.y, a1.y, a2.y, a3.y }, 4);
		}

Und hier wäre die eine komplette Anwendung dazu:

import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Timer;
import java.util.TimerTask;

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

public class Uhr extends JFrame {
	private static final long serialVersionUID = 1L;

	private Canvas canvas = new Canvas() {
		private static final long serialVersionUID = 1L;

		private void darawLine(Graphics2D g2d, Point a, Point b, int thickness) {
			Point2D.Double c = new Point2D.Double(-(b.y - a.y), (b.x - a.x));
			double len = Math.sqrt(c.x * c.x + c.y * c.y);
			c.x /= len;
			c.y /= len;
			Point a0 = new Point((int) (a.x + c.x * thickness), (int) (a.y + c.y * thickness));
//			g2d.drawString("0", a0.x, a0.y);
			Point a1 = new Point((int) (b.x + c.x * thickness), (int) (b.y + c.y * thickness));
//			g2d.drawString("1", a1.x, a1.y);
			Point a2 = new Point((int) (b.x + c.x * -thickness), (int) (b.y + c.y * -thickness));
//			g2d.drawString("2", a2.x, a2.y);
			Point a3 = new Point((int) (a.x + c.x * -thickness), (int) (a.y + c.y * -thickness));
//			g2d.drawString("3", a3.x, a3.y);
			g2d.fillPolygon(new int[] { a0.x, a1.x, a2.x, a3.x }, new int[] { a0.y, a1.y, a2.y, a3.y }, 4);
		}

		@Override
		public void paint(Graphics g) {
			Graphics2D g2d = (Graphics2D) g;
			int w = this.getWidth();
			int h = this.getHeight();
			int wh = Math.min(w, h);
			int wh2 = wh / 2;
			g2d.drawOval(5, 5, wh - 10, wh - 10);
			for (int i = 0; i < 12; i++) {
				int x0 = (int) (wh2 + (wh2 - 20) * Math.cos(Math.toRadians(360 / 12 * i)));
				int y0 = (int) (wh2 + (wh2 - 20) * Math.sin(Math.toRadians(360 / 12 * i)));
				int x1 = (int) (wh2 + (wh2 - 10) * Math.cos(Math.toRadians(360 / 12 * i)));
				int y1 = (int) (wh2 + (wh2 - 10) * Math.sin(Math.toRadians(360 / 12 * i)));
				darawLine(g2d, new Point(x0, y0), new Point(x1, y1), 5);
			}
			{
				// Calibrate
				Calendar calendar = GregorianCalendar.getInstance();
				float minute = calendar.get(Calendar.MINUTE);
				float hour = calendar.get(Calendar.HOUR) + (minute / 60f); // The hour pointer should tick every minute.
				minute += calendar.get(Calendar.SECOND) / 60f; // The minute pointer should tick every second.

				// Draw, wh2 is the center
				int x0 = (int) (wh2 + (wh2 - 20) * Math.cos(Math.toRadians(360 / 60 * minute - 90)));
				int y0 = (int) (wh2 + (wh2 - 20) * Math.sin(Math.toRadians(360 / 60 * minute - 90)));
				int x1 = (int) (wh2 + (wh2 - 40) * Math.cos(Math.toRadians(360 / 12 * hour - 90)));
				int y1 = (int) (wh2 + (wh2 - 40) * Math.sin(Math.toRadians(360 / 12 * hour - 90)));
				darawLine(g2d, new Point(wh2, wh2), new Point(x0, y0), 5);
				darawLine(g2d, new Point(wh2, wh2), new Point(x1, y1), 10);
			}
		}
	};

	public Uhr() {
		add(canvas);
		setSize(400, 400);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setVisible(true);

		// Repaint every second.
		new Timer().schedule(new TimerTask() {
			@Override
			public void run() {
				SwingUtilities.invokeLater(new Runnable() {
					@Override
					public void run() {
						canvas.repaint();
					}
				});
			}
		}, 1000, 1000);
	}

	public static void main(String[] args) {
		new Uhr();
	}
}

(Flackert die Analoguhr bei euch auch manchmal? )

Hallo Marco,

Eine interessante Frage. Eigentlich sollte man die Grundlagen dafür in … hm… spätestens der 10. Klasse lernen, aber … das ganze dann in Code zu gießen kann schwierig sein. Es gibt einige Freiheitsgrade bei der Implementierung, aber hier ist mal ein Beispiel, wie man das machen könnte:

package bytewelt;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;

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

class ThisLineThingyPanel extends JPanel 
    implements MouseListener, MouseMotionListener
{
    private Point p0 = new Point(50, 270);
    private Point p1 = new Point(300, 90);
    private Point2D draggedPoint;
    
    public ThisLineThingyPanel()
    {
        addMouseListener(this);
        addMouseMotionListener(this);
    }
    
    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        gr.setColor(Color.WHITE);
        gr.fillRect(0,0,getWidth(),getHeight());
        Graphics2D g = (Graphics2D) gr;
        g.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING, 
            RenderingHints.VALUE_ANTIALIAS_ON);
        paintThatLine(g, p0, p1);
    };
    
    protected static void paintThatLine(Graphics2D g, Point2D p0, Point2D p1)
    {
        // Die einfache Line zeichnen
        g.setColor(Color.BLACK);
        g.draw(new Line2D.Double(p0, p1));
        
        // Richtung der Line ausrechnen
        double deltaX = p1.getX() - p0.getX();
        double deltaY = p1.getY() - p0.getY();
        double distance = Math.hypot(deltaX, deltaY);
        double dirX = deltaX / distance;
        double dirY = deltaY / distance;
        
        double thickness = 25.0;
        
        // Die Veschiebung für die Eckpunte des Rechtecks ausrechnen
        double offsetX = - dirY * thickness;
        double offsetY = dirX * thickness;
        
        // Die Eckpunkte erstellen und die Seitenlinien zeichnen
        // (Das wäre nicht notwendig, man könnte direkt die 
        // Koordinaten unten in den Path2D packen...)
        Point2D a0 = new Point2D.Double(
            p0.getX() + offsetX,
            p0.getY() + offsetY); 
        
        Point2D a1 = new Point2D.Double(
            p0.getX() - offsetX,
            p0.getY() - offsetY); 

        Point2D b0 = new Point2D.Double(
            p1.getX() + offsetX,
            p1.getY() + offsetY); 

        Point2D b1 = new Point2D.Double(
            p1.getX() - offsetX,
            p1.getY() - offsetY); 

        g.setColor(Color.GRAY);
        g.draw(new Line2D.Double(a0, a1));
        g.draw(new Line2D.Double(b0, b1));
        
        // Erstelle das Rechteck
        Path2D path = new Path2D.Double();
        path.moveTo(a0.getX(), a0.getY());
        path.lineTo(a1.getX(), a1.getY());
        path.lineTo(b1.getX(), b1.getY());
        path.lineTo(b0.getX(), b0.getY());
        path.closePath();
        
        
        g.setColor(new Color(0,128,0,32));
        g.fill(path);
    }
    
    @Override
    public void mouseDragged(MouseEvent e)
    {
        if (draggedPoint != null)
        {
            draggedPoint.setLocation(e.getX(), e.getY());
            repaint();
        }
    }

    @Override
    public void mouseMoved(MouseEvent e)
    {
        // Nothing to do here
    }


    @Override
    public void mouseClicked(MouseEvent e)
    {
        // Nothing to do here
    }


    @Override
    public void mousePressed(MouseEvent e)
    {
        draggedPoint = null;
        
        double thresholdSquared = 10 * 10;
        Point2D p = e.getPoint();
        if (p.distanceSq(p0) < thresholdSquared)
        {
            draggedPoint = p0;
        }
        if (p.distanceSq(p1) < thresholdSquared)
        {
            draggedPoint = p1;
        }
    }

    @Override
    public void mouseReleased(MouseEvent e)
    {
        draggedPoint = null;
    }


    @Override
    public void mouseEntered(MouseEvent e)
    {
        // Nothing to do here
    }


    @Override
    public void mouseExited(MouseEvent e)
    {
        // Nothing to do here
    }
    
}
 

public class ThisLineThingy
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel p = new ThisLineThingyPanel();
        f.getContentPane().add(p);
        f.setSize(400, 400);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

Das wichtigste ist in der paintThatLine-Methode kurz beschrieben. Die sonstigen Erweiterungen sind nur, damit man die Endpunkte der Linie mit der Maus rumziehen kann, um zu sehen, wie es aussieht:

LineThingy

double distance = Math.hypot(...); :face_with_raised_eyebrow:

double x3 = (x1 - x0) / betrag(x1 - x0, y1 - y0); :face_with_raised_eyebrow: