Mapping the mouse to an affinetransformed shape.

Greetings,

I am writing a drawing tool and using affine transform to place my shapes. The problem is, I do not know how to map the cursor such that it's coordinate space is the same as my shapes once I have transformed them. Can anyone help me?

Below is a simplified example. If you compile and run, you will notice that the mouse thinks it's on the graphics when its in the upper left corner of the Panel. That is because the default graphics context is in the upper left corner of the screen, while the shape's bounds provide a the coordinates relative to their universal center, which is the affine transformed location.

Method mousemoved has the handler code, and you can see what I am doing to test location match.

In the actual application, I have levels of transformations, because shapes are children/dependent on other shapes, and must move relative to other shapes. etc.

Thanks in Advance for even looking at this problem.

Any questions, please feel free to contact me at richard.cromer@itt.com. No spam please!

import javax.swing.*;

import javax.swing.event.*;

import java.awt.*;

import java.awt.event.*;

import java.awt.geom.*;

import java.util.ArrayList;

public class RolloverTest

{

public static void main(String[] args){

new RolloverTest();

}

public RolloverTest(){

shapes = new ArrayList();

//

// Notice this ellipse is at 0, 0. The top left hand corner of default graphics context.

// The shape size is completely arbitrary.

Ellipse2D oval1 = new Ellipse2D.Double(0, 0, 90, 40);

shapes.add(new HotShape(oval1, mouseOff));

//

// Notice this rectangle object is at 20, 0. Twenty pixels right of the top left hand corner of default graphics context.

// The shape size is completely arbitrary.

Rectangle rect1 = new Rectangle(20, 0, 50, 50);

shapes.add(new HotShape(rect1, mouseOff));

hotPiece = (HotShape)shapes.get(0);

drawingBoard = new DrawingBoard(this);

frame = new JFrame("Rollover Test");

frame.setContentPane(drawingBoard);

frame.setSize(300, 300);

frame.setLocationRelativeTo(null);

frame.setVisible(true);

}

public class DrawingBoard extends JPanel implements MouseMotionListener{

public DrawingBoard(RolloverTest rt){

this.rt = rt;

this.setBackground(Color.WHITE);

this.addMouseMotionListener(this);

}

public void paintComponent(Graphics g){

super.paintComponent(g);

Graphics2D gfx = (Graphics2D)g;

gfx.setStroke(new BasicStroke(3));

HotShape shape;

AffineTransform at;

// First transform moves the origin down and right.

at = AffineTransform.getTranslateInstance(80,80);

gfx.transform(at);

shape = (HotShape)rt.shapes.get(0);

gfx.setColor(shape.color);

gfx.setClip(null);

gfx.draw(shape.shape);

//

// second transform moves the origin down and right some more.

at = AffineTransform.getTranslateInstance(100,100);

gfx.transform(at);

shape = (HotShape)rt.shapes.get(1);

gfx.setColor(shape.color);

gfx.setClip(null);

gfx.draw(shape.shape);

}

public void mouseMoved(MouseEvent evt)

{

Rectangle rect = new Rectangle(evt.getX(), evt.getY(), 1, 1);

for(int i = 0; i < rt.shapes.size(); i++)

{

HotShape shape = (HotShape)rt.shapes.get(i);

if (shape.shape.intersects(rect)){

rt.hotPiece.color = rt.mouseOff;

rt.hotPiece = shape;

shape.color = rt.mouseOn;

this.repaint();

}

else

{

if (shape.color == rt.mouseOn)

{

rt.hotPiece.color = rt.mouseOff;

shape.color = rt.mouseOff;

this.repaint();

}

}

}

}

public void mouseDragged(MouseEvent e) {

}

RolloverTest rt;

}

public class HotShape{

public HotShape(Shape shape, Color color){

this.shape = shape;

this.color = color;

}

Shape shape;

Color color;

}

JFrame frame;

DrawingBoard drawingBoard;

ArrayList shapes;

HotShape hotPiece;

Color mouseOff = Color.BLUE;

Color mouseOn = Color.RED;

}

[4308 byte] By [Richard_Cromera] at [2007-11-27 4:00:01]
# 1

Basically you are using AffineTransform on shapes to show them at different places but for determining intersection you are using just geometry of the shapes.

Say in your case you have a shape Ellipse2D oval1 = new Ellipse2D.Double(0, 0, 90, 40);

