贝塞尔曲线没有显示在框架中

时间:2015-10-16 21:52:48

标签: java swing paintcomponent mouselistener

用户必须通过单击框架中的任意位置来选择4个点,然后程序将绘制Bezier曲线。我还提供了一种方法,可以在用户点击的位置绘制小圆圈,以便更容易查看。

我没有收到任何错误,但曲线没有显示出来。显然,我错过了一些东西,但我无法弄清楚是什么。

代码:

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

2 个答案:

答案 0 :(得分:3)

虽然我强烈建议您考虑 MadProgrammer Petter Friberg 指出的关于AWTSwing中绘画的内容,这里的直接问题非常简单。

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

当然,这是因为您尝试访问controlPoints[i],实际上,i的值为4且controlPointszero-based,这意味着你引用一个实际上不存在的元素( controlPoints[4]null)。在ThorbjørnRavnAndersen的答案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);

提供了这个:

enter image description here

我还在mousePressed()进行了一次小编辑,以便在绘制贝塞尔曲线后返回:

@Override
public void mousePressed(MouseEvent e) {
    if (pSize==4) {
        drawBezier(pSize,4,getGraphics());
        return;
    }
    drawPoint(e);   
    pSize++;
}

如果您进行此编辑,请尝试以下操作:

单击5次后,绘制Bezier曲线。现在,最小化窗口并恢复它。你会看到它是空白的。但是,如果再次单击内部,则会看到曲线重新出现(仅曲线;而不是控制点)。为什么会这样?

如果您能够回答这个问题,那么您将更快地了解评论中提到的一些问题以及 Petter Friberg的答案。

更新/附录 - 给予持久性

整个想法是找到一种方法让你的绘图持久化到任意用户动作,例如最小化,调整大小等。正如 Petter Friberg 指出的那样,这样做的方法是{{3 } paintComponent()方法。

怎么做?嗯,实际上它很简单:

首先完全删除您的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);
    }
}

值得指出的一点是,您仍然需要调用override paintComponent()方法,这可以通过以下方式实现:super.paintComponent(g);

此外,getGraphics()不再有效(如果有的话)作为drawBezier()的参数;相反,使用了g Graphics对象。

到目前为止,应用程序具有以前缺少的持久性。

请注意,代码中没有任何地方可以直接调用paintComponent()。相反,它在您调用repaint()时由图形子系统调用,或者系统决定是时候重绘。查看此内容的一种方法是将对repaint()的调用注释掉。现在没有任何一个绘图功能的调用。这是否意味着什么都没有被画出来?或者,也许,您可以通过执行一些看似无关的动作来间接调用绘图?

答案 1 :(得分:2)

要绘制的JPanel调用protected void paintComponent(Graphics gr)

我建议你override这个方法,在用户点击的位置放置List<Point>,并在此方法中绘制所有曲线。

Yuor代码就是这样的

@Override
protected void paintComponent(Graphics g) {
  super.paintComponent(g)
  ...loop your points and draw your cruves on g
}

因此,当用户mousePressed添加到List<Point>并在repaint

上调用JPanel metod时

因此,您现在可以使用Graphics,但是当您的swing应用程序刷新JPanel时,会绘制您添加到其中的components(没有)。基本上你正在将摆动上下文中的曲线绘制到Grafics g,当paintComponent中的摇摆被判定时,它将被重绘。