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);
}