2015-02-12 6 views
5

Видя, как День Святого Валентина быстро приближается, я решил создать сердце. Так что я нашел this heart from mathematica.se:Как исправить это сердце?

enter image description here

Я играл в Mathematica (решена г, переключая некоторые переменные вокруг), чтобы получить это уравнение для г-значения сердца, при х и у значения (нажмите для полного размера):

enter image description here

Я точно портирую это уравнение на Java, имея дело с несколькими недоступных случаями:

import static java.lang.Math.cbrt; 
import static java.lang.Math.pow; 
import static java.lang.Math.sqrt; 

... 

public static double heart(double xi, double yi) { 
    double x = xi; 
    double y = -yi; 
    double temp = 5739562800L * pow(y, 3) + 109051693200L * pow(x, 2) * pow(y, 3) 
        - 5739562800L * pow(y, 5); 
    double temp1 = -244019119519584000L * pow(y, 9) + pow(temp, 2); 
    // 
    if (temp1 < 0) { 
     return -1; // this is one possible out of bounds location 
        // this spot is the location of the problem 
    } 
    // 
    double temp2 = sqrt(temp1); 
    double temp3 = cbrt(temp + temp2); 
    if (temp3 != 0) { 
     double part1 = (36 * cbrt(2) * pow(y, 3))/temp3; 
     double part2 = 1/(10935 * cbrt(2)) * temp3; 
     double looseparts = 4.0/9 - 4.0/9 * pow(x, 2) - 4.0/9 * pow(y, 2); 
     double sqrt_body = looseparts + part1 + part2; 
     if (sqrt_body >= 0) { 
      return sqrt(sqrt_body); 
     } else { 
      return -1; // this works; returns -1 if we are outside the heart 
     } 
    } else { 
     // through trial and error, I discovered that this should 
     // be an ellipse (or that it is close enough) 
     return Math.sqrt(Math.pow(2.0/3, 2) * (1 - Math.pow(x, 2))); 
    } 
} 

Единственная проблема заключается в том, что, когда temp1 < 0, я не могу просто вернуться -1, как я:

if (temp1 < 0) { 
    return -1; // this is one possible out of bounds location 
       // this spot is the location of the problem 
} 

Это не поведение сердца в этой точке. Как это, когда я пытаюсь сделать мое изображение:

import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 
import javax.imageio.ImageIO; 

import static java.lang.Math.cbrt; 
import static java.lang.Math.pow; 
import static java.lang.Math.sqrt; 

public class Heart { 
    public static double scale(int x, int range, double l, double r) { 
     double width = r - l; 
     return (double) x/(range - 1) * width + l; 
    } 

    public static void main(String[] args) throws IOException { 
     BufferedImage img = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_RGB); 
     // this is actually larger than the max heart value 
     final double max_heart = 0.679; 
     double max = 0.0; 
     for (int x = 0; x < img.getWidth(); x++) { 
      for (int y = 0; y < img.getHeight(); y++) { 
       double xv = scale(x, img.getWidth(), -1.2, 1.2); 
       double yv = scale(y, img.getHeight(), -1.3, 1); 
       double heart = heart(xv, yv); //this isn't an accident 
       // yes I don't check for the return of -1, but still 
       // the -1 values return a nice shade of pink: 0xFFADAD 
       // None of the other values should be negative, as I did 
       // step through from -1000 to 1000 in python, and there 
       // were no negatives that were not -1 
       int r = 0xFF; 
       int gb = (int) (0xFF * (max_heart - heart)); 
       int rgb = (r << 16) | (gb << 8) | gb; 
       img.setRGB(x, y, rgb); 
      } 
     } 
     ImageIO.write(img, "png", new File("location")); 
    } 
    // heart function clipped; it belongs here 
} 

я получаю это:

enter image description here

Посмотрите на этот провал на вершине! Я попытался изменить эту проблемную -1 к .5, в результате чего в этом:

enter image description here

Теперь сердце есть рога. Но становится ясно, когда это проблемное состояние if выполнено.

Как исправить эту проблему? Мне не нужна дыра в моем сердце наверху, и я не хочу рогатого сердца. Если бы я мог закрепить рожки в форме сердца и правильно покрасить все остальное, это было бы прекрасно. В идеале, две стороны сердца собирались вместе как точка (сердца имеют небольшую точку в соединении), но если они изогнуты вместе, как показано на рогах, это тоже будет хорошо. Как я могу это достичь?

+0

'double heart = heart (xv, yv);' - вы не проверяете результат, чтобы быть> = 0 –

+0

@ Lashane Я полностью осознаю, но -1 производит приятный розовый оттенок для своего диапазона (не спрашивайте меня, как). Это не устраняет проблему, о которой я прошу. Все нестандартные значения обрезанных производят неотрицательные числа (проверено на python) – Justin

+4

Посмотрите на 'java.util.Timer'; сломанное сердце исцеляет со временем.

ответ

3

Проблема проста. Если мы посмотрим на эту область подковы, мы получим мнимые числа. Для части его это должно принадлежать нашему сердцу. В этой области, если бы мы оценивали нашу функцию (по математике, а не по программированию), мнимая часть функции отменила. Так оно и должно выглядеть следующим образом (генерируется в Mathematica):

