Creative Coding Blog

Maths, coding and art

Creating string art parabolic curves in Processing

One of the fascinating things about string art is how straight lines can appear to form various curves. The simplest examples are parabolic curves (aka quadratic curves). These can be described by an equation of the form:

y = a*x^2

How it works

To create a parabolic curve using pins and thread, we first create two lines of pins, equally spaced. In the picture we have created a horizontal lines of pins (numbered 1 to 5), and s second vertical line of pins (also numbered 1 to 5).

To create the curve, we join horizontal pin 1 to vertical pin 5, pin 2 to pin 4, pin 3 to pin 3 etc. In other words, we work along the horizontal row from start in increasing order, and work along the vertical row from the end in decreasing order, joining the pairs of pins.

Example

We will look at an example first, then look at the code used to create it.

The code

To draw the shape above, we need to write a couple of functions:

  • linePoints creates a line of (x, y) points
  • joinPoints takes two lines of points and joins each pair of points with a line

linePoints

Here is the code:

PVector[] linePoints(float ax, float ay, float bx, float by, int count)
{
  PVector[] points = new PVector[count];
  for (int i = 0; i < count; i++)
  {
    float x = ax + (bx - ax)*i/(count -1);
    float y = ay + (by - ay)*i/(count -1);
    points[i] = new PVector(x, y);
  }
  return points;
}

This takes five parameters:

  • the start point (ax, ay)
  • the end point (bx, by)
  • count - the total number of points

We will use the Processing class PVector to represent the points (or pins) in the image. A PVector stores an x and a y value.

In the code, first we create an array of PVector items, of length count. We then loop count times, calculating the x and y values for each point. We create a new PVector and place it in the array.

Finally we return the full array of points.

Notice that the calculation ensures that the first and last point are in the correct positions, with the other points equally spaced between then. Whatever values you choose for the start and end points, the remaining points will be equally distributed between them.

joinPoints

Here is the code:

void joinPoints(PVector[] p1, PVector[] p2)
{
  int count = p1.length;
  for (int i = 0; i < count; i++)
  {
    line(p1[i].x, p1[i].y, p2[i].x, p2[i].y);
  }
}

This function accepts two lists of points, p1 and p2. It draws a line from the first point in p1 to the first point in p2, then draws a line from the second point in p1 to the second point in p2, and so on.

Main draw function

Here is the main draw function:

void draw()
{
    clear();
    background(255);
    noFill();
    strokeWeight(1);
    
    stroke(255, 0, 0);
    PVector[] p1 = linePoints(50, 50, 550, 50, 30);
    PVector[] p2 = linePoints(50, 550, 50, 50, 30);
    joinPoints(p1, p2);
}

The corner of the image is at (50, 50).

We create p1, list of 30 points on a horizontal line from (50, 50) to (550, 50).

We then create p2, list of 30 points on a vertical line from (50, 550) back to (50, 50). This line is going in the opposite direct to the first line - it ends (50, 50) whereas the first line starts at (50, 50).

Finally we use joinPoints to join the points in the two lists.