top of page
2022

FIGURE TAXONOMY

an alphabet of ballroom dance steps

The notation of movements of the body has been a topic actively explored since the 17th century. The project explores the opportunities in representing figures of smooth ballroom dance styles - the waltz, the tango, and the foxtrot, in three dimensions.

CONTEXT

Drawing ++

MIT Media Lab

p5js, Open Frameworks, Grasshopper, Rhinoceros3D, Photoshop, Premiere, Illustrator, 3D Printing

 

SUPERVISORS

Zach Liberman

 

SOFTWARE

DANCE NOTATION

a history

In the 1680s Pierre Beauchamp developed a dance notation, the Beauchamp-Feuillet notation, which was used to record Baroque dance styles.

The Smooth dance styles explored in this project are similarly a progressive dances, which means they are directionally moving dances made up of specific collections of steps.

How might one approach drawing ballroom dance in three-dimensions?

DEVELOPING A 3D NOTATION

a process

video isolation of leader and follower

in order to track both, only one can be tracked at a time

let _USE_LIVE_VIDEO = false;


let landmarks;


let W = 640;
let H = 360;

// https://mediapipe.dev/images/mobile/pose_tracking_full_body_landmarks.png
let connects = [0,15,13,11,12,14,16,23,24,25,26,27,28];


function onResults(results) {
  if (results.poseLandmarks && results.poseLandmarks.length){
    landmarks = results.poseLandmarks;
  }
}


const pose = new Pose({locateFile: (file) => {
  return `https://cdn.jsdelivr.net/npm/@mediapipe/pose/${file}`;
}});
pose.setOptions({
  modelComplexity: 1,
  smoothLandmarks: true,
  enableSegmentation: true,
  smoothSegmentation: true,
  minDetectionConfidence: 0.5,
  minTrackingConfidence: 0.5
});
pose.onResults(onResults);

 

let capture;


async function send(){
  if (capture.elt.width){
    await pose.send({image: capture.elt});
  }
  setTimeout(send,10);
}


let sel;
let tex=[];
let color = []
let tex_w = 10;

function draw3D() {
  noFill();
  for (let i =0; i< limbs.length; i++){
    Line[i].push([landmarks[limbs[i]].x*W,landmarks[limbs[i]].y*H,landmarks[limbs[i]].z*H]);
 
    texture(tex[i]);
    makeMesh(Line[i]);
    }
}

function setup() {
  pixelDensity(1);
  createCanvas(W,H,WEBGL);
 
  tex[0] = createGraphics(tex_w,tex_w);
    tex[0].stroke("green");
    tex[0].line(0,tex_w,tex_w,tex_w); 
 
  tex[1] = createGraphics(tex_w,tex_w);
    tex[1].stroke("red");
    tex[1].line(0,tex_w,tex_w,tex_w); 
 
  tex[2] = createGraphics(tex_w,tex_w);
    tex[2].stroke("yellow");
    tex[2].line(0,tex_w,tex_w,tex_w); 
 
  tex[3] = createGraphics(tex_w,tex_w);
    tex[3].stroke("blue");
    tex[3].line(0,tex_w,tex_w,tex_w); 
 
  if (_USE_LIVE_VIDEO){
    capture = createCapture({video:{mandatory:{maxWidth:W,maxHeight:H}},audio:false});
  }
  else {
    capture = createVideo("Foxtrot_Twinkle_Leader.mp4");
  capture.play();
  }
  capture.hide();
 
  setTimeout(send,10);

}

let trace = [];
let pos = [];
let USE_ORTHO = false;
let limbs = [15,16, 31, 32];
let Line = new Array(limbs.length).fill(0);
for (let i=0; i<limbs.length; ++i) Line[i] = [];


function draw() {
  background(220);
  translate(-W/2,-H/2);

  image(capture,0,0,W,H);
 
  if (landmarks){
//     fill(255,255,0);
//     noStroke();
//     landmarks.forEach(({x,y})=>circle(x*W,y*H,2));
    
    stroke(255);
    strokeWeight(2);
    noFill();
    
    draw3D();
  }
}

function clamp(a,b,c){
  return min(max(a,b),c);
}