enter image description here

В основном, функция для этой части практически идентичны; мы просто должны делать арифметику с комплексными числами вместо действительных чисел.Вот функция, которая делает именно то, что:

private static double topOfHeart(double x, double y, double temp, double temp1) { 
    //complex arithmetic; each double[] is a single number 
    double[] temp3 = cbrt_complex(temp, sqrt(-temp1)); 
    double[] part1 = polar_reciprocal(temp3); 
    part1[0] *= 36 * cbrt(2) * pow(y, 3); 
    double[] part2 = temp3; 
    part2[0] /= (10935 * cbrt(2)); 
    toRect(part1, part2); 
    double looseparts = 4.0/9 - 4.0/9 * pow(x, 2) - 4.0/9 * pow(y, 2); 
    double real_part = looseparts + part1[0] + part2[0]; 
    double imag_part = part1[1] + part2[1]; 
    double[] result = sqrt_complex(real_part, imag_part); 
    toRect(result); 

    // theoretically, result[1] == 0 should work, but floating point says otherwise 
    if (Math.abs(result[1]) < 1e-5) { 
     return result[0]; 
    } 
    return -1; 
} 

/** 
* returns a specific cuberoot of this complex number, in polar form 
*/ 
public static double[] cbrt_complex(double a, double b) { 
    double r = Math.hypot(a, b); 
    double theta = Math.atan2(b, a); 
    double cbrt_r = cbrt(r); 
    double cbrt_theta = 1.0/3 * (2 * PI * Math.floor((PI - theta)/(2 * PI)) + theta); 
    return new double[]{cbrt_r, cbrt_theta}; 
} 

/** 
* returns a specific squareroot of this complex number, in polar form 
*/ 
public static double[] sqrt_complex(double a, double b) { 
    double r = Math.hypot(a, b); 
    double theta = Math.atan2(b, a); 
    double sqrt_r = Math.sqrt(r); 
    double sqrt_theta = 1.0/2 * (2 * PI * Math.floor((PI - theta)/(2 * PI)) + theta); 
    return new double[]{sqrt_r, sqrt_theta}; 
} 

public static double[] polar_reciprocal(double[] polar) { 
    return new double[]{1/polar[0], -polar[1]}; 
} 

public static void toRect(double[]... polars) { 
    for (double[] polar: polars) { 
     double a = Math.cos(polar[1]) * polar[0]; 
     double b = Math.sin(polar[1]) * polar[0]; 
     polar[0] = a; 
     polar[1] = b; 
    } 
} 

Чтобы присоединиться к этому с помощью вашей программы, просто изменить свою функцию, чтобы отразить это:

if (temp1 < 0) { 
    return topOfHeart(x, y, temp, temp1); 
} 

И запустить его, мы получаем желаемый результат:

enter image description here


это должно быть совершенно ясно, что этот новый functi на реализует ту же формулу. Но как каждая часть работает?

double[] temp3 = cbrt_complex(temp, sqrt(-temp1)); 

cbrt_complex занимает комплексное число в виде a + b i. Вот почему второй аргумент - это просто sqrt(-temp1) (обратите внимание, что temp1 < 0, поэтому я использую - вместо Math.abs; Math.abs, вероятно, лучшая идея). cbrt_complex возвращает корень куба комплексного числа, в полярной форме: r e. We can see from wolframalpha что с положительной r и θ, мы можем написать п-й корень из комплексных чисел следующим образом:

MathJax: \sqrt[n]r\,e^{i\left(2\pi\left\lfloor\frac{\pi-\theta}{2\pi}\right\rfloor+\theta\right)}

И это именно то, как код cbrt_complex и sqrt_complex работы. Обратите внимание, что оба принимают комплексное число в прямоугольных координатах (a + b i) и возвращают комплексное число в полярных координатах (r e)

double[] part1 = polar_reciprocal(temp3); 

Легче взять обратные полярное комплексное число, чем прямоугольное комплексное число. Если у нас есть r e, то его обратное (это, к счастью, соответствует стандартным правилам питания) - это просто 1/r e-iθ. Вот почему мы остаемся в полярной форме; полярная форма упрощает операции умножения, а операции типа ввода сложнее, а прямоугольная форма - наоборот. Обратите внимание, что если у нас есть полярное комплексное число r e, и мы хотим умножить на действительное число d, ответ будет таким же простым, как и d r e.

Функция toRect выполняет именно то, что кажется: она преобразует комплексные числа полярных координат в комплексные числа прямоугольной координаты.

Возможно, вы заметили, что оператор if не проверяет наличие нет мнимой части, но только если мнимая часть действительно маленькая. Это связано с тем, что мы используем числа с плавающей запятой, поэтому проверка result[1] == 0, скорее всего, не удастся.

И вот вы где! Обратите внимание, что мы могли бы реализовать всю функцию сердца с помощью этой сложной арифметики чисел, но, вероятно, быстрее этого избежать.

Смежные вопросы