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()

