框架中未显示贝塞尔曲线
Bezier curve not showing in frame
用户必须通过单击框架中的任意位置来选择 4 个点,然后程序应该绘制贝塞尔曲线。我还提供了一种在用户单击的位置绘制小圆圈的方法,这样更容易看到。
我没有收到任何错误,只是没有显示曲线。显然,我错过了一些东西,但我不知道是什么。
代码:
public class Splines {
public Splines(){
JFrame frame = new JFrame("Bezier curves");
frame.add(new draw());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class draw extends JPanel implements MouseListener{
Point[] controlPoints = new Point[100];
ArrayList<Point> punkter = new ArrayList<>();
int pSize = punkter.size();
public draw(){
addMouseListener(this);
}
@Override
public void mousePressed(MouseEvent e) {
if (pSize==4) drawBezier(pSize,4,getGraphics());
drawPoint(e);
pSize++;
}
//Method drawing points to visualize the control points
public void drawPoint(MouseEvent evt){
Graphics g = getGraphics();
Graphics2D g2d = (Graphics2D) g;
punkter.add(new Point(evt.getX(), evt.getY()));
g2d.setColor(Color.red);
g2d.fillOval(punkter.get(pSize).x, punkter.get(pSize).y, 5, 5);
controlPoints[pSize] = punkter.get(pSize);
}
public void drawBezier(int i, int n, Graphics g) {
int j;
double t, delta;
Point curvePoints[] = new Point[n + 1];
delta = 1.0 / n;
for (j = 0; j <= n; j++) {
t = j * delta;
curvePoints[j] = new Point();
curvePoints[j].x = (int) Math.round(controlPoints[i - 3].x * (1.0 - t) * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 2].x * 3.0 * t * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 1].x * 3.0 * t * t * (1.0 - t)
+ controlPoints[i].x * t * t * t);
curvePoints[j].y = (int) Math.round(controlPoints[i - 3].y * (1.0 - t) * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 2].y * 3.0 * t * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 1].y * 3.0 * t * t * (1.0 - t)
+ controlPoints[i].y * t * t * t);
}
g.setColor(Color.red);
for (j = 0; j < n; j++)
g.drawLine(curvePoints[j].x, curvePoints[j].y, curvePoints[j + 1].x, curvePoints[j + 1].y);
} // End drawBezier
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public Dimension getPreferredSize() {
return new Dimension(600, 400);
}
}//End draw class
public static void main(String[] args) {
new Splines();
}//End main method
}//End Spline class
JPanel
自己绘制调用protected void paintComponent(Graphics gr)
我建议你 override
这个方法,放一个 List<Point>
用户点击的地方,然后在这个方法中画出你所有的曲线。
你的代码是这样的
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g)
...loop your points and draw your cruves on g
}
因此,当用户 mousePressed
添加到 List<Point>
并调用 JPanel
上的 repaint
方法时
因此,现在您在 Graphics
上绘制,但是当您的 swing 应用程序刷新 JPanel
时,绘制了您添加到其中的 components
(即 none).基本上,您正在将曲线从 swing 上下文中绘制到 Grafics g
,当 paintComponent
.
中的 swing decieds 时将重新绘制该曲线
尽管我强烈建议您考虑 MadProgrammer 和 Petter Friberg 在 AWT
中指出的有关绘画的内容并且 Swing
,您这里的直接问题非常微不足道。
具体来说,它位于 drawBezier()
方法中,该方法在第一次尝试访问 controlPoints
数组时引发 NullPointerException
。
当然,这是因为您试图访问 controlPoints[i]
,而实际上 i
的值为 4,而 controlPoints
是 zero-based意味着您正在引用一个实际上不存在的元素(
controlPoints[4]
是 null
)。在 Thorbjørn Ravn Andersen 的 回答 here.
中查看有关数组初始化的更多信息
现在解决方案对您来说应该很明显了:
curvePoints[j].x = (int) Math.round(controlPoints[i - 4].x * (1.0 - t) * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 3].x * 3.0 * t * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 2].x * 3.0 * t * t * (1.0 - t)
+ controlPoints[i - 1].x * t * t * t);
curvePoints[j].y = (int) Math.round(controlPoints[i - 4].y * (1.0 - t) * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 3].y * 3.0 * t * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 2].y * 3.0 * t * t * (1.0 - t)
+ controlPoints[i - 1].y * t * t * t);
它提供了这个:
我在mousePressed()
里也做了一个小的修改,这样画出贝塞尔曲线后returns:
@Override
public void mousePressed(MouseEvent e) {
if (pSize==4) {
drawBezier(pSize,4,getGraphics());
return;
}
drawPoint(e);
pSize++;
}
如果你做这个编辑,那么试试这个:
点击5次后绘制贝塞尔曲线。现在,最小化 window 并恢复它。你会看到它是空白的。但是,如果您在内部再次单击,您将看到曲线重新出现(只有曲线;不是控制点)。为什么会这样?
如果您能回答这个问题,那么您离理解评论和 Petter Friberg 的 回答中提到的一些问题又近了一步。
更新/附录 - 给予坚持
整个想法是找到一种方法,使您的绘图持久适用于任意用户操作,例如最小化、调整大小等。正如 Petter Friberg 指出的那样,实现此目的的方法是对overridepaintComponent()
的方法。
如何做到这一点?嗯,其实很简单:
首先完全删除您的 drawPoint()
方法。它的功能将分为 mousePressed()
和 paintComponent()
,如下所示:
@Override
public void mousePressed(MouseEvent e) {
if (pSize<4){
punkter.add(new Point(e.getX(), e.getY()));
controlPoints[pSize] = punkter.get(pSize);
pSize++;
repaint();
} else if (pSize==4){
// do other stuff, for example reset everything and start over
//pSize = 0;
//punkter.clear();
//for (int i= 0; i < controlPoints.length; i++){
// controlPoints[i]= null;
//}
//repaint();
//System.out.println("Reset");
}
}
还有这个:
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.red);
for (int i=0; i<pSize; i++){
g2d.fillOval(punkter.get(i).x, punkter.get(i).y, 5, 5);
}
if (pSize==4) {
drawBezier(pSize,4,g);
}
}
这里值得指出的一件事是,您仍然需要调用 superclass's paintComponent()
方法,这是在这一行中实现的:super.paintComponent(g);
此外,getGraphics()
作为 drawBezier()
的参数不再有效(如果曾经有效的话);相反,使用 g
图形对象。
现在,应用程序拥有了以前所缺乏的持久性。
请注意,代码中没有任何地方直接调用 paintComponent()
。相反,它会在您调用 repaint()
时由图形子系统调用,或者系统决定是时候重绘了。一种查看方式是注释掉对 repaint()
的调用。现在没有对任何绘图函数的一次调用。这是否意味着没有绘制任何东西?或者,也许,您可以通过执行一些看似无关的操作来间接调用绘图?
用户必须通过单击框架中的任意位置来选择 4 个点,然后程序应该绘制贝塞尔曲线。我还提供了一种在用户单击的位置绘制小圆圈的方法,这样更容易看到。
我没有收到任何错误,只是没有显示曲线。显然,我错过了一些东西,但我不知道是什么。
代码:
public class Splines {
public Splines(){
JFrame frame = new JFrame("Bezier curves");
frame.add(new draw());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class draw extends JPanel implements MouseListener{
Point[] controlPoints = new Point[100];
ArrayList<Point> punkter = new ArrayList<>();
int pSize = punkter.size();
public draw(){
addMouseListener(this);
}
@Override
public void mousePressed(MouseEvent e) {
if (pSize==4) drawBezier(pSize,4,getGraphics());
drawPoint(e);
pSize++;
}
//Method drawing points to visualize the control points
public void drawPoint(MouseEvent evt){
Graphics g = getGraphics();
Graphics2D g2d = (Graphics2D) g;
punkter.add(new Point(evt.getX(), evt.getY()));
g2d.setColor(Color.red);
g2d.fillOval(punkter.get(pSize).x, punkter.get(pSize).y, 5, 5);
controlPoints[pSize] = punkter.get(pSize);
}
public void drawBezier(int i, int n, Graphics g) {
int j;
double t, delta;
Point curvePoints[] = new Point[n + 1];
delta = 1.0 / n;
for (j = 0; j <= n; j++) {
t = j * delta;
curvePoints[j] = new Point();
curvePoints[j].x = (int) Math.round(controlPoints[i - 3].x * (1.0 - t) * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 2].x * 3.0 * t * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 1].x * 3.0 * t * t * (1.0 - t)
+ controlPoints[i].x * t * t * t);
curvePoints[j].y = (int) Math.round(controlPoints[i - 3].y * (1.0 - t) * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 2].y * 3.0 * t * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 1].y * 3.0 * t * t * (1.0 - t)
+ controlPoints[i].y * t * t * t);
}
g.setColor(Color.red);
for (j = 0; j < n; j++)
g.drawLine(curvePoints[j].x, curvePoints[j].y, curvePoints[j + 1].x, curvePoints[j + 1].y);
} // End drawBezier
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public Dimension getPreferredSize() {
return new Dimension(600, 400);
}
}//End draw class
public static void main(String[] args) {
new Splines();
}//End main method
}//End Spline class
JPanel
自己绘制调用protected void paintComponent(Graphics gr)
我建议你 override
这个方法,放一个 List<Point>
用户点击的地方,然后在这个方法中画出你所有的曲线。
你的代码是这样的
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g)
...loop your points and draw your cruves on g
}
因此,当用户 mousePressed
添加到 List<Point>
并调用 JPanel
repaint
方法时
因此,现在您在 Graphics
上绘制,但是当您的 swing 应用程序刷新 JPanel
时,绘制了您添加到其中的 components
(即 none).基本上,您正在将曲线从 swing 上下文中绘制到 Grafics g
,当 paintComponent
.
尽管我强烈建议您考虑 MadProgrammer 和 Petter Friberg 在 AWT
中指出的有关绘画的内容并且 Swing
,您这里的直接问题非常微不足道。
具体来说,它位于 drawBezier()
方法中,该方法在第一次尝试访问 controlPoints
数组时引发 NullPointerException
。
当然,这是因为您试图访问 controlPoints[i]
,而实际上 i
的值为 4,而 controlPoints
是 zero-based意味着您正在引用一个实际上不存在的元素(
controlPoints[4]
是 null
)。在 Thorbjørn Ravn Andersen 的 回答 here.
现在解决方案对您来说应该很明显了:
curvePoints[j].x = (int) Math.round(controlPoints[i - 4].x * (1.0 - t) * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 3].x * 3.0 * t * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 2].x * 3.0 * t * t * (1.0 - t)
+ controlPoints[i - 1].x * t * t * t);
curvePoints[j].y = (int) Math.round(controlPoints[i - 4].y * (1.0 - t) * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 3].y * 3.0 * t * (1.0 - t) * (1.0 - t)
+ controlPoints[i - 2].y * 3.0 * t * t * (1.0 - t)
+ controlPoints[i - 1].y * t * t * t);
它提供了这个:
我在mousePressed()
里也做了一个小的修改,这样画出贝塞尔曲线后returns:
@Override
public void mousePressed(MouseEvent e) {
if (pSize==4) {
drawBezier(pSize,4,getGraphics());
return;
}
drawPoint(e);
pSize++;
}
如果你做这个编辑,那么试试这个:
点击5次后绘制贝塞尔曲线。现在,最小化 window 并恢复它。你会看到它是空白的。但是,如果您在内部再次单击,您将看到曲线重新出现(只有曲线;不是控制点)。为什么会这样?
如果您能回答这个问题,那么您离理解评论和 Petter Friberg 的 回答中提到的一些问题又近了一步。
更新/附录 - 给予坚持
整个想法是找到一种方法,使您的绘图持久适用于任意用户操作,例如最小化、调整大小等。正如 Petter Friberg 指出的那样,实现此目的的方法是对overridepaintComponent()
的方法。
如何做到这一点?嗯,其实很简单:
首先完全删除您的 drawPoint()
方法。它的功能将分为 mousePressed()
和 paintComponent()
,如下所示:
@Override
public void mousePressed(MouseEvent e) {
if (pSize<4){
punkter.add(new Point(e.getX(), e.getY()));
controlPoints[pSize] = punkter.get(pSize);
pSize++;
repaint();
} else if (pSize==4){
// do other stuff, for example reset everything and start over
//pSize = 0;
//punkter.clear();
//for (int i= 0; i < controlPoints.length; i++){
// controlPoints[i]= null;
//}
//repaint();
//System.out.println("Reset");
}
}
还有这个:
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.red);
for (int i=0; i<pSize; i++){
g2d.fillOval(punkter.get(i).x, punkter.get(i).y, 5, 5);
}
if (pSize==4) {
drawBezier(pSize,4,g);
}
}
这里值得指出的一件事是,您仍然需要调用 superclass's paintComponent()
方法,这是在这一行中实现的:super.paintComponent(g);
此外,getGraphics()
作为 drawBezier()
的参数不再有效(如果曾经有效的话);相反,使用 g
图形对象。
现在,应用程序拥有了以前所缺乏的持久性。
请注意,代码中没有任何地方直接调用 paintComponent()
。相反,它会在您调用 repaint()
时由图形子系统调用,或者系统决定是时候重绘了。一种查看方式是注释掉对 repaint()
的调用。现在没有对任何绘图函数的一次调用。这是否意味着没有绘制任何东西?或者,也许,您可以通过执行一些看似无关的操作来间接调用绘图?