我一直在尝试使用Graphics2D和PaintComponent在Java中创建一个程序。但是,Java并没有正确地舍入一些值,导致一些点被渲染成一个像素所不同的像素,这会给出一个不洁的图像。你可以在下面的图片中看到我的意思。
这是我的代码。我可以改变什么来解决这个问题?谢谢!
public void paintComponent( Graphics g )
{
super.paintComponent( g );
g.setColor(Color.red);
int orginX = getWidth()/2;
int orginY = getHeight()/2;
for(int i=0; i<=360; i+= 10)
{
double angle = Math.toRadians(i);
double centerX = radius * Math.cos(angle) + orginX;
double centerY = radius * Math.sin(angle) + orginY;
int[] anglePointsX = {(int) (radius * Math.cos(angle+Math.toRadians(60)) + centerX), (int) (radius * Math.cos(angle-Math.toRadians(60)) + centerX), orginX};
int[] anglePointsY = {(int) (radius * Math.sin(angle+Math.toRadians(60)) + centerY), (int) (radius * Math.sin(angle-Math.toRadians(60)) + centerY), orginY};
g.drawPolygon(anglePointsX, anglePointsY, 3);
}
答案 0 :(得分:2)
尝试使用i < 360
代替i<=360
,因为您已经在这个角度画了一条线(0 == 360)
尝试使用Path2D
或GeneralPath
而不是drawPolygon
,因为这会为您提供float
和double
精确度
有关详细信息,请查看Drawing Arbitrary Shapes
<强>更新... 强>
所以我将i <= 360
更改为i < 360
并且没有看到任何立即更改,但是,然后应用了一些渲染提示,它似乎改善了问题。
我首先将元素数量减少到6(这是我开始看到问题的地方)
int dif = (int) (360 / 6d);
for (int i = 0; i < 360; i += dif) {
然后我应用了一些渲染提示......
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.red);
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
int orginX = getWidth() / 2;
int orginY = getHeight() / 2;
int dif = (int) (360 / 6d);
for (int i = 0; i < 360; i += dif) {
double angle = Math.toRadians(i);
double centerX = radius * Math.cos(angle) + orginX;
double centerY = radius * Math.sin(angle) + orginY;
int[] anglePointsX = {(int) (radius * Math.cos(angle + Math.toRadians(60)) + centerX), (int) (radius * Math.cos(angle - Math.toRadians(60)) + centerX), orginX};
int[] anglePointsY = {(int) (radius * Math.sin(angle + Math.toRadians(60)) + centerY), (int) (radius * Math.sin(angle - Math.toRadians(60)) + centerY), orginY};
g2d.drawPolygon(anglePointsX, anglePointsY, 3);
}
g2d.dispose();
}
比较之前和之后......
然后我使用drawPolygon
更改了图纸以使用Shape
API,特别是Path2D
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.red);
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
int orginX = getWidth() / 2;
int orginY = getHeight() / 2;
int dif = (int) (360 / 6d);
for (int i = 0; i < 360; i += dif) {
double angle = Math.toRadians(i);
double centerX = radius * Math.cos(angle) + orginX;
double centerY = radius * Math.sin(angle) + orginY;
Path2D path = new Path2D.Double();
path.moveTo(radius * Math.cos(angle + Math.toRadians(60)) + centerX, radius * Math.sin(angle + Math.toRadians(60)) + centerY);
path.lineTo((radius * Math.cos(angle - Math.toRadians(60)) + centerX), (radius * Math.sin(angle - Math.toRadians(60)) + centerY));
path.lineTo(orginX, orginY);
g2d.draw(path);
}
g2d.dispose();
}
有和没有渲染提示
尽可能地,即使没有提示提示,问题仍然存在
答案 1 :(得分:2)
我建议你可以画两条不同的线:
g.drawPolygon(anglePointsX, anglePointsY, 2); // the outter line.
g.drawLine(orginX, orginY, anglePointsX[0], anglePointsY[0]); // from origin to one outer point
几乎工作,只有一条线不如预期。
那么我的下一个建议是再次绘制两条单独的线条,但这次只是旋转从原点绘制的线条。
super.paintComponent( g );
g.setColor(Color.red);
Graphics2D g2 = (Graphics2D)g.create();
int radius = 100;
int orginX = getWidth()/2;
int orginY = getHeight()/2;
double radians = Math.toRadians(60);
for(int i=0; i < 360; i+= 10)
{
double angle = Math.toRadians(i);
double centerX = radius * Math.cos(angle) + orginX;
double centerY = radius * Math.sin(angle) + orginY;
int[] anglePointsX = {(int) (radius * Math.cos(angle + radians) + centerX), (int) (radius * Math.cos(angle - radians) + centerX), orginX};
int[] anglePointsY = {(int) (radius * Math.sin(angle + radians) + centerY), (int) (radius * Math.sin(angle - radians) + centerY), orginY};
g.drawPolygon(anglePointsX, anglePointsY, 2);
// g2.drawLine(orginX, orginY, anglePointsX[0], anglePointsY[0]);
AffineTransform af = new AffineTransform();
af.translate(orginX, orginY);
af.rotate( angle );
g2.setTransform( af );
g2.drawLine(0, 0, 175, 0);
}
这种方法的问题在于,我不知道如何从原点计算线的长度,只是硬编码175.也许你的数学比我的好,你知道如何计算固定长度这条线。
确定最大线长度的强力方法可以是创建两个循环。第一个循环绘制外部线并确定使用的最大x值。然后第二个循环使用此值作为长度绘制36条旋转线:
protected void paintComponent(Graphics g)
{
super.paintComponent( g );
g.setColor(Color.red);
Graphics2D g2 = (Graphics2D)g.create();
int radius = 100;
int orginX = getWidth()/2;
int orginY = getHeight()/2;
double radians60 = Math.toRadians(60);
int maxX = 0;
for(int i=0; i < 360; i+= 10)
{
double angle = Math.toRadians(i);
double centerX = radius * Math.cos(angle) + orginX;
double centerY = radius * Math.sin(angle) + orginY;
int[] anglePointsX = {(int) (radius * Math.cos(angle + radians60) + centerX), (int) (radius * Math.cos(angle - radians60) + centerX), orginX};
int[] anglePointsY = {(int) (radius * Math.sin(angle + radians60) + centerY), (int) (radius * Math.sin(angle - radians60) + centerY), orginY};
g.drawPolygon(anglePointsX, anglePointsY, 2);
maxX = Math.max(maxX, anglePointsX[0]);
}
for(int i=0; i < 360; i+= 10)
{
double angle = Math.toRadians(i);
AffineTransform af = new AffineTransform();
af.translate(orginX, orginY);
af.rotate( angle );
g2.setTransform( af );
g2.drawLine(0, 0, maxX - orginX, 0);
}
g2.dispose();
}
答案 2 :(得分:2)
在这种情况下,“粗”线的原因确实是舍入误差。所以你观察到的并不是绘图的问题,而是计算的问题。请注意,由于各种原因(主要是一般double
值的精度有限),您不能假设像cos
和sin
这样的三角函数的所有结果都是您可能期望的结果成为。例如,
System.out.println(Math.sin(Math.toRadians(180)));
将打印1.2246467991473532E-16
,虽然在数学上应该是0.0
。同样,当结果应为199.999999999941234234
时,您计算的某些值最终可能会像200.0
。然后,当您将这些值转换为int
时,您将截断小数部分,结果将为199
- 导致您观察到的奇怪视觉效果。
在这种情况下,可以,实际上,通过使用Math.round
舍入结果来解决,就像在{的以下示例中所做的那样{1}}。
为了完整起见,PixelsOffByOnePanel
课程会显示如何使用PixelsOffByOnePanelNicer
和Path2D
值执行此操作。
double
附注:通常,通过调用
启用消除锯齿时,绘图可能看起来更好import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PixelsOffByOne
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
JFrame frame = new JFrame("");
frame.getContentPane().setLayout(new GridLayout(1,2));
frame.getContentPane().add(new PixelsOffByOnePanel());
frame.getContentPane().add(new PixelsOffByOnePanelNicer());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
class PixelsOffByOnePanel extends JPanel
{
int radius = 100;
@Override
public Dimension getPreferredSize()
{
return new Dimension(400,400);
}
@Override
public void paintComponent( Graphics g )
{
super.paintComponent( g );
g.setColor(Color.RED);
int orginX = getWidth()/2;
int orginY = getHeight()/2;
for(int i=0; i<=360; i+=10)
{
double angle = Math.toRadians(i);
double centerX = radius * Math.cos(angle) + orginX;
double centerY = radius * Math.sin(angle) + orginY;
int x0 = (int) Math.round(radius * Math.cos(angle+Math.toRadians(60)) + centerX);
int x1 = (int) Math.round(radius * Math.cos(angle-Math.toRadians(60)) + centerX);
int[] anglePointsX = {x0, x1, orginX};
int y0 = (int) Math.round(radius * Math.sin(angle+Math.toRadians(60)) + centerY);
int y1 = (int) Math.round(radius * Math.sin(angle-Math.toRadians(60)) + centerY);
int[] anglePointsY = {y0, y1, orginY};
g.drawPolygon(anglePointsX, anglePointsY, 3);
}
}
}
class PixelsOffByOnePanelNicer extends JPanel
{
int radius = 100;
@Override
public Dimension getPreferredSize()
{
return new Dimension(400,400);
}
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(Color.RED);
int originX = getWidth()/2;
int originY = getHeight()/2;
double d = Math.toRadians(60);
Path2D path = new Path2D.Double();
for(int i=0; i<=360; i+=10)
{
double angle = Math.toRadians(i);
double centerX = radius * Math.cos(angle) + originX;
double centerY = radius * Math.sin(angle) + originY;
double x0 = (int) Math.round(radius * Math.cos(angle+d) + centerX);
double x1 = (int) Math.round(radius * Math.cos(angle-d) + centerX);
double x2 = originX;
double y0 = (int) Math.round(radius * Math.sin(angle+d) + centerY);
double y1 = (int) Math.round(radius * Math.sin(angle-d) + centerY);
double y2 = originY;
path.moveTo(x0, y0);
path.lineTo(x1, y1);
path.lineTo(x2, y2);
path.closePath();
}
g.draw(path);
}
}
开始画画之前。但是,当您多次绘制多行时,如果专用仅绘制每行一次,结果可能看起来不那么令人满意。
答案 3 :(得分:0)
也许是两个不同的问题。
问题1,别名。您需要使用以下内容打开它:
https://docs.oracle.com/javase/tutorial/2d/advanced/quality.html
对于看起来比它们应该更粗的线条,你要画两次,这使它们看起来更厚。因此,最好的解决方案是避免两次绘制它们。尝试重构代码以绘制线而不是多边形,因此每行只绘制一次。