Presentation is loading. Please wait.

Presentation is loading. Please wait.

1.00 Lecture 18 Geometric Transformations in the 2D API.

Similar presentations


Presentation on theme: "1.00 Lecture 18 Geometric Transformations in the 2D API."— Presentation transcript:

1 1.00 Lecture 18 Geometric Transformations in the 2D API

2 Transformed Coordinates in NgonApp

3 Pixel Coordinates in NgonApp static final float SCALE=200.0F; static final float tx = 1.5F; static final float ty = 1.5F; float transX( float x ) { return ( x + tx ) * SCALE; } float transY( float y ) { return ( y + ty ) * SCALE; }

4 Transformations in the Cardioid Grapher

5 Affine Transformations The 2D API provides strong support for affine transformations. An affine transformation maps 2D coordinates so that the straightness and parallelism of lines are preserved. All affine transformations can be represented by a 3x3 floating point matrix. There are a number of “primitive” affine transformations that can be combined.

6 Scaling S x 0 0 x S x *x 0 S y 0 y = S y *y 0 0 1 1 1

7 Scaling Notes Basic scaling operations take place with respect to the origin. If the shape is at the origin, it grows. If it is anywhere else, it grows and moves. s x, scaling along the X dimension, does not have to equal s y, scaling along the y. For instance, to flip a figure vertically about the x- axis, scale by s x =1, s y =-1

8 Reflection as Scaling 1 0 0 x x 0 -1 0 y = -y 0 0 1 1 1

9 Translation =

10 Rotation =

11 Composing Transformations Suppose we want to scale point (x, y) by 2 and then rotate by 90 degrees. rotate scale

12 Composing Transformations, 2 Because matrix multiplication is associative, we can rewrite this as

13 Composing Transformations, 3 Because matrix multiplication does not regularly commute, the order of transformations matters. This squares with our geometric intuition. If we invert the matrix, we reverse the transformation.

14 Transformations and the Origin When we transform a shape, we transform each of the defining points of the shape, and then redraw it. If we scale or rotate a shape that is not anchored at the origin, it will translate as well. If we just want to scale or rotate, then we should translate back to the origin, scale or rotate, and then translate back.

15 Transformations and the Origin, 2

16 Transformations in the 2D API Transformations are represented by instances of the AffineTransform class in the java.awt.geom package. Build a compound transform by 1. Creating a new instance of AffineTransform 2. Calling methods to build a stack of basic transforms: last in, first applied: –translate(double tx, double ty) –scale(double sx, double sy) –rotate(double theta) –rotate(double theta, double x, double y) rotates about (x,y)

17 Transformation Example baseXf = new AffineTransform(); baseXf.scale( scale, -scale ); baseXf.translate( -uRect.x, -uRect.y ); If we now apply baseXF it will translate first, then scale. Remember in Java® that transforms are built up like a stack, last in, first applied.

