How to draw a butterfly curve as accurate as possible?

六月ゝ 毕业季﹏ 提交于 2019-12-11 10:57:31

问题


I am trying to draw a butterfly curve using Java.

Here's the parametric equation for the mentioned curve:

From what I remember from the college, the way to draw a parametric equation with Java is the next:

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D)g;
    g2.translate(300,300);
    int x1,y1;
    int x0 = 0;
    int y0 = (int)(Math.E-2); //for x = 0, we get y = Math.E - 2
    int nPoints = 1000;
    g2.scale(30,-30);
    for(int i=0;i<nPoints;i++) {
        double t= 12*i*Math.PI/nPoints; //to make it between 0 and 12*PI.
        x1=(int)(Math.sin(t)*(Math.pow(Math.E,Math.cos(t))-2*Math.cos(4*t)-Math.pow(Math.sin(t/12),5)));
        y1 = (int)(Math.cos(t)*(Math.pow(Math.E,Math.cos(t))-2*Math.cos(4*t)-Math.pow(Math.sin(t/12),5)));
        g2.drawLine(x0,y0,x1,y1);
        x0=x1;
        y0=y1;
    }
}

Now, this gave me the next result:

Okay, this is so far away from the expected result.

I then decided to try it using Line2D.Double thinking that this would give a more accurate drawing.

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D)g;
    g2.translate(300,300);
    double x1,y1;
    double x0 = 0;
    int nPoints = 500;
    g2.scale(30,-30);
    double y0 = Math.E-2;
    for(int i=0;i<nPoints;i++) {
        double t= 12*i*Math.PI/nPoints;
        x1=(Math.sin(t)*(Math.pow(Math.E,Math.cos(t))-2*Math.cos(4*t)-Math.pow(Math.sin(t/12),5)));
        y1 = (Math.cos(t)*(Math.pow(Math.E,Math.cos(t))-2*Math.cos(4*t)-Math.pow(Math.sin(t/12),5)));
        g2.draw(new Line2D.Double(x0,y0,x1,y1));
        x0=x1;
        y0=y1;
    }
}

Which yielded the next result:

Okay, this surely looks better, but not the expected result for sure.

Hence I am asking, is there a way to draw the most accurate curve using this parametric equation with Java?

It doesn't have to look 100% like the image above, but the closest.


回答1:


Your scale-statement scales also the width of your line causing the strange shape of your curve. There are two easy ways to solve te problem:

  1. Reduce the width of your line, e.g. to 0.01f:

    Graphics2D g2 = (Graphics2D)g;
    g2.translate(300,300);
    double x1,y1;
    double x0 = 0;
    int nPoints = 500;
    // Alternative 1 ---------------------
    g2.scale(30,-30);
    g2.setStroke(new BasicStroke(0.01f ));
    // -----------------------------------
    double y0 = Math.E-2;
    for(int i=0;i<nPoints;i++) {
        double t= 12*i*Math.PI/nPoints;
        x1= (Math.sin(t)*(Math.pow(Math.E,Math.cos(t))-2*Math.cos(4*t)-Math.pow(Math.sin(t/12),5)));
        y1 = (Math.cos(t)*(Math.pow(Math.E,Math.cos(t))-2*Math.cos(4*t)-Math.pow(Math.sin(t/12),5)));
        g2.draw(new Line2D.Double(x0,y0,x1,y1));
        x0=x1;
        y0=y1;
    }  
    

This results in:

  1. Remove your scale-statement and scale the curve using its amplitude i.e. use a constant prefactor concerning your x- and y-values, e.g. -30:

    Graphics2D g2 = (Graphics2D)g;
    g2.translate(300,300);
    double x1,y1;
    double x0 = 0;
    int nPoints = 500;
    // Alternative 2 ---------------------
    double amp = -30.0;
    // -----------------------------------
    double y0 = Math.E-2;
    for(int i=0;i<nPoints;i++) {
        double t= 12*i*Math.PI/nPoints;
        // Alternative 2 ----------------------------------------------------------------------------------
        x1=amp*(Math.sin(t)*(Math.pow(Math.E,Math.cos(t))-2*Math.cos(4*t)-Math.pow(Math.sin(t/12),5)));
        y1=amp*(Math.cos(t)*(Math.pow(Math.E,Math.cos(t))-2*Math.cos(4*t)-Math.pow(Math.sin(t/12),5)));
        // ------------------------------------------------------------------------------------------------
        g2.draw(new Line2D.Double(x0,y0,x1,y1));
        x0=x1;
        y0=y1;
    }  
    

This results in (which is more or less identical):

Moreover you can enhance the quality of your plot by using an antialiasing and an increase of nPoints:

    Graphics2D g2 = (Graphics2D)g;
    // Optimization ------------------------------------
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    int nPoints = 1500;
    // -------------------------------------------------
    g2.translate(300,300);
    double x1,y1;
    double x0 = 0;
    // Alternative 1 ---------------------
    g2.scale(50,-50);
    g2.setStroke(new BasicStroke(0.01f ));
    // -----------------------------------
    double y0 = Math.E-2;
    for(int i=0;i<nPoints;i++) {
        double t= 12*i*Math.PI/nPoints;
        x1= (Math.sin(t)*(Math.pow(Math.E,Math.cos(t))-2*Math.cos(4*t)-Math.pow(Math.sin(t/12),5)));
        y1 = (Math.cos(t)*(Math.pow(Math.E,Math.cos(t))-2*Math.cos(4*t)-Math.pow(Math.sin(t/12),5)));
        g2.draw(new Line2D.Double(x0,y0,x1,y1));
        x0=x1;
        y0=y1;
    }  

This results in (which looks much better):

So far, the connection between two points is a straight line. Sure, you can use splines (Bezier etc.) for further optimization, but probably that is not trivial.



来源:https://stackoverflow.com/questions/53054837/how-to-draw-a-butterfly-curve-as-accurate-as-possible

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!