We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexProgramming Questions & HelpSyntax Questions › confusion with transformation
Page Index Toggle Pages: 1
confusion with transformation (Read 1400 times)
confusion with transformation
Mar 20th, 2010, 4:08pm
 
Hi all:

I'm trying to understand transformations, but I have a question. Consider the following code:

Code:
void setup()
{
size(600,600);
background(255);

pushMatrix();
translate(width/2, height/2);
rotate(radians(32));

noFill();
ellipse(0,0,10,10);
line(0,0,200,0);
ellipse(200,0,10,10);

popMatrix();
}


The first point of the line is at the translated (0,0), and the second is at the translated (200,0), creating a nicely rotated diagonal line. Perfect.

But what if I want to know where the second point is located within the *parent* matrix--in this case something very close to (471,407)? Is there a way to pass the translated coordinates of an object from a child matrix to its parent?

This is more difficult to explain than I'd expected. Does the question at least make sense?
Re: confusion with transformation
Reply #1 - Mar 20th, 2010, 4:34pm
 
Related question: is it possible to reverse-translate mouseX and mouseY?

Here's my basic scenario: I'm building a graphing application, and my graphing coordinate system should be centered on the window. So during setup() I translate the whole matrix to (width/2,height/2). Easy enough.

Now I want to instantiate a complex shape centered on arbitrary coordinates on said graph, and rotated an arbitrary amount. So I pushMatrix(), use translate/rotate, and then draw my complex shape from the new origin. Also great!

Now I want to be able to click/drag the object. Hmm, this is where it's tricky. My mouse coordinates seem to be absolute, so the two nested matrixes that define my complex shape do not transform my mouse position Sad

Any ideas?
Re: confusion with transformation
Reply #2 - Mar 20th, 2010, 6:08pm
 
Quote:
Now I want to be able to click/drag the object.


My suggestion: After you've done the transformations to get to the objects position, use screenX and screenY to get the corresponding position on screen. So, in the case you'd do

 translate(200,0);
 float x=screenX(0,0);
 float y=screenY(0,0);
 println("X:"+x+" Y:"+y);

you'd get x=200 and y=0.

So now you can find the positions and therefore select objects by comparing their screen-projected geometry to the cursor pos. I do that by using this code in case of an array of points:
Code:
// Returns the point which is closest to the cursor
 // and closest to the camera (Z-distance).
 PVector nearCursor(PVector[] a) {
   pushMatrix();
//Do necessary transformations to reach the object space
//or maybe restore a stored matrix.
   float sx,sy,sz;
   float pd=1000000; //Random Initial Value
   PVector selected=null;
   for (int i=0, n=a.length; i < n; i++) {
     PVector tp=a[i];
     sx=screenX(tp.x,tp.y,tp.z);
     sy=screenY(tp.x,tp.y,tp.z);
     sz=screenZ(tp.x,tp.y,tp.z);
     if (mouseX>sx-8 && mouseX<sx+8 && mouseY>sy-8 && mouseY<sy+8 && sz<pd) {
       pd=sz;
       selected=tp;
     }
   }
   popMatrix();
   return(selected);
 }



Now to drag you have to have one pair of coords at selection time and a target pair, updated as the mouse is dragged. You can use modelX(), modelY() and modelZ() to translate screen positions back to model coordinates. To tell you the truth, this gets me confused all the time, but I think for 2D it is straightforward.

Hope it is a start.
Re: confusion with transformation
Reply #3 - Mar 21st, 2010, 6:32am
 
yconst: Beautiful. Thank you SO MUCH for your thorough answer. ScreenX, ModelX, and PVector are all new to me, and exactly what I needed. Thanks a ton!
Re: confusion with transformation
Reply #4 - Mar 21st, 2010, 9:18am
 
Yes...screenX looks really useful. I was using a rotation matrix manually to get the screen coordinates back after rotation!!!
Re: confusion with transformation
Reply #5 - Mar 21st, 2010, 11:23am
 
AdamTheNoob wrote on Mar 20th, 2010, 4:34pm:
Related question: is it possible to reverse-translate mouseX and mouseY

A comment on this specific question:

I think in general the answer is no.  

In 3D, it's not possible, because a pixel doesn't correspond to a specific x,y,z location--rather, it corresponds to a line (ray) of points extending outwards from the camera.  Since there's no way to specify depth with the mouse (at least until 3D mice become standardized and popular) there's no generic way to go from mouse coordinates to a location in the modelview.

In 2D, it should be possible in theory, but I haven't seen any easy way to do it.  Whenever I've run into this issue, I've always either found a workaround (like yconsts' code above--instead of translating the mouse to model coordinates, translate the things you might want to select into screen coordinates), or tracked all my transformations "manually" with global variables like currentScaleFactor, currentXTranslation, or whatever and inverted the mouse position myself.
Re: confusion with transformation
Reply #6 - Mar 21st, 2010, 2:31pm
 
There is, however, a library that allows you to give 3D objects IDs as you draw them, and then find out later which object was clicked on.

Demo: http://www.openprocessing.org/visuals/?visualID=7881

Info: http://n.clavaud.free.fr/processing/picking-library/

Get the library: http://code.google.com/p/processing-picking-library/downloads/list
Re: confusion with transformation
Reply #7 - Mar 21st, 2010, 4:40pm
 
