Optical illusion: are these lines parallel or not?

Hello, I joined recently the forum.

I’ve decided to share some of my sketches which, I believe, some may find interesting.

From time to time, I like wandering the Internet to look for ideas on maths and arts. I came across this optical illusion in a Tumblr blog and I decided to make a sketch out of it. This is a gif with the result; you can find the code below to run the animation endlessly.

illusio_optica

main sketch
/*
Intent to reproduce the following optical illusion: 
https://szimmetria-airtemmizs.tumblr.com/post/170377417893/illusion-the-horizontal-lines-between-the
 
 The animation loops forever with a period set
 via the variable 'duration_secs'.
 
 The image can be divided into rows of squares,
 some of which move, while other don't.
 
 The visual part (render, colours, etc.) is managed
 by the class 'Row'; on the other hand, the dynamic
 of the mobile rows are carried out by the class 'DynSyst'.
 */
Row[] rows;
int q_rows;
int q_squares; // squares per row
float edge; // edge of the squares
float duration_secs; // duration of the animation, in secs
DynSyst sd;


void setup() {
  size(640, 480);
  //fullScreen();
  frameRate(34);

  //background(80);
  rectMode(CENTER); // important
  stroke(120);
  strokeWeight(2);

  duration_secs = 6; // 6 seconds
  edge = 80;
  q_rows = int(height / edge) + 1;
  q_squares = int(width / edge) + 5;


  float cx_ini = -2 * edge; // abscissa of all rows' position

  sd = new DynSyst(cx_ini, 3.f * edge / 4, duration_secs);
  rows = new Row[q_rows];
  for (int i = 0; i < rows.length; i++) {
    rows[i] = new Row(cx_ini, i * edge, edge, q_squares);
    rows[i].is_mobile = i % 2 == 0 ? false : true;
    rows[i].draw();
  }
}

void draw() {
  sd.iterate();
  for (int i = 0; i < rows.length; i++) {
    rows[i].update(sd.x);
    rows[i].draw();
  }
}
class for the motion bits
/*
Class representing a (discrete) simple harmonic motion (SHM).
 A simple harmonic motion is a sort of periodic motion,
 which is described by a sinusoidal function.
 
 This class will be using this one:
 x(t) = A * sin(w * t + a)
 
 Where:
 -A is the amplitude of the motion.
 -w is the angular velocity, calculated as w = 2 * PI / T.
 -T is the motion's period, in seconds.
 -a is the initial phase, which we'll take as 0.
 
 By <em>discrete</em>, it is meant that the time variable
 changes in a discrete manner, instead of continuously.
 In each iteration the time variable's value is incremented
 by a fixed value.
 
 This fixed value is related to the sketch's frameRate
 and guarantees that, if the dynamical system's 'iterate()'
 method is called once per frame, the animation has period T.
 */
class DynSyst {
  final float DELTA_T = 1.f / frameRate; // time increment per iteration
  private float cx; // center or equilibrium point of the SHM
  float x; // position at time t
  float amplitude;
  float period;
  float ang_vel;
  float t;


  DynSyst(float cx, float amplitude, float period_secs) {
    this.cx = x = cx;
    this.amplitude = amplitude;
    period = period_secs;
    ang_vel = 2 * PI / period;
    t = 0.f;
  }

  /*Increments dynamical system's time variable
   and returns the position at this new time.*/
  float iterate() {
    t += DELTA_T;
    if (t >= period) {
      t = 0.f;
    }
    x = cx + amplitude * sin(ang_vel * t);

    return x;
  }
}
class for the visual bits
/*
Class representing a row of squares with alternating colours.
 The row's position is the center of its first (left-most) square.
 */
class Row {
  private float cx_ini; // row's initial abscissa
  float cx, cy; // row's current position
  float edge;
  int q_squares; // quantity of squares
  color c1 = #ffffff, c2 = #000000; // colours to alternate between
  boolean is_mobile;


  /**/
  Row(float cx, float cy, float edge, int q_squares) {
    this.cx = cx_ini = cx;
    this.cy = cy;
    this.edge = edge <= 0.f ? 0.f : edge;
    this.q_squares = q_squares <= 0 ? 0 : q_squares;
    is_mobile = false;
  }

  void update(float cx) {
    if (is_mobile) {
      this.cx = cx;
    }
  }

  /*Draws this row alternating the colour of the squares.
  Assumes rectMode(CENTER) has been set.*/
  void draw() {
    color c;

    c = c1;
    for (int i = 0; i < q_squares; i++) {
      draw_square(cx + i * edge, cy, edge, c);
      c = c == c1 ? c2 : c1;
    }
  }

  /*Draws a square given its center, edge and colour.
   Assumes rectMode(CENTER) has been set.*/
  private void draw_square(float cx, float cy, float edge, color c) {
    fill(c);
    rect(cx, cy, edge, edge);
  }
}

Feel free to make any comments, suggestions or even post back your tinkers.

8 Likes