框架中未显示贝塞尔曲线

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 时将重新绘制该曲线

尽管我强烈建议您考虑 MadProgrammerPetter FribergAWT 中指出的有关绘画的内容并且 Swing,您这里的直接问题非常微不足道。

具体来说,它位于 drawBezier() 方法中,该方法在第一次尝试访问 controlPoints 数组时引发 NullPointerException

当然,这是因为您试图访问 controlPoints[i],而实际上 i 的值为 4,而 controlPointszero-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() 的调用。现在没有对任何绘图函数的一次调用。这是否意味着没有绘制任何东西?或者,也许,您可以通过执行一些看似无关的操作来间接调用绘图?