warp.scala


// Initialize coordinate & texture indices arrays 
val num_rows = 24 
val num_cols = 24 
var x = make_array(0., imgh.asInstanceOf[Float]/(num_rows-1), 0., num_rows, num_cols); 
var y = make_array(0., 0., imgw.asInstanceOf[Float]/(num_cols-1), num_rows, num_cols); 
val u = make_array(0., imgh.asInstanceOf[Float]/(num_rows-1), 0., num_rows, num_cols); 
val v = make_array(0., 0., imgw.asInstanceOf[Float]/(num_cols-1), num_rows, num_cols); 
// Initialize image 
val imgw = 400 
val imgh = 400 
val img = loadImage("image.jpg") 
// Initialize button state 
var button_down = false 
 
// Setup 
size(400, 400,P3D) 
colorMode(RGB,255,255,255,100) 
 
def draw() { 
  background(100) 
  noStroke 
  fill(255,255,255) 
 
  // If button is down, distort the image's mesh 
  if (button_down) { 
    val sign = if (mouseButton == LEFT) 1. else -1. 
    val (nx, ny) = stretch(mouseX, mouseY, sign, x, y) 
    x = nx 
    y = ny 
  } 
  // otherwise move it back towards original 
  else { 
    val (nx, ny) = restore(x, y, u, v); 
    x = nx; 
    y = ny; 
  } 
 
  // draw the image 
  draw_image(img, x, y, u, v) 
} 
 
override def mousePressed() { 
  button_down = true 
} 
 
override def mouseReleased() { 
  button_down = false 
} 
 
// Create a 1D array [x, x+inc, x+2*inc, ...] 
def make_array(x: Float, inc: Float, n: Int): Array[Float] = { 
  if (n == 1) 
    Array(x) 
  else 
    Array.concat(Array(x), make_array(x+inc, inc, n-1)); 
} 
 
// create a 2D Array [x     , x     +cinc, x     +2*cinc, ...; 
//                    x+rinc, x+rinc+cinc, x+rinc+2*cinc, ...; 
//                    ... ] 
def make_array(x: Float, rinc: Float, cinc: Float, nr: Int, nc: Int): 
      Array[Array[Float]] = { 
  if (nr == 1) 
    Array(make_array(x,cinc,nc)); 
  else 
    Array.concat(Array(make_array(x,cinc,nc)), make_array(x+rinc, rinc, cinc, nr-1, nc)); 
} 
 
// map the image onto the coordinates 
def draw_image(img: PImage, 
               x: Array[Array[Float]], y: Array[Array[Float]], 
               u: Array[Array[Float]], v:Array[Array[Float]]) { 
  val nr = x.length 
  val nc = x(0).length 
  for (r <- 0 to nr-2) { 
    for (c <- 0 to nc-2) { 
      beginShape(QUAD) 
      texture(img) 
      vertex(x(r  )(c  ),y(r  )(c  ),0,u(r  )(c  ),v(r  )(c  )) 
      vertex(x(r+1)(c  ),y(r+1)(c  ),0,u(r+1)(c  ),v(r+1)(c  )) 
      vertex(x(r+1)(c+1),y(r+1)(c+1),0,u(r+1)(c+1),v(r+1)(c+1)) 
      vertex(x(r  )(c+1),y(r  )(c+1),0,u(r  )(c+1),v(r  )(c+1)) 
      endShape(CLOSE) 
    } 
  } 
} 
 
// Stretch coordinate arrays (x, y) with mouse location (mx, my) in direction sign 
def stretch(mx: Float, my: Float, sign: Float, x: Array[Array[Float]], y: Array[Array[Float]]): 
      (Array[Array[Float]], Array[Array[Float]]) = { 
  val nr = x.length 
  val nc = x(0).length 
  var nx = new Array[Array[Float]](nr,nc) 
  var ny = new Array[Array[Float]](nr,nc) 
 
  for ( r <- 0 to nr-1) { 
    for (c <- 0 to nc-1) { 
      val dx = mx - x(r)(c) 
      val dy = my - y(r)(c) 
      val len2 = dx*dx+dy*dy 
      val scale = sign*exp(-len2/1000.)/8. 
      nx(r)(c) = x(r)(c) + dx*scale 
      ny(r)(c) = y(r)(c) + dy*scale 
    } 
  } 
  (nx, ny); 
} 
 
// move (x, y) back towards (u, v) 
def restore(x: Array[Array[Float]], y: Array[Array[Float]], 
            u: Array[Array[Float]], v: Array[Array[Float]]): 
      (Array[Array[Float]], Array[Array[Float]]) = { 
  val nr = x.length 
  val nc = x(0).length 
  var nx = new Array[Array[Float]](nr,nc) 
  var ny = new Array[Array[Float]](nr,nc) 
  for ( r <- 0 to nr-1) { 
    for (c <- 0 to nc-1) { 
      val dx = u(r)(c) - x(r)(c) 
      val dy = v(r)(c) - y(r)(c) 
      val len2 = dx*dx+dy*dy 
      val scale = if (len2 > 0) (1.-exp(-len2/1000.))/sqrt(len2) else 0. 
      nx(r)(c) = x(r)(c) + dx*scale 
      ny(r)(c) = y(r)(c) + dy*scale 
    } 
  } 
  (nx, ny); 
}