/******************************************************************************* * Copyright (c) 2015 EclipseSource and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * EclipseSource - initial API and implementation ******************************************************************************/ package org.eclipse.swt.graphics; import java.util.Arrays; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; /** * Instances of this class represent transformation matrices for * points expressed as (x, y) pairs of floating point numbers. * <p> * Application code must explicitly invoke the <code>Transform.dispose()</code> * method to release the operating system resources managed by each instance * when those instances are no longer required. * </p> * * @since 3.1 */ public class Transform extends Resource { private final static float[] IDENTITY_MATRIX = { 1, 0, 0, 1, 0, 0 }; private float[] elements; /** * Constructs a new identity Transform. * <p> * This operation requires the operating system's advanced * graphics subsystem which may not be available on some * platforms. * </p> * * @param device the device on which to allocate the Transform * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> * </ul> * * @see #dispose() */ public Transform( Device device ) { this( device, IDENTITY_MATRIX ); } /** * Constructs a new Transform given an array of elements that represent the matrix that describes * the transformation. * <p> * This operation requires the operating system's advanced graphics subsystem which may not be * available on some platforms. * </p> * * @param device the device on which to allocate the Transform * @param elements an array of floats that describe the transformation matrix * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device, or the * elements array is null</li> * <li>ERROR_INVALID_ARGUMENT - if the elements array is too small to hold the matrix * values</li> * </ul> * @see #dispose() */ public Transform( Device device, float[] elements ) { this( device, checkTransform( elements )[ 0 ], elements[ 1 ], elements[ 2 ], elements[ 3 ], elements[ 4 ], elements[ 5 ] ); } /** * Constructs a new Transform given all of the elements that represent the matrix that describes * the transformation. * <p> * This operation requires the operating system's advanced graphics subsystem which may not be * available on some platforms. * </p> * * @param device the device on which to allocate the Transform * @param m11 the first element of the first row of the matrix * @param m12 the second element of the first row of the matrix * @param m21 the first element of the second row of the matrix * @param m22 the second element of the second row of the matrix * @param dx the third element of the first row of the matrix * @param dy the third element of the second row of the matrix * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> * </ul> * @see #dispose() */ public Transform( Device device, float m11, float m12, float m21, float m22, float dx, float dy ) { super( device ); elements = new float[] { m11, m12, m21, m22, dx, dy }; } static float[] checkTransform( float[] elements ) { if( elements == null ) { SWT.error( SWT.ERROR_NULL_ARGUMENT ); } if( elements.length < 6 ) { SWT.error( SWT.ERROR_INVALID_ARGUMENT ); } return elements; } @Override void destroy() { elements = null; } /** * Fills the parameter with the values of the transformation matrix that the receiver represents, * in the order {m11, m12, m21, m22, dx, dy}. * * @param elements array to hold the matrix values * @exception SWTException * <ul> * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> * </ul> * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> * <li>ERROR_INVALID_ARGUMENT - if the parameter is too small to hold the matrix * values</li> * </ul> */ public void getElements( float[] elements ) { if( isDisposed() ) { SWT.error( SWT.ERROR_GRAPHIC_DISPOSED ); } if( elements == null ) { SWT.error( SWT.ERROR_NULL_ARGUMENT ); } if( elements.length < 6 ) { SWT.error( SWT.ERROR_INVALID_ARGUMENT ); } System.arraycopy( this.elements, 0, elements, 0, 6 ); } /** * Modifies the receiver to represent a new transformation given all of the elements that * represent the matrix that describes that transformation. * * @param m11 the first element of the first row of the matrix * @param m12 the second element of the first row of the matrix * @param m21 the first element of the second row of the matrix * @param m22 the second element of the second row of the matrix * @param dx the third element of the first row of the matrix * @param dy the third element of the second row of the matrix * @exception SWTException * <ul> * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> * </ul> */ public void setElements( float m11, float m12, float m21, float m22, float dx, float dy ) { if( isDisposed() ) { SWT.error( SWT.ERROR_GRAPHIC_DISPOSED ); } elements = new float[] { m11, m12, m21, m22, dx, dy }; } /** * Modifies the receiver such that the matrix it represents becomes the identity matrix. * * @exception SWTException * <ul> * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> * </ul> */ public void identity() { if( isDisposed() ) { SWT.error( SWT.ERROR_GRAPHIC_DISPOSED ); } System.arraycopy( IDENTITY_MATRIX, 0, elements, 0, 6 ); } /** * Returns <code>true</code> if the Transform represents the identity matrix and false otherwise. * * @return <code>true</code> if the receiver is an identity Transform, and <code>false</code> * otherwise */ public boolean isIdentity() { if( isDisposed() ) { SWT.error( SWT.ERROR_GRAPHIC_DISPOSED ); } return Arrays.equals( elements, IDENTITY_MATRIX ); } /** * Modifies the receiver such that the matrix it represents becomes the mathematical inverse of * the matrix it previously represented. * * @exception SWTException * <ul> * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_CANNOT_INVERT_MATRIX - if the matrix is not invertible</li> * </ul> */ public void invert() { if( isDisposed() ) { SWT.error( SWT.ERROR_GRAPHIC_DISPOSED ); } float d = elements[ 0 ] * elements[ 3 ] - elements[ 1 ] * elements[ 2 ]; elements = new float[] { elements[ 3 ] / d, -elements[ 1 ] / d, -elements[ 2 ] / d, elements[ 0 ] / d, ( elements[ 2 ] * elements[ 5 ] - elements[ 4 ] * elements[ 3 ] ) / d, ( elements[ 4 ] * elements[ 1 ] - elements[ 5 ] * elements[ 0 ] ) / d }; } /** * Modifies the receiver such that the matrix it represents becomes the the result of multiplying * the matrix it previously represented by the argument. * * @param matrix the matrix to multiply the receiver by * @exception SWTException * <ul> * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> * </ul> * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li> * <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li> * </ul> */ public void multiply( Transform matrix ) { if( isDisposed() ) { SWT.error( SWT.ERROR_GRAPHIC_DISPOSED ); } if( matrix == null ) { SWT.error( SWT.ERROR_NULL_ARGUMENT ); } if( matrix.isDisposed() ) { SWT.error( SWT.ERROR_INVALID_ARGUMENT ); } float[] elements = new float[ 6 ]; matrix.getElements( elements ); multiply( elements[ 0 ], elements[ 1 ], elements[ 2 ], elements[ 3 ], elements[ 4 ], elements[ 5 ] ); } /** * Modifies the receiver so that it represents a transformation that is equivalent to its previous * transformation translated by (offsetX, offsetY). * * @param offsetX the distance to translate in the X direction * @param offsetY the distance to translate in the Y direction * @exception SWTException * <ul> * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> * </ul> */ public void translate( float offsetX, float offsetY ) { if( isDisposed() ) { SWT.error( SWT.ERROR_GRAPHIC_DISPOSED ); } multiply( 1, 0, 0, 1, offsetX, offsetY ); } /** * Modifies the receiver so that it represents a transformation that is equivalent to its previous * transformation scaled by (scaleX, scaleY). * * @param scaleX the amount to scale in the X direction * @param scaleY the amount to scale in the Y direction * @exception SWTException * <ul> * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> * </ul> */ public void scale( float scaleX, float scaleY ) { if( isDisposed() ) { SWT.error( SWT.ERROR_GRAPHIC_DISPOSED ); } multiply( scaleX, 0, 0, scaleY, 0, 0 ); } /** * Modifies the receiver so that it represents a transformation that is equivalent to its previous * transformation rotated by the specified angle. The angle is specified in degrees and for the * identity transform 0 degrees is at the 3 o'clock position. A positive value indicates a * clockwise rotation while a negative value indicates a counter-clockwise rotation. * * @param angle the angle to rotate the transformation by * @exception SWTException * <ul> * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> * </ul> */ public void rotate( float angle ) { if( isDisposed() ) { SWT.error( SWT.ERROR_GRAPHIC_DISPOSED ); } double radians = Math.toRadians( angle ); float m11 = ( float )Math.cos( radians ); float m12 = ( float )Math.sin( radians ); float m21 = -( float )Math.sin( radians ); float m22 = ( float )Math.cos( radians ); multiply( m11, m12, m21, m22, 0, 0 ); } /** * Modifies the receiver so that it represents a transformation that is equivalent to its previous * transformation sheared by (shearX, shearY). * * @param shearX the shear factor in the X direction * @param shearY the shear factor in the Y direction * @exception SWTException * <ul> * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> * </ul> */ public void shear( float shearX, float shearY ) { if( isDisposed() ) { SWT.error( SWT.ERROR_GRAPHIC_DISPOSED ); } multiply( 1, shearY, shearX, 1, 0, 0 ); } /** * Given an array containing points described by alternating x and y values, modify that array * such that each point has been replaced with the result of applying the transformation * represented by the receiver to that point. * * @param pointArray an array of alternating x and y values to be transformed * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the point array is null</li> * </ul> * @exception SWTException * <ul> * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> * </ul> */ public void transform( float[] pointArray ) { if( isDisposed() ) { SWT.error( SWT.ERROR_GRAPHIC_DISPOSED ); } if( pointArray == null ) { SWT.error( SWT.ERROR_NULL_ARGUMENT ); } for( int i = 1; i < pointArray.length; i += 2 ) { float dx = pointArray[ i - 1 ]; float dy = pointArray[ i ]; pointArray[ i - 1 ] = elements[ 0 ] * dx + elements[ 2 ] * dy + elements[ 4 ]; pointArray[ i ] = elements[ 1 ] * dx + elements[ 3 ] * dy + elements[ 5 ]; } } /** * Returns a string containing a concise, human-readable description of the receiver. * * @return a string representation of the receiver */ @Override public String toString() { if( isDisposed() ) { return "Transform {*DISPOSED*}"; } return "Transform {" + elements[ 0 ] + "," + elements[ 1 ] + "," + elements[ 2 ] + "," + elements[ 3 ] + "," + elements[ 4 ] + "," + elements[ 5 ] + "}"; } private void multiply( float m11, float m12, float m21, float m22, float dx, float dy ) { elements = new float[] { elements[ 0 ] * m11 + elements[ 2 ] * m12, elements[ 1 ] * m11 + elements[ 3 ] * m12, elements[ 0 ] * m21 + elements[ 2 ] * m22, elements[ 1 ] * m21 + elements[ 3 ] * m22, elements[ 0 ] * dx + elements[ 2 ] * dy + elements[ 4 ], elements[ 1 ] * dx + elements[ 3 ] * dy + elements[ 5 ] }; } }