"Valentine's Day Equation" plotted in Ruby
Tags: ruby, gnuplot, graphs, valentinesday
Published: 2021-02-15 17:37:43

---
title: "Valentine's Day Equation" plotted in Ruby
published: true
description: How to plot a graph of Valentine's Day equation using Ruby & GNUPlot
tags: ruby, gnuplot, graphs, valentinesday
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/2ml8zt8lw8qfvc2sb141.png
---

I'm trying to expose my kids to programming bit by bit. :smile: Things that work the best are something that they relate to.  Almost right on cue, [this blog post](https://dev.to/devlorenzo/the-san-valentine-equation-1jb9) came up on my feed on [dev.to](https://dev.to) on Saturday and I thought it would be a fun activity to help kids make Valentines in a new way. Thank you, [@DevLorenz0](https://dev.to/devlorenzo) for the formula! :heart:

All you need is [ruby](https://www.ruby-lang.org/en/downloads/) & [gnuplot](http://www.gnuplot.info/), and possibly [gnuplot official docs pdf](http://www.gnuplot.info/docs_5.4/Gnuplot_5_4.pdf) if you want to experiment further.

```bash
# on Mac
brew install gnuplot
gem install gnuplot 

# Chocolotey has a package for Windows
# choco install gnuplot 

# Use rpm / apt on Linux
# apt-get install gnuplot
```

## Code

```ruby
# valentine_day_plot.rb
# Tested on Ruby 2.7.2
require 'rubygems'
require 'gnuplot'

valentines_day_function = lambda { |t|
  x = 16 * Math.sin(t)**3
  y = 13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)
  # Experimentally found that rounding to 4 
  # produces the best results for us 
  [x.round(4).to_f, y.round(4).to_f]
}

# Adjust this to get more or less number of plot points
# At the end we transpose array, to shape data for 
# Gnuplot::DataSet input below
DISTANCE_BETWEEN_POINTS = 0.0001
COORDINATES = (0..(2 * Math::PI)).step(DISTANCE_BETWEEN_POINTS)
                                 .to_a
                                 .map do |t|
  valentines_day_function.call(t)
end.transpose

Gnuplot.open do |gp|
  Gnuplot::Plot.new(gp) do |plot|
    plot.output 'valentine_day_plot.pdf'
    # Sized as a nice square to be printed on a A4 or US Letter page
    plot.terminal 'pdf colour size 8in,8in'

    # The following is self-explanatory, but setting range too small,
    # will put the graph out of bounds
    plot.xrange '[-20:20]'
    plot.yrange '[-20:20]'
    plot.title  "Valentine's Day Equation"
    plot.ylabel 'Love-y'
    plot.xlabel 'Love-x'

    # DataSet.new can also take the formula directly, not just an array
    # of shape [x-coordinates, y-coordinates]
    plot.data << Gnuplot::DataSet.new(COORDINATES) do |ds|
      # You can also draw graph using variaous other types,
      # 'linespoints','steps','fillsteps', etc
      # See http://www.gnuplot.info/docs_5.4/Gnuplot_5_4.pdf 
      # for full docs
      ds.with = 'lines'
      ds.linewidth = 15
      # Change this to the name of your Valentine
      ds.title = 'My Valentine'  
    end
  end
end
```

## Result
![Valentine Day Graph plotted with lines](https://dev-to-uploads.s3.amazonaws.com/i/fin9qrwfsxbxjjeqhan9.jpg)

Here is an example of the plotted graph if we change `DISTANCE_BETWEEN_POINTS = 0.1` and `ds.with = 'steps'` and `ds.linewidth = 1`
![Valentine Day Graph plotted with steps](https://dev-to-uploads.s3.amazonaws.com/i/xb4lw3xcy88hw9qbmnzy.png)

## Bonus

PDF we generated above is perfect for printing, but if you wanted to convert it to an JPEG or PNG, you can use a graphics app like Photoshop or Gimp, or simply use imagemagick

```bash
# Install it if you don't have it
brew install imagemagick
convert -quality 300 -density 300 valentine_day_plot.pdf valentine_day_plot.jpg
```