function getSmoothed(poly,smoothingSize,smoothingShape=0){
  // https://github.com/openframeworks/openFrameworks/blob/c21aba181f5180a8f4c2e0bcbde541a643abecec/libs/openFrameworks/graphics/ofPolyline.inl#L470
  let n = poly.length;
  smoothingSize = clamp(smoothingSize, 0, n);
  smoothingShape = clamp(smoothingShape, 0, 1);
 
  // precompute weights and normalization
  let weights = new Array(smoothingSize);
  // side weights
  for(let i = 1; i < smoothingSize; i++) {
    let curWeight = map(i, 0, smoothingSize, 1, smoothingShape);
    weights[i] = curWeight;
  }
  // make a copy of this polyline
  let result = poly.map(xy=>[...xy]);
  let bClosed = false;
  for(let i = 0; i < n; i++) {
    let sum = 1; // center weight
    for(let j = 1; j < smoothingSize; j++) {
      let curx = 0;
      let cury = 0;
      let leftPosition = i - j;
      let rightPosition = i + j;
      if(leftPosition < 0 && bClosed) {
        leftPosition += n;
      }
      if(leftPosition >= 0) {
        curx += poly[leftPosition][0];
        cury += poly[leftPosition][1];
        sum += weights[j];
      }
      if(rightPosition >= n && bClosed) {
        rightPosition -= n;
      }
      if(rightPosition < n) {
        curx += poly[rightPosition][0];
        cury += poly[rightPosition][1];
        sum += weights[j];
      }
      result[i][0] += curx * weights[j];
      result[i][1] += cury * weights[j];
    }
    result[i][0] /= sum;
    result[i][1] /= sum;
  }
  return result;
}

function keyPressed() {
  if (keyCode === ENTER){
    for (let i =0; i< limbs.length; i++){
      Linev = lineV(Line[i]);
      download_file("line"+i+".csv", Linev.map(xyz=>xyz.join(",")).join("\r\n"));
    }
  }
}

function lineV (LineI)
{
  let Linev = [];
    for (let i = 1; i < LineI.length; i++){
      let p0 = LineI[i-1];
      let p1 = LineI[i];
      let d = Math.hypot(p0[0]-p1[0],p0[1]-p1[1],p0[2]-p1[2]);
      Linev.push([...p0,d]);
    }
  return Linev;
}

function download_file(pth,text){
  var element = document.createElement('a');
  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
  element.setAttribute('download', pth);
  element.style.display = 'none';
  document.body.appendChild(element);
  element.click();
  document.body.removeChild(element);
}

function getResampledBySpacing(poly0,spacing) {
  if(spacing==0 || poly0.length == 0) return poly0;
  let poly = [];
  let acc_len = [0];
  let tot_len = 0;
  for (let i = 0; i < poly0.length-1; i++){
    let [x0,y0] = poly0[i];
    let [x1,y1] = poly0[i+1];
    tot_len += Math.hypot(x1-x0,y1-y0);
    acc_len.push(tot_len);
  }
  function getPointAtLength(l){
    for (let i = poly0.length-1; i >= 0; i--){
      if (acc_len[i] <= l){
        let t = (l - acc_len[i])/(acc_len[i+1] - acc_len[i]);
        return [
          poly0[i][0] * (1-t) + poly0[i+1][0] * t,
          poly0[i][1] * (1-t) + poly0[i+1][1] * t,
        ];
      }
    }
    return [0,0];
  }

  for (let f = 0; f < tot_len; f += spacing){
    poly.push(getPointAtLength(f));
  }
  if(poly.length) poly.push(poly0[poly0.length-1]);
  return poly;
}

videos are run through the code

after the video is played, four spreadsheets, one for each limb, are exported

spreadsheets input into grasshopper script to generate geometry

the spreadsheets are parsed and a curve is drawn from the points in the first three columns, the speeds in the fourth column are used as the radii for the pipe 

ViewCapture20221206_233408 2_edited.jpg

geometry backed into rhino

the rhino geometry can then be rendered or exported for 3D printing

THE WALTZ

an elegant, progressive dance

waltz mashup

waltz figure alphabet

underarm turn, side by side, reverse turn, hesitation with turn, grapevine, right box turn

waltz 3D model

THE TANGO

a sharp, progressive dance

tango mashup

tango figure alphabet

tango walk, rock step, medis corto, left rock step, la peurta, flare promenade

tango figure turntables

tango walk, la peurta, flare promenade

tango 3D model

THE FOXTROT

a smooth, progressive dance 

foxtrot figure alphabet

promenade, open imputus, base step, inside promenade, rock step, twinkle

foxtrot mashup

foxtrot 3D model

bottom of page