FYI, here's what I've got so far. Just mocked up; it will need to be streamlined a bit, especially since my end-goal is to have a whole array of these drag-able objects, but it's a start:

**NOTE: Updated since original posting:

Code:

PFont fontA;
PFont fontB;

grid grid1;
clickRect[] items = new clickRect[1];

void setup()
{
size(600,400); smooth();

fontA = loadFont("HelveticaNeue-Bold-48.vlw");
fontB = loadFont("HelveticaNeue-12.vlw");
textFont(fontB, 12);

grid1 = new grid(0,0,width-10,height-10);
items[0] = new clickRect(100, 150, 200, 100,32);
}

void draw()
{
background(255);
translate(width/2, height/2);

grid1.display();

for(int i=0; i<items.length; i++){
items[i].display();
}
}

void mousePressed()
{
for(int i=0; i<items.length; i++){
if(items[i].bounds(mouseX,mouseY)) {
items[i].lock();
}
}
}

void mouseDragged()
{
for(int i=0; i<items.length; i++){
if(items[i].hover) {
items[i].update(mouseX,mouseY);
}
}
}

void mouseReleased()
{
for(int i=0; i<items.length; i++){
items[i].unlock();
}
}

class grid {
float x;
float y;
float w;
float h;

grid(float xi,float yi,float wi,float hi) {
x = xi;
y = yi;
w = wi;
h = hi;
}

void display() {
rectMode(CENTER); stroke(150); noFill(); strokeWeight(1);
rect(x,y,w,h);

for(int i=0; i<=5; i++) {
stroke(245);
if(i==0) stroke(#ffb0b0);
line(x+(i*w/10), y-h/2, x+(i*w/10), y+h/2);
if(i!=0) line(x-(i*w/10), y-h/2, x-(i*w/10), y+h/2);

if(i==0) stroke(#b5ffbe);
line( x-w/2, y+(i*h/10), x+w/2, y+(i*h/10) );
if(i!=0) line(x-w/2, y-(i*h/10), x+w/2, y-(i*h/10));
}
}
}

class clickRect {
float mouseX1 = 0;
float mouseY1 = 0;

float x, y; // center X, center Y
float w, h; // width, height
float a; // angle (counter-clockwise from horiz)

float[] c = new float[8]; // will contain (x,y) for all four points

boolean locked = false;
boolean hover = false;

clickRect(float xi, float yi, float wi, float hi,float ai) {
x = xi; y = yi;
w = wi; h = hi;
a = radians(ai);
findCorners();
}

void display() {
if(bounds(mouseX,mouseY)) { fill(#ffe030,20); hover = true; } else { fill(0,20); hover = false; }
stroke(0); strokeWeight(2); rectMode(CENTER);
pushMatrix();
transform(); // move the object center into place
rect(0,0,w,h);
popMatrix();
}

void update(float xi, float yi) {
if(locked) {
xi = xi - mouseX1; yi = yi - mouseY1;
x = x + xi;
y = y - yi;
findCorners();
mouseX1 = mouseX; mouseY1 = mouseY;
}
}

void transform() {
translate(x,-y); rotate(a);
}

void findCorners() {
c[0] = -w/2; /* top-left X */ c[1] = -h/2; /* top-left Y */
c[2] = w/2; /* top-right X */ c[3] = -h/2; /* top-right Y */
c[4] = w/2; /* bottom-right X */ c[5] = h/2; /* bottom-right Y */
c[6] = w/2; /* bottom-left X */ c[7] = -h/2; /* bottom-left Y */
}

boolean bounds(float xi, float yi) {
pushMatrix();
transform(); // we'll be reversing the transforms manually for the mouse coordinates

// find out pixel coordinates of translated object center
float x2 = screenX(0,0);
float y2 = screenY(0,0);
popMatrix();

// input coordinates must be translated and rotated to match object coordinate system:
// see http://en.wikipedia.org/wiki/Rotation_(mathematics)
float xj = (xi-x2)*cos(-a) - (yi-y2)*sin(-a); // based on x' = x * cos(a) - y * sin(a)
float yj = (xi-x2)*sin(-a) + (yi-y2)*cos(-a); // based on y' = x * sing(a) + y * cos(a)

// since inputs are now oriented to object, it's easy to find out if input is inside the bounding box
if( xj < c[2] && xj > c[0] && yj > c[1] && yj < c[5]) return true;
else return false;
}

void lock() { locked = true; mouseX1 = mouseX; mouseY1 = mouseY; }
void unlock() { locked = false; }

}

Re: confusion with transformation
Reply #8 - Mar 21st, 2010, 5:26pm
 
Nice one.

Just a hint: If you want to drag just one object, you an simplify your mouseDragged() so that you use a temporary 'locked' container that points to the locked object instead of a boolean. This way you can just do:

Code:
if (locked!=null) {
locked.update(mouseX,mouseY);
}


and then accordingly set and re-set(=null) that during mousePressed() and mouseReleased()
Re: confusion with transformation
Reply #9 - Mar 21st, 2010, 7:40pm
 
Good call, yconst. I've already updated it to allow multiple drag-enabled objects, and now I'm working on a collision-detection system. I'll be sure to get your advice!

Thanks again for all your help!
Page Index Toggle Pages: 1