Hearty maths with calculang ❤️

calculang
mathsart
Author
Published

August 3, 2023

Few models I’ve worked on in my life have been lovelier than the ❤️ visual I made for my Twitter followers on Valentines day.

The missing interaction is finally below, along with some help about how it works. I did my best to choose somewhat self-explanatory input names 🙂

Visual Spec
embed(
  calcuvizspec({
    models: [beating],
    input_cursors: [{pinchiness_in, radius_in, waviness_in, tallness_in, use_wave_override_in: false}],
    mark: 'point',
    encodings: {
      x: {'name': 'x_in', type:'quantitative', domain: _.range(-6,6,0.05)},
      y: {'name': 'heart', type: 'quantitative' },
      color: {'name': 'x_in', type:'nominal', domain: _.range(-6,6,0.05), legend: false},
    },
    width: 300, height:250
}))

.

.

How it works

If you prefer video format, check my video on my YouTube channel about breaking (mathematical) hearts with calculang 😊

The visual is a scatterplot of a heart formula against a range of different x_in input values from -6 to 6.

The calculang formula for heart is:

heart = () =>  trend()   +   semi_circle() * wave() * tallness()

There are 2 terms being added here:

  • semi_circle() * wave() * tallness(): the components of this term are discussed below
  • trend(): effectively a base or ‘spine’ of the heart shape

visually 📊

Let’s first visualize trend or the base or ‘spine’:

Visual Spec
embed(
  calcuvizspec({
    models: [beating],
    input_cursors: [{pinchiness_in, radius_in, waviness_in, tallness_in, use_wave_override_in: false}],
    mark: 'line',
    encodings: {
      x: {'name': 'x_in', type:'quantitative', domain: _.range(-6,6,0.05)},
      y: {'name': 'value', type: 'quantitative' },
      row: {'name': 'formula', type:'nominal', domain: ['trend']},
      color: {'name': 'formula', type:'nominal', domain: ['trend'], legend: false},
    },
    width: 300, height:50
}))

Now let’s look at the components of the term that is added to trend; recall that these are multipled together (they are factors):

Visual Spec
embed(
  calcuvizspec({
    models: [beating],
    input_cursors: [{pinchiness_in, radius_in, waviness_in, tallness_in, use_wave_override_in: false}],
    mark: 'line',
    encodings: {
      x: {'name': 'x_in', type:'quantitative', domain: _.range(-6,6,0.05)},
      y: {'name': 'value', type: 'quantitative' },
      row: {'name': 'formula', type:'nominal', domain: ['semi_circle','wave', 'tallness']},
      color: {'name': 'formula', type:'nominal', domain: ['semi_circle','wave', 'tallness'], legend: false},
    },
    width: 300, height:50
}))

It’s value moves smoothly between -1 and 1 (inclusive); this adds noise that makes the points scatter across the shape of the heart.

To make it easier to visualise how this works, we can remove the noise.

In the following wave is no longer a sine wave: it’s a constant -1 or +1 representing the limits of the sine wave. I’m also showing 0 so we can see that the result is = trend in this case:

Visual Spec
embed(
  calcuvizspec({
    models: [beating],
    input_cursors: [{pinchiness_in, radius_in, waviness_in, tallness_in, use_wave_override_in: true}],
    mark: 'line',
    encodings: {
      x: {'name': 'x_in', type:'quantitative', domain: _.range(-6,6,0.05)},
      y: {'name': 'value', type: 'quantitative' },
      column: {'name': 'formula', type:'nominal', domain: ['heart','wave','trend'], legend:true},
      color: {name: 'wave_override_in', type: 'nominal', domain: [-1,1,0]},
    },
    width: 150, height:150
}))
Visual Spec
embed(
  calcuvizspec({
    models: [beating],
    input_cursors: [{pinchiness_in, radius_in, waviness_in, tallness_in, use_wave_override_in: false}],
    mark: { type: 'line', stroke: 0.1, point: {size: 3, filled: false} },
    encodings: {
      x: {'name': 'x_in', type:'quantitative', domain: _.range(-6,6,0.05)},
      y: {'name': 'value', type: 'quantitative' },
      column: {'name': 'formula', type:'nominal', domain: ['heart','trend','wave'], legend: false},
    },
    width: 150, height:150
}))

The heart visual here is the same (mathematically) as the one at the top 😀

.

.

credits

This blog was made with calculang, a language for calculations for transparency, education and certainty about numbers and maths, and maths art 🎨

Tip

You can find the calculang model source code by opening Developer Tools (Ctrl+Shift+I) and navigating to the .cul.js file (Ctrl+P and search .cul.js).

This example is inspired by heart functions I found written in Desmos - which has lots of awesome maths art 💚, I used this one as a reference.

For feedback you can contact me; or for issues/suggestions please use “Report an Issue” below ↓

|