now it starts from 0,0 but by affine transformation you are showing it somewhere 150,200 instead of 0,0.

But for intersection purpose you are still using its start point as 0,0.

I hope its clear now !.just in case you are still not sure how to do it,than reply back.Will write a small utility for you.

khangharotha at 2007-7-12 9:04:36 > top of Java-index,Security,Cryptography...
# 2

You will need to create a way to save the transform for each shape so that you can use them

in your mouse code. Since you are already using a data store class that is one place that

you could keep the transform. Try this

import java.awt.*;

import java.awt.event.*;

import java.awt.geom.*;

import java.util.ArrayList;

import javax.swing.*;

import javax.swing.event.*;

public class RolloverTestRx {

public static void main(String[] args) {

new RolloverTestRx();

}

public RolloverTestRx() {

shapes = new ArrayList();

//shapes = new ArrayList<HotShape>(); // generics declaration

//

// Notice this ellipse is at 0, 0. The top left hand corner of

// default graphics context. The shape size is completely arbitrary.

Ellipse2D oval1 = new Ellipse2D.Double(0, 0, 90, 40);

shapes.add(new HotShape(oval1, mouseOff));

//

// Notice this rectangle object is at 20, 0. Twenty pixels right of

// the top left hand corner of default graphics context.

// The shape size is completely arbitrary.

Rectangle rect1 = new Rectangle(20, 0, 50, 50);

shapes.add(new HotShape(rect1, mouseOff));

hotPiece = (HotShape)shapes.get(0);

drawingBoard = new DrawingBoard(this);

frame = new JFrame("Rollover Test");

frame.setContentPane(drawingBoard);

frame.setSize(300, 300);

frame.setLocationRelativeTo(null);

frame.setVisible(true);

}

public class DrawingBoard extends JPanel implements MouseMotionListener {

public DrawingBoard(RolloverTestRx rt) {

this.rt = rt;

this.setBackground(Color.WHITE);

this.addMouseMotionListener(this);

}

protected void paintComponent(Graphics g) {

super.paintComponent(g);

Graphics2D gfx = (Graphics2D)g;

gfx.setStroke(new BasicStroke(3));

HotShape shape;

AffineTransform at;

// Save the accumulated/ongoing transform.

AffineTransform total = new AffineTransform();

// First transform moves the origin down and right.

at = AffineTransform.getTranslateInstance(80,80);

if(total.isIdentity())

total.setTransform(at);// initialize

shape = (HotShape)rt.shapes.get(0);

// Copy total to save for this HotShape.

// Otherwise the next concatenation will alter

// this HotShapes transform.

shape.draw(gfx, new AffineTransform(total));

//

// second transform moves the origin down and right some more.

at = AffineTransform.getTranslateInstance(100,100);

total.concatenate(at);// update

shape = (HotShape)rt.shapes.get(1);

shape.draw(gfx, new AffineTransform(total)); // send copy

}

public void mouseMoved(MouseEvent evt) {

Rectangle rect = new Rectangle(evt.getX(), evt.getY(), 1, 1);

for(int i = 0; i < rt.shapes.size(); i++) {

HotShape shape = (HotShape)rt.shapes.get(i);

if (shape.intersects(rect)) {

rt.hotPiece.color = rt.mouseOff;

rt.hotPiece = shape;

shape.color = rt.mouseOn;

this.repaint();

} else {

if (shape.color == rt.mouseOn) {

rt.hotPiece.color = rt.mouseOff;

shape.color = rt.mouseOff;

this.repaint();

}

}

}

}

public void mouseDragged(MouseEvent e) {}

RolloverTestRx rt;

}

public class HotShape {

public HotShape(Shape shape, Color color) {

this.shape = shape;

this.color = color;

}

public boolean intersects(Rectangle r) {

return at.createTransformedShape(shape).intersects(r);

}

public void draw(Graphics2D g2, AffineTransform at) {

this.at = at;

g2.setPaint(color);

g2.draw(at.createTransformedShape(shape));

}

Shape shape;

Color color;

AffineTransform at;

}

JFrame frame;

DrawingBoard drawingBoard;

ArrayList shapes;// for j2se 1.4- non-generic

//ArrayList<HotShape> shapes;// for j2se 1.5+ generics

HotShape hotPiece;

Color mouseOff = Color.BLUE;

Color mouseOn = Color.RED;

}

crwooda at 2007-7-12 9:04:36 > top of Java-index,Security,Cryptography...