18 Back to Cardioid Let’s build our coordinate system: public class Cardioid extends JFrame { private CardioidGraph graph; private Rectangle2D.Float uSpace; public static void main( String [] args ) { Rectangle2D.Float uS = new Rectangle2D.Float(-1.5F,1.5F,4.0F,3.0F); Cardioid card = new Cardioid( uS ); card.setSize( 640, 480 ); card.setVisible( true ); }

19 Cardioid Cartesian Space Rectangle2D.Float(-1.5F,1.5F,4.0F,3.0F) represents the area of Cartesian coordinates in which we will draw our graph:

20 CardioidGraph public Cardioid( Rectangle2D.Float uS ) { uSpace = uS; graph = new CardioidGraph( uSpace );... public class CardioidGraph extends GraphPanel implements ActionListener { public CardioidGraph( Rectangle2D.Float uR ) { super( uR );...

21 GraphPanel public class GraphPanel extends JPanel { protected Rectangle2D.Float uRect; protected AffineTransform baseXf = null; protected Dimension curDim = null; protected double scale; private GeneralPath axes = null; public GraphPanel( Rectangle2D.Float uR ) { uRect = (Rectangle2D.Float) uR.clone(); }

22 GraphPanel, paintComponent() public void paintComponent( Graphics g ) { super.paintComponent( g ); Graphics2D g2 = (Graphics2D) g; if ( ! getSize().equals( curDim ) ) doResize(); drawAxes( g2 ); drawGrid( g2 ); }

23 GraphPanel, doResize() private void doResize() { curDim = getSize(); hScale = curDim.width / uRect.width; vScale = curDim.height / uRect.height; scale = Math.min( hScale, vScale ); baseXf = new AffineTransform(); baseXf.scale( scale, -scale ); baseXf.translate( -uRect.x, -uRect.y ); axes = createAxes(); grid = createGrid(); }

24 Creating and Drawing the Axes private GeneralPath createAxes() { GeneralPath path = new GeneralPath(); path.moveTo( uRect.x, 0F ); path.lineTo( uRect.x + uRect.width, 0F ); path.moveTo( 0F, uRect.y ); path.lineTo( 0F, uRect.y - uRect.height ); return path; } private void drawAxes( Graphics2D g2 ) { g2.setPaint( Color.green ); g2.setStroke( new BasicStroke(3 ) ); g2.draw(baseXf.createTransformedShape(axes)); }

25 Initializing CardioidGraph

26 Initializing CardioidGraph,2 public CardioidGraph( Rectangle2D.Float uR ) { super( uR ); diam = uRect.width / 4; hub = new Ellipse2D.Float(0F, -diam/2, diam, diam); tick = uRect.width / 40; wheel = new GeneralPath(); Shape s = new Ellipse2D.Double(diam, -diam/2,diam, diam); wheel.append( s, false ); wheel.moveTo( 2*diam, 0F ); wheel.lineTo( 2*diam + tick, 0F ); }

27 Timers Swing provides a utility class called Timer that makes it simpler to build animations. Timers tick at an interval you can set, and the ticks are reported as ActionEvents. public class TimerUser implements ActionListener { private Timer timer = null; private int tIval = 100; // interval in milliseconds public TimerUser() { timer = new Timer( tIval, this ); } public void start() { timer.start(); } public void actionPerformed( ActionEvent e ) { /*do repeated action on every tick*/ }

28 Timer Methods Timer( int tickMillis, ActionListener l ) void start() void stop() boolean isRunning() void setRepeats(boolean repeats) boolean isRepeats() void setCoalesce(boolean coalesce) boolean isCoalesce()

29 CardioidGraph, start() public void start() { if ( timer != null ) { timer.stop(); } timer = new Timer( tIval, this ); curve = new GeneralPath(); curve.moveTo( 2F, 0F ); tCount = 0; repaint(); timer.start(); }

30 CardioidGraph, actionPerformed() public void actionPerformed( ActionEvent e ) { tCount++; double theta = tCount * Math.PI / 180; double sint = Math.sin( theta ); double cost = Math.cos( theta ); double cx = cost + cost*cost; double cy = sint + sint*cost; curve.lineTo( (float)cx, (float) cy ); if ( tCount >= 360 ) { timer.stop(); timer = null; } repaint(); }

31 CardioidGraph, paintComponent() public void paintComponent( Graphics g ) { super.paintComponent( g ); Graphics2D g2 = (Graphics2D) g; drawHub( g2 ); drawCurve( g2 ); drawWheel( g2 ); }

32 CardioidGraph, drawCurve() private void drawCurve( Graphics2D g2 ) { if ( curve == null ) return; // make curve translucent Composite c = g2.getComposite(); Composite hc = AlphaComposite.getInstance( AlphaComposite.SRC_OVER,.5F ); g2.setComposite( hc ); g2.setPaint( Color.red ); g2.setStroke( new BasicStroke( 2 ) ); g2.draw( baseXf.createTransformedShape( curve ) ); g2.setComposite( c ); }

33 Geometry of the Cardioid

34 CardioidGraph, drawWheel() private void drawWheel( Graphics2D g2 ) { Composite c = g2.getComposite(); Composite hc = AlphaComposite.getInstance( AlphaComposite.SRC_OVER,.5F ); g2.setComposite( hc ); g2.setPaint( Color.orange ); g2.setStroke( new BasicStroke( 2 ) ); double theta = tCount * Math.PI / 180; AffineTransform whlXf = new AffineTransform(baseXf); whlXf.rotate( theta, diam/2, 0.0 ); whlXf.rotate( theta, 3*diam/2, 0.0 ); g2.draw( whlXf.createTransformedShape( wheel ) ); g2.setComposite( c ); }


Download ppt "1.00 Lecture 18 Geometric Transformations in the 2D API."

Similar presentations


Ads by Google