// A rotatable, scaleable, selectable image. class RotatableImage { PVector center; // the center of the image PVector scale; // the scale to draw the image // it is possible to have different x and y, // but both are the same for now. float rotation; // the rotation of the image in radians. PImage img; // The actual image. boolean cursorInside; // true if the cursor is inside the image. boolean rotateMe; // true if the cursor is in the rotate handle boolean resizeMeX; // true if the cursor is on the resize width handle boolean resizeMeY; // true if the cursor is on the resize height handle // default constructor // filename is relative path to data dir, full path on local machine or url RotatableImage( String filename ) { img = loadImage( filename ); scale = new PVector(1,1); cursorInside = false; rotateMe = false; resizeMeX = false; resizeMeY = false; setRandom( ); } // resize the image to something random, place somewhere random on the // screen and rotate to random direction // size is between 150 and 300 pixels // location is set to have a 100 pixels between image center and sides void setRandom( ) { float minSize = 150; float maxSize = 300; center = new PVector( random(100,width-100), random(100, height-100 )); float imgSize = random( minSize, maxSize); float scaleX = imgSize / img.width; if( img.height * scaleX < maxSize) { scale.x = scaleX; scale.y = scaleX; } else { scale.y = imgSize / img.height; scale.x = scale.y; } rotation = random( -HALF_PI, HALF_PI); } // draw an individual image. // put a black border around the image when inactive // when active, put a yellow border and a handle to show rotation void draw( ) { pushMatrix( ); translate( center.x, center.y); rotate( rotation ); float scaleWidth = img.width * scale.x; float scaleHeight = img.height * scale.y; fill( 0 ); stroke( 0 ); if( cursorInside ) stroke( 255, 255, 0 ); if( cursorInside ) fill( 255, 255, 0 ); rect( 0, 0, scaleWidth+ 3, scaleHeight+3 ); image( img, 0, 0, scaleWidth, scaleHeight); if( cursorInside ) { stroke( 255, 255, 0); fill( 205, 205, 0); line( 0, 0, 0, -scaleHeight*0.5); ellipse( 0, -(scaleHeight*0.5), 7,7); } popMatrix( ); } // compute the distance from pt to the line defined by // p1 to p2. Also compute the direction of the line float distanceToLine( PVector pt, PVector p1, PVector p2) { PVector A = new PVector(pt.x, pt.y); A.sub(p1); PVector B = new PVector( p2.x, p2.y); B.sub( p1); float t = B.dot(A) / (B.mag()*B.mag()); PVector C = new PVector( B.x, B.y); C.mult(t); C.add( p1); float distance = pt.dist( C); if( A.cross(B).z < 0) distance *= -1; return distance; } // compute the distance from point pt to the segment defined // by p1 to p2. float distanceToLineSegment( PVector pt, PVector p1, PVector p2) { PVector A = new PVector(pt.x, pt.y); A.sub(p1); PVector B = new PVector( p2.x, p2.y); B.sub( p1); float t = B.dot(A) / (B.mag()*B.mag()); t = constrain(t, 0, 1.0); PVector C = new PVector( B.x, B.y); C.mult(t); C.add( p1); float distance = pt.dist( C); if( A.cross(B).z < 0) distance *= -1; return distance; } // determines if a point defined by x,y is inside // the image. // sets or resets rotateMe and resizeMe{X,Y} boolean insideImage( int x, int y) { PVector pt = new PVector( x, y); pt.sub( center ); float scaleWidth = img.width * scale.x; float scaleHeight = img.height * scale.y; PVector vec = new PVector( scaleWidth, scaleHeight); float len = vec.mag( ); float angle1 = atan2( scaleHeight, scaleWidth); float angle2 = atan2( -scaleHeight, scaleWidth); PVector lowerRight = new PVector( cos( angle1 + rotation ) * len/2 , sin( angle1 + rotation ) * len/2); PVector upLeft = new PVector( cos( angle1 + rotation + PI) * len/2, sin( angle1 + rotation + PI ) * len/2 ); PVector lowerLeft = new PVector( cos( angle2 + rotation ) * len/2, sin( angle2 + rotation ) * len/2); PVector upRight = new PVector( cos( angle2 + rotation + PI) * len/2, sin( angle2 + rotation + PI) * len/2); PVector rotatePt = new PVector( cos( rotation - (2*PI/4.0)) * scaleHeight *0.5, sin( rotation - (2*PI/4.0)) * scaleHeight *0.5); float d1 = distanceToLine( pt, upLeft, upRight); float d2 = distanceToLine( pt, lowerLeft, upLeft); rotateMe= false; resizeMeX= false; resizeMeY= false; boolean found = false; if( d1 > 0 && d1 < scaleWidth && d2 > 0 && d2 < scaleHeight) found = true; if( found ) { float centerlineDist = pt.dist( rotatePt); if( centerlineDist < 5) rotateMe = true; else if( d1 < 10 || d1 > (scaleWidth -10)) resizeMeX = true; else if( d2 < 10 || d2 > (scaleHeight - 10)) resizeMeY = true; } return found; } // used to set the cursorInsde and the mouse cursor. boolean mouseMoved( ) { cursorInside = insideImage( mouseX, mouseY); if( cursorInside ) { if( rotateMe) cursor( rotateIcon, 16, 16 ); else if( resizeMeX || resizeMeY ) cursor( resizeIcon, 16, 16 ); else cursor( HAND ); } return cursorInside; } // used to set the cursorInside, assume the cursor is already set. boolean mousePressed( ) { cursorInside = insideImage( mouseX, mouseY); return cursorInside; } void mouseReleased( ) { cursor( ARROW ); } void mouseDragged( ) { if( rotateMe) { // use the cross product of the mouse delta vector with // the start point to image center to determine which // direction to rotate //PVector center = center; PVector v1 = new PVector( pmouseX - center.x, pmouseY - center.y); PVector v2 = new PVector( mouseX - center.x, mouseY - center.y); PVector v3 = new PVector( pmouseX - mouseX, pmouseY - mouseY); double angle = PVector.angleBetween( v1, v2); if( v1.cross( v3).z > 0) angle *= -1; rotation += angle; } else if( resizeMeX) { // try to resize based on grabbing the width of the image float delta = mouseX - pmouseX; if( mouseX > center.x) delta *=-1; float newScaleMult = 1.0 - ( delta / (img.width * scale.x)); scale.mult( newScaleMult ); if( (img.width * scale.x) < 100) { scale.x = 100.0 / img.width; scale.y =scale.x; } } else if( resizeMeY) { // try to resize based on grabbing the height of the image float delta = mouseY - pmouseY; if( mouseY > center.y) delta *=-1; float newScaleMult = 1.0 - ( delta / (img.height * scale.y)); scale.mult( newScaleMult ); if( (img.height * scale.y) < 100) { scale.y = 100.0 / img.height; scale.x = scale.y; } } else { // default case is to move the image PVector delta = new PVector( mouseX - pmouseX, mouseY - pmouseY); center.add( delta ); } } }