Как нарисовать кривую бабочку как можно точнее? - PullRequest
0 голосов
/ 30 октября 2018

Я пытаюсь нарисовать кривую бабочки , используя Java.

Вот параметрическое уравнение для упомянутой кривой:

Butterfly Curve Parametric equation

Из того, что я помню из колледжа, способ нарисовать параметрическое уравнение с помощью Java следующий:

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;
    }
}

Теперь это дало мне следующий результат:

first try curve

Хорошо, это так далеко от ожидаемого результата.

Затем я решил попробовать, используя Line2D.Double, думая, что это даст более точный рисунок.

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;
    }
}

Что дало следующий результат:

Better curve

Хорошо, это, конечно, выглядит лучше, но не совсем ожидаемый результат.

Поэтому я спрашиваю, есть ли способ нарисовать наиболее точную кривую, используя это параметрическое уравнение с Java?

best curve

Это не должно выглядеть на 100% как на картинке выше, но самое близкое.

1 Ответ

0 голосов
/ 30 октября 2018

Ваше утверждение масштаба также масштабирует ширину вашей линии, вызывая странную форму вашей кривой. Есть два простых способа решения проблемы:

  1. Уменьшите ширину вашей линии, например, до 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;
    }  
    

В результате:

enter image description here

  1. Удалите ваше выражение масштаба и масштабируйте кривую, используя ее амплитуду, то есть используйте постоянный префактор относительно ваших значений x и y, например, -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;
    }  
    

В результате (более или менее идентичный):

enter image description here

Более того, вы можете улучшить качество своего графика с помощью сглаживания и увеличения 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;
    }  

В результате (выглядит намного лучше):

enter image description here

Пока что связь между двумя точками является прямой линией. Конечно, вы можете использовать сплайны (Безье и т. Д.) Для дальнейшей оптимизации, но, вероятно, это не тривиально.

...