1
\$\begingroup\$

Happy Valentine's Day! To celebrate this day of love and happiness (or sadness and loneliness for those of us who are single...), let's write some code to create some hearts!

Specifically, if you take this challenge upon you, you are to write code to output a heart/hearts as an image file, with the ability to easily change the dimensions of the image (both width and height, separately). You could take input into your program to get the dimensions, or two simple variables that can be changed would be enough (or a couple constants). If you do opt for the variables, no more than two tokens of the program should be changed to adjust the size.

So for instance, you could write a program to output this image (with variable width and height, of course):

wikipedia heart

However, that heart isn't that interesting. It's just a plain heart. I challenge you to create the most creative and interesting heart that you can!

\$\endgroup\$

closed as off-topic by Peter Taylor, feersum, Martin Ender, grc, Justin Feb 13 '15 at 15:15

  • This question does not appear to be about programming puzzles or code golf within the scope defined in the help center.
If this question can be reworded to fit the rules in the help center, please edit the question.

  • \$\begingroup\$ I can help if you do not desire the love being given at Valentine's eve :P \$\endgroup\$ – Optimizer Feb 13 '15 at 7:34
  • \$\begingroup\$ Just a note. I did not downvote it :P \$\endgroup\$ – Optimizer Feb 13 '15 at 7:45
  • \$\begingroup\$ With practically no restrictions this feels like it could turn into an art contest... \$\endgroup\$ – Sp3000 Feb 13 '15 at 7:45
  • 5
    \$\begingroup\$ I'm voting to close this question as off-topic because it's an art contest, not a programming contest. \$\endgroup\$ – Peter Taylor Feb 13 '15 at 8:03
  • 3
    \$\begingroup\$ I would say it's more the other way round: that question was never on topic, but we've got better at closing stuff which isn't. \$\endgroup\$ – Peter Taylor Feb 13 '15 at 9:32
1
\$\begingroup\$

Java

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

import static java.lang.Math.PI;
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);
        final double max_heart = 0.679; // This is the approximate max value that can come out of the heart function (found by trial and error)
        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);
                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.png"));
    }

    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 topHeart(x, y, temp, temp1);
        }
        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;
            }
        } else {
            return Math.sqrt(Math.pow(2.0 / 3, 2) * (1 - Math.pow(x, 2)));
        }
    }

    private static double topHeart(double x, double y, double temp, double temp1) {
        //complex arithmetic
        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);

        if (Math.abs(result[1]) < 1e-5) {
            return result[0];
        }
        return -1;
    }

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

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

To scale, just change the x/y dimensions of this line (the first line in the main function):

BufferedImage img = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_RGB);

Output (for a size of 1000 by 1000):


On wikipedia, there is a 3D heart shown. This was my inspiration; I thought to myself, "Why not use a 3D heart and use the Z output as color?" So that's exactly what I did. However, I didn't use the exact same formula; I used this implicit equation for a 3D heart:

I played around in Mathematica, (I think I switched z and y) and solved for z, yielding this really ugly equation (click for full size):

Then, I "simply" implemented the function in Java. Okay, it wasn't so simple. I had to do some checks to avoid exceptions (no sqrts of negative numbers). But there is one case where we do need to take a sqrt of a negative number (IE delve into the complex world) in order to get a real output. That's why I have the topOfHeart function; it does the computation for that part. If I replace the call to that function with return -1, this is the image I get instead (smallified):

A couple interesting things to note:

  • I do a lot of return -1 to show that I reached an invalid value, but I don't handle this in the main function. This results in a color of 0xFFADAD - a lovely shade of pink
  • The complex number functions are semi-interesting. I switch between polar and rectangular coordinates, since polar coordinates make multiplicative operations easier, while rectangular coordinates make additive operations easier.

    • cbrt_complex and sqrt_complex are actually very interesting. Using wolframalpha, I found this formula for the nth root of a complex number (polar form):

    • taking the reciprocal of a polar coordinate complex number is easy: r e -> 1/r e-iθ
    • multiplying by a real number d is also easy: r e -> d r e
\$\endgroup\$
  • \$\begingroup\$ W o w. J u s t - w o w. \$\endgroup\$ – Alvaro Feb 23 '15 at 15:38
1
\$\begingroup\$

Bash: 16 characters

echo -e "\u2665"

output (when unicode supported)

Ah wait, it must be an image?

ImageMagick: 137 characters

w=99
h=199
convert -size ${w}x$w -gravity center -font WebDings -fill red label:Y h.gif
convert h.gif -resize ${w}x$h\! h2.gif

output (when WebDings font installed)

h.gif h2.gif

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.