- Вы должны не вызывать
move
из метода paintComponent
!Вы никогда не знаете, когда будет вызван этот метод, и, следовательно, вы не можете должным образом контролировать скорость движения. - Вы должны не вызывать
repaint
из метода paintComponent
! Никогда .Это отправит систему рисования в бесконечный цикл операций перекраски!
По вопросу:
Существует метод рисования произвольных фигур, основанный на double
координатах,Об этом также подробно рассказывается в Учебник по 2D-графике .Ключ должен использовать интерфейс Shape
.Для вашего конкретного примера соответствующая часть кода выглядит так:
private double x = 0;
private double y = 0;
@Override
public void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
double radius = 5;
g.draw(new Ellipse2D.Double(
x - radius, y - radius, radius * 2, radius * 2));
}
То есть вы создаете экземпляр Ellipse2D
, а затем просто рисуете его.
Вот MVCE, показывающий, что вы, вероятно, пытаетесь выполнить:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PaintWithDouble
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PaintWithDoublePanel p = new PaintWithDoublePanel();
f.getContentPane().add(p);
startMoveThread(p);
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static void startMoveThread(PaintWithDoublePanel p)
{
Thread t = new Thread(() -> {
while (true)
{
p.move();
p.repaint();
try
{
Thread.sleep(20);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
return;
}
}
});
t.setDaemon(true);
t.start();
}
}
class PaintWithDoublePanel extends JPanel
{
private double x = 0;
private double y = 0;
@Override
public void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
double radius = 5;
g.draw(new Ellipse2D.Double(
x - radius, y - radius, radius * 2, radius * 2));
g.drawString("At " + x + ", " + y, 10, 30);
}
public void move()
{
x += 0.05;
y += 0.05;
}
}
Отредактировано в ответ на комментарий (и прояснить некоторые вещи, которые были сказаны в других ответах):
Хотя технически правильно говорить, что «есть только целые пиксели» и «нет пикселя с координатами (0,3, 1,8)», это не означает, что дробные координаты не влияют на окончательный вид визуализированного вывода.Каждая тема становится наукой, когда вы изучаете ее достаточно долго.В частности, много исследований было посвящено вопросу о том, как улучшить визуальный внешний вид визуализируемой продукции, выходя за рамки того, что вы можете достичь с помощью тривиального Брезенхема или около того.Отправной точкой для дальнейших исследований могла бы стать статья о субпиксельном рендеринге .
Во многих случаях, как обычно, существуют компромиссы между внешним видом и производительностью рисования.Что касается Java и его возможностей 2D-рисования, эти компромиссы в основном контролируются с помощью класса RenderingHints
.Например, есть RenderingHints#VALUE_STROKE_PURE
, который включает субпиксельный рендеринг.Эффект показан на следующем снимке экрана:
Ползунок используется для изменения смещения по оси y самой правой точки горизонтальной линии с помощью -От 3 до +3 пикселей.В левом верхнем углу вы видите линию, представленную как есть.Посередине вы видите линию, увеличенную в 8 раз, чтобы лучше показать эффект: пиксели заполнены различными непрозрачностями, в зависимости от , сколько пикселей покрыто идеализированной линией шириной 1 пиксель .
Хотя, безусловно, это не относится к большинству случаев применения, здесь стоит отметить это.
Ниже приведен MCVE, который использовался для захвата экрана:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
public class PaintWithDoubleMagnified
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
PaintWithDoubleMagnifiedPanel p = new PaintWithDoubleMagnifiedPanel();
f.getContentPane().add(p, BorderLayout.CENTER);
JSlider slider = new JSlider(0, 100, 50);
slider.addChangeListener(e -> {
int value = slider.getValue();
double relative = -0.5 + value / 100.0;
p.setY(relative * 6);
});
f.getContentPane().add(slider, BorderLayout.SOUTH);
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class PaintWithDoubleMagnifiedPanel extends JPanel
{
private double y = 0;
@Override
public void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
g.drawString("At " + y, 10, 20);
paintLine(g);
BufferedImage image = paintIntoImage();
g.setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
g.scale(8.0, 8.0);
g.drawImage(image, 0, 0, null);
}
public void setY(double y)
{
this.y = y;
repaint();
}
private void paintLine(Graphics2D g)
{
g.setColor(Color.BLACK);
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(
RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE);
Line2D line = new Line2D.Double(
10, 30, 50, 30 + y);
g.draw(line);
}
private BufferedImage paintIntoImage()
{
BufferedImage image = new BufferedImage(
100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
paintLine(g);
g.dispose();
return image;
}
}