Dealing with AffineTransform mouse driven rotation

Hi there,

I'm implementing the mouse control to navigate through an image representing a map. Pan and zoom where trivial operations, but I'm getting in trouble when trying to rotate the BufferedImage.

I actually can perform the rotation, but because the map coordinate system gets rotated too. I have applied a trigonometric correction when performing a pan after a rotation. This was the hard part... my problem is that I have inverted the axis when panning,so dragging the mouse to the bottom of the canvas becomes a horizontal translation if we had a rotation by PI rads.

Because original Java code is pretty big, I have coded a simple example that pans, zooms or rotates a square.

The magic happens here:

def performPan(self,diff):

xa = diff.x*lang.Math.cos(self.theta)+diff.y*lang.Math.sin(self.theta)

ya = -diff.x*lang.Math.sin(self.theta)+diff.y*lang.Math.cos(self.theta)

diff.x -= (diff.x - xa)

diff.y -= (diff.y - ya)

center = self.canvas.squareCenter()

#if self.theta != 0: self.transform.rotate(-self.theta, center.x, center.y)

self.transform.translate(diff.x, diff.y)

#if self.theta != 0: self.transform.rotate(self.theta, center.x, center.y)

def performZoom(self,diff):

zoomLevel = 1.0+0.01*diff.y;

if zoomLevel <= 0:

zoomLevel = 0

center = self.canvas.windowCenter()

self.transform.scale(zoomLevel, zoomLevel)

def performRotation(self,diff):

angleStep = diff.y * 0.1

self.theta += diff.y

self.theta %= 2*lang.Math.PI

center = self.canvas.squareCenter()

self.transform.rotate(angleStep, center.x, center.y)

def toWindowCoordinates(self,diff):

try:

self.transform.inverseTransform(diff,diff)

except:

print"error en window coordinates"

I have already tried changing diff.x and diff.x sign, and commenting that trigonometric correction and uncommeting the lines concatenating two rotations surrounding the translation without sucess.

Please, I'd appreciate some feedback. Brainstorms are welcome. :-)

Thanks, Vicente.

The full code:

from javaximport swing

from javaimport awt, lang;

class Listener(swing.event.MouseInputAdapter):

def __init__(self,subject):

self.offset = awt.geom.Point2D.Double()

self.anchor = awt.geom.Point2D.Double()

self.canvas = subject

self.transform = subject.transform

self.rectangle = subject.rectangle

self.theta = 0.0

def mousePressed(self,e):

self.anchor.x = e.getX()

self.anchor.y = e.getY()

self.offset.x = e.getX()

self.offset.y = e.getY()

def mouseDragged(self,e):

self.offset.x = e.getX()

self.offset.y = e.getY()

diff = awt.geom.Point2D.Double()

tx = self.offset.x - self.anchor.x

ty = self.offset.y - self.anchor.y

diff.x = tx

diff.y = ty

self.anchor.x = self.offset.x

self.anchor.y = self.offset.y

class Painter(lang.Runnable):

def __init__(self,canvas, listener):

self.canvas = canvas

self.listener = listener

def run(self):

if e.isControlDown():

self.listener.performRotation(diff)

elif swing.SwingUtilities.isLeftMouseButton(e):

self.listener.performPan(diff)

if swing.SwingUtilities.isRightMouseButton(e):

self.listener.performZoom(diff)

self.canvas.repaint()

work = Painter(self.canvas, self)

swing.SwingUtilities.invokeLater(work)

def mouseReleased(self,e):

self.color = awt.Color.red

self.canvas.repaint()

def performPan(self,diff):

xa = diff.x*lang.Math.cos(self.theta)+diff.y*lang.Math.sin(self.theta)

ya = -diff.x*lang.Math.sin(self.theta)+diff.y*lang.Math.cos(self.theta)

diff.x -= (diff.x - xa)

diff.y -= (diff.y - ya)

center = self.canvas.squareCenter()

if self.theta != 0: self.transform.rotate(-self.theta, center.x, center.y)

self.transform.translate(diff.x, diff.y)

if self.theta != 0: self.transform.rotate(self.theta, center.x, center.y)

def performZoom(self,diff):

zoomLevel = 1.0+0.01*diff.y;

if zoomLevel <= 0:

zoomLevel = 0

center = self.canvas.windowCenter()

self.transform.scale(zoomLevel, zoomLevel)

def performRotation(self,diff):

angleStep = diff.y * 0.1

self.theta += diff.y

self.theta %= 2*lang.Math.PI

center = self.canvas.squareCenter()

self.transform.rotate(angleStep, center.x, center.y)

def toWindowCoordinates(self,diff):

try:

self.transform.inverseTransform(diff,diff)

except:

print"error en window coordinates"

class Canvas(swing.JPanel):

def __init__(self):

self.rectangle = awt.geom.Rectangle2D.Double(0,0,50,50)

self.transform = awt.geom.AffineTransform()

self.wcenter = awt.geom.Point2D.Double()

self.rcenter = awt.geom.Point2D.Double()

listener = Listener(self)

swing.JPanel.addMouseMotionListener(self,listener)

swing.JPanel.addMouseListener(self,listener)

def paintComponent(self,g2d):

self.super__paintComponent(g2d)

g2d.setTransform(self.transform)

g2d.fill(self.rectangle)

def windowCenter(self):

if self.wcenter.x == 0 or self.wcenter.y == 0:

self.wcenter.x = self.getHeight()/2.0

self.wcenter.y = self.getWidth()/2.0

return self.wcenter

def squareCenter(self):

if self.rcenter.x == 0 or self.rcenter.y == 0:

self.rcenter.x = self.rectangle.getBounds2D().height/2.0

self.rcenter.y = self.rectangle.getBounds2D().width/2.0

return self.rcenter

frame = swing.JFrame(title="test",

visible=1,

defaultCloseOperation = swing.JFrame.EXIT_ON_CLOSE,

preferredSize = awt.Dimension(400,400),

maximumSize = awt.Dimension(800,600),

minimumSize = awt.Dimension(200,200),

size = awt.Dimension(500,500)

)

frame.add(Canvas(), awt.BorderLayout.CENTER)

frame.pack()

[6784 byte] By [vicente.reiga] at [2007-11-27 5:30:07]
# 1
Sorry I can't help you, but just a question. It almost looks like you're mixing java and python code. Is this Jython?
petes1234a at 2007-7-12 14:53:52 > top of Java-index,Desktop,Core GUI APIs...
# 2
I forgot to mention that the example is written in Jython, because the Java was pretty big, but it is legible bu a Java programmer. :-)
vicente.reiga at 2007-7-12 14:53:52 > top of Java-index,Desktop,Core GUI APIs...
# 3

> I forgot to mention that the example is written in

> Jython, because the Java was pretty big, but it is

> legible bu a Java programmer. :-)

It's legible, but most of us w/out a jython compiler would have to re-write the code if we wanted to try it out. That may hurt your chances of getting a useful response (as opposed to my useless responses). ... Or it might not.

Good luck!

/Pete

petes1234a at 2007-7-12 14:53:52 > top of Java-index,Desktop,Core GUI APIs...