// Flat tringle mesh with texture for image warping // Written by Jarek Rossignac June 2006 import processing.opengl.*; // comment out if not using OpenGL import java.lang.reflect.*; PImage myImage; // image used as tecture PImage myImage2; PFont curveFont; boolean anim = false; float globalTime =0; final int modeFirst =0; final int modeSecond =2; final int modeBoth = 1; final int modeWarp = 3; int mode = modeFirst; boolean twisterMode; boolean movie = false; RectTextButton createCurveButton; RectTextButton adjustCurveButton; RectTextButton twisterModeButton; final int doNothingMode =0; final int createCurveMode =1; final int adjustCurveMode =2; int editMode = doNothingMode; int curSelectedCurve = -1; int curAdjustCurve = -1; ArrayList curvesFirst = new ArrayList (); ArrayList curvesSecond = new ArrayList (); ArrayList curves = curvesFirst; ArrayList twisters = new ArrayList (); int curTwister = -1; pt constrainPt = new pt(0,0,0); int frameCounter; int n=50; // size of grid. Must be >2! pt[][] G = new pt [n][n]; // array of vertices pt[][] T = new pt [n][n]; pt[][] T2 = new pt[n][n]; int pi,pj; // indices of vertex being dragged when mouse is pressed pt Mouse = new pt(0,0,0); // current mouse position boolean showVertices=true, showEdges=false, showTexture=true; // flags for rendering vertices and edges color red = color(200, 10, 10), blue = color(10, 10, 200), green = color(10, 200, 20), black = color(10, 10, 10); float w,h,ww,hh; // width, height of cell in absolute and normalized units vec offset = new vec (0,0,0); // offset vector from mouse to clicked vertex boolean inverse = false; // constraints final int slots = 2; final int mc = 500; // max number of constraints int[] cn = new int [slots]; // number of constraints in each set int[][] I = new int [slots][mc]; // i coordiantes of saved constraints int[][] J = new int [slots][mc]; // j coordiantes of saved constraints pt[][] C = new pt [slots][mc]; // constrainted location int m=0; // current set of constraints pt[][] textureSecond = new pt[n][n]; int corrNum =0; int[] corrI = new int[mc]; int[] corrJ = new int[mc]; pt[] corrCstart = new pt[mc]; pt[] corrCend = new pt[mc]; final int rightBorder = 100; void restoreConstraints () { resetVertices (); pinBorder(); for (int k=0; k 1.0 ) { if (anim ) prepareAnim (); } if ( anim ) if ( movie ) animMorph (globalTime,5); else animMorph (globalTime,1); else filterConstrains (1); Mouse.setToMouse(); // not right border if (mousePressed && editMode == doNothingMode && Mouse.x < myImage.width) { G[pi][pj].setTo(Mouse); G[pi][pj].addVec(offset); }; if (showTexture) { paintImage (255); if ( anim) { if ( mode == modeSecond ) paintSecondImage (255); if ( mode == modeBoth ) paintSecondImage (128); if ( mode == modeWarp ) { paintSecondImage ( transformTime (globalTime / 1.2) * 255.0 ); if ( movie) saveFrame("frame" + frameCounter++ + ".png"); } } } if (showEdges) drawEdges(); if (showVertices) drawVertices(); if (showL) drawL(); if (showLL) drawLL(); if (!anim) drawCurves (); textFont(curveFont,14); stroke(255); fill(255); textAlign(CENTER); String s; if ( m == 0 ) s = "Source"; else s = "Destination"; text (s , myImage.width + rightBorder /2 , height /2); if ( mode == modeFirst ) s = "First\nimage"; else if ( mode == modeSecond ) s = "Second\nimage"; else if ( mode == modeBoth ) s = "Both\noverlayed"; else s = "Warping"; text (s , myImage.width + rightBorder /2 , height /2 + 30); } else { // draw the image textureMode(NORMALIZED); // texture parameters in [0,1]x[0,1] noStroke(); fill(255); if (anim) { globalTime += inverse ? (-1.0/60.0) : (1.0/60.0); if ( !inverse && globalTime > 1.0 ) { globalTime = 0.0; frameCounter =0; } if ( inverse && globalTime < 0.0 ) { globalTime = 1.0; frameCounter =0; } twisterAnim (globalTime); } for (int i=0; i 0) { b.getLastPoint().p.setFromMouse(); b.adjustLastDir (); } } if (mouseButton == RIGHT || (keyPressed && key == CODED && keyCode == CONTROL && mouseButton == LEFT) ) { BezierCurve b = ((BezierCurve) curves.get(curves.size()-1)); BezierControlPoint bcp = b.getLastPoint(); pt cur = new pt(0,0,0); cur.setFromMouse(); cur.subPt (constrainPt); float change = cur.x + cur.y; if ( keyPressed == true && ( keyCode != CONTROL) ) { bcp.sleft = bcp.sright = bcp.sleft + change / 2; } else { float c = cos ( change / 180.0 * PI ); float s = sin ( change / 180.0 * PI ); pt dir = bcp.dir.make(); bcp.dir.x = c * dir.x - s * dir.y; bcp.dir.y = s * dir.x + c * dir.y; } constrainPt.setFromMouse (); } } if (editMode == adjustCurveMode && curAdjustCurve >= 0 ) { BezierCurve b = ((BezierCurve) curves.get(curAdjustCurve)); b.handleDragEvent( ); } } else { if ( curTwister >= 0 ) ((Twister) twisters.get(curTwister)).handleDragEvent (); } } void mouseReleased() { if ( !twisterMode ) { // unpin vertex if ctrl is pressed when mouse released if (keyPressed) if (key==CODED) if (keyCode==CONTROL) pinned[pi][pj]=false; snapToGrid ( G[pi][pj] ); if (editMode == adjustCurveMode && curAdjustCurve >= 0) { BezierCurve b = ((BezierCurve) curves.get(curAdjustCurve)); if (b.handleReleaseEvent () ) { curves.remove(curAdjustCurve); curAdjustCurve = -1; } } curAdjustCurve = -1; } else { if ( curTwister >= 0 ) ((Twister) twisters.get(curTwister)).handleReleaseEvent (); curTwister = -1; } }; void readFromFile() { String[] strs = loadStrings("data.pts"); if (Array.getLength(strs) == 0 ) return; ArrayList a = new ArrayList(); for ( int i=0; i < Array.getLength(strs); ++i ) a.add ( strs[i] ); for ( int i =0; i < slots; ++i) { cn[i] = Integer.parseInt ( (String) a.remove(0) ); for ( int j =0; j < cn[i]; ++j ) { I[i][j] = Integer.parseInt ( (String) a.remove(0) ); J[i][j] = Integer.parseInt ( (String) a.remove(0) ); C[i][j].x = Float.parseFloat ( (String) a.remove(0) ); C[i][j].y = Float.parseFloat ( (String) a.remove(0) ); } } curvesFirst.clear(); curvesSecond.clear (); int num = Integer.parseInt ( (String) a.remove(0)); for ( int i =0 ; i < num; ++i ) { BezierCurve bc = new BezierCurve (); bc.readFrom (a); curvesFirst.add (bc); } num = Integer.parseInt ( (String) a.remove(0)); for ( int i =0 ; i < num; ++i ) { BezierCurve bc = new BezierCurve (); bc.readFrom (a); curvesSecond.add (bc); } num = Integer.parseInt ( (String) a.remove(0)); for ( int i =0; i < num; ++i ) { Twister t = new Twister (); t.readFrom (a); twisters.add (t); } restoreConstraints (); } void writeToFile() { ArrayList a = new ArrayList(); // save I,J,C,cn for ( int i =0 ; i < slots; ++i ) { a.add ( "" + cn[i] ); for ( int j =0; j < cn[i]; ++j ) { a.add ( "" + I[i][j] ); a.add ( "" + J[i][j] ); a.add ( "" + C[i][j].x ); a.add ( "" + C[i][j].y ); } } a.add ( "" + curvesFirst.size() ); for ( int i =0, sz = curvesFirst.size(); i < sz; ++i ) ((BezierCurve) curvesFirst.get(i) ).saveTo (a); a.add ( "" + curvesSecond.size() ); for ( int i =0, sz = curvesSecond.size(); i < sz; ++i ) ((BezierCurve) curvesSecond.get(i) ).saveTo (a); a.add ( "" + twisters.size () ); for ( int i =0, sz = twisters.size(); i < sz; ++i ) ((Twister) twisters.get(i) ).saveTo (a); String[] strs = new String[a.size() ]; for ( int i =0; i < a.size(); ++i ) strs[i] = (String) a.get(i); saveStrings ("data.pts", strs ); }