/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001 by:
EXSE, Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/exse/
lat/lon GmbH
http://www.lat-lon.de
It has been implemented within SEAGIS - An OpenSource implementation of OpenGIS specification
(C) 2001, Institut de Recherche pour le D�veloppement (http://sourceforge.net/projects/seagis/)
SEAGIS Contacts: Surveillance de l'Environnement Assist�e par Satellite
Institut de Recherche pour le D�veloppement / US-Espace
mailto:seasnet@teledetection.fr
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53115 Bonn
Germany
E-Mail: poth@lat-lon.de
Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: klaus.greve@uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.model.csct.resources;
// Geometry
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
/**
* Utility methods for affine transforms. This class provides a set
* of public static methods working on any {@link AffineTransform}.
* <br><br>
* Class <code>XAffineTransform</code> overrides all mutable methods
* of {@link AffineTransform} in order to check for permission before
* to change the transform's state. If {@link #checkPermission} is
* defined to always thrown an exception, then <code>XAffineTransform</code>
* is immutable.
*
* @version 1.0
* @author Martin Desruisseaux
*/
public abstract class XAffineTransform extends AffineTransform
{
/**
* Serial number for interoperability with different versions.
*/
private static final long serialVersionUID = 4891543057571195291L;
/**
* Tolerance value for floating point comparaisons.
*/
private static final double EPS=1E-6;
/**
* Constructs a new <code>XAffineTransform</code> that is a
* copy of the specified <code>AffineTransform</code> object.
*/
protected XAffineTransform(final AffineTransform tr)
{super(tr);}
/**
* Check if the caller is allowed to change this
* <code>XAffineTransform</code>'s state.
*/
protected abstract void checkPermission();
/**
* Check for permission before translating this transform.
*/
public void translate(double tx, double ty)
{
checkPermission();
super.translate(tx, ty);
}
/**
* Check for permission before rotating this transform.
*/
public void rotate(double theta)
{
checkPermission();
super.rotate(theta);
}
/**
* Check for permission before rotating this transform.
*/
public void rotate(double theta, double x, double y)
{
checkPermission();
super.rotate(theta, x, y);
}
/**
* Check for permission before scaling this transform.
*/
public void scale(double sx, double sy)
{
checkPermission();
super.scale(sx, sy);
}
/**
* Check for permission before shearing this transform.
*/
public void shear(double shx, double shy)
{
checkPermission();
super.shear(shx, shy);
}
/**
* Check for permission before setting this transform.
*/
public void setToIdentity()
{
checkPermission();
super.setToIdentity();
}
/**
* Check for permission before setting this transform.
*/
public void setToTranslation(double tx, double ty)
{
checkPermission();
super.setToTranslation(tx, ty);
}
/**
* Check for permission before setting this transform.
*/
public void setToRotation(double theta)
{
checkPermission();
super.setToRotation(theta);
}
/**
* Check for permission before setting this transform.
*/
public void setToRotation(double theta, double x, double y)
{
checkPermission();
super.setToRotation(theta, x, y);
}
/**
* Check for permission before setting this transform.
*/
public void setToScale(double sx, double sy)
{
checkPermission();
super.setToScale(sx, sy);
}
/**
* Check for permission before setting this transform.
*/
public void setToShear(double shx, double shy)
{
checkPermission();
super.setToShear(shx, shy);
}
/**
* Check for permission before setting this transform.
*/
public void setTransform(AffineTransform Tx)
{
checkPermission();
super.setTransform(Tx);
}
/**
* Check for permission before setting this transform.
*/
public void setTransform(double m00, double m10, double m01, double m11, double m02, double m12)
{
checkPermission();
super.setTransform(m00, m10, m01, m11, m02, m12);
}
/**
* Check for permission before concatenating this transform.
*/
public void concatenate(AffineTransform Tx)
{
checkPermission();
super.concatenate(Tx);
}
/**
* Check for permission before concatenating this transform.
*/
public void preConcatenate(AffineTransform Tx)
{
checkPermission();
super.preConcatenate(Tx);
}
/**
* Retourne un rectangle qui contient enti�rement la transformation directe de <code>bounds</code>.
* Cette op�ration est l'�quivalent de <code>createTransformedShape(bounds).getBounds2D()</code>.
*
* @param transform Transformation affine � utiliser.
* @param bounds Rectangle � transformer. Ce rectangle ne sera pas modifi�.
* @param dest Rectangle dans lequel placer le r�sultat. Si nul, un nouveau rectangle sera cr��.
* @return La transformation directe du rectangle <code>bounds</code>.
*/
public static Rectangle2D transform(final AffineTransform transform, final Rectangle2D bounds, final Rectangle2D dest)
{
double xmin=Double.POSITIVE_INFINITY;
double ymin=Double.POSITIVE_INFINITY;
double xmax=Double.NEGATIVE_INFINITY;
double ymax=Double.NEGATIVE_INFINITY;
final Point2D.Double point=new Point2D.Double();
for (int i=0; i<4; i++)
{
point.x = (i&1)==0 ? bounds.getMinX() : bounds.getMaxX();
point.y = (i&2)==0 ? bounds.getMinY() : bounds.getMaxY();
transform.transform(point, point);
if (point.x<xmin) xmin=point.x;
if (point.x>xmax) xmax=point.x;
if (point.y<ymin) ymin=point.y;
if (point.y>ymax) ymax=point.y;
}
if (dest!=null)
{
dest.setRect(xmin, ymin, xmax-xmin, ymax-ymin);
return dest;
}
return new Rectangle2D.Double(xmin, ymin, xmax-xmin, ymax-ymin);
}
/**
* Retourne un rectangle qui contient enti�rement la transformation inverse de <code>bounds</code>.
* Cette op�ration est l'�quivalent de <code>createInverse().createTransformedShape(bounds).getBounds2D()</code>.
*
* @param transform Transformation affine � utiliser.
* @param bounds Rectangle � transformer. Ce rectangle ne sera pas modifi�.
* @param dest Rectangle dans lequel placer le r�sultat. Si nul, un nouveau rectangle sera cr��.
* @return La transformation inverse du rectangle <code>bounds</code>.
* @throws NoninvertibleTransformException si la transformation affine ne peut pas �tre invers�e.
*/
public static Rectangle2D inverseTransform(final AffineTransform transform, final Rectangle2D bounds, final Rectangle2D dest) throws NoninvertibleTransformException
{
double xmin=Double.POSITIVE_INFINITY;
double ymin=Double.POSITIVE_INFINITY;
double xmax=Double.NEGATIVE_INFINITY;
double ymax=Double.NEGATIVE_INFINITY;
final Point2D.Double point=new Point2D.Double();
for (int i=0; i<4; i++)
{
point.x = (i&1)==0 ? bounds.getMinX() : bounds.getMaxX();
point.y = (i&2)==0 ? bounds.getMinY() : bounds.getMaxY();
transform.inverseTransform(point, point);
if (point.x<xmin) xmin=point.x;
if (point.x>xmax) xmax=point.x;
if (point.y<ymin) ymin=point.y;
if (point.y>ymax) ymax=point.y;
}
if (dest!=null)
{
dest.setRect(xmin, ymin, xmax-xmin, ymax-ymin);
return dest;
}
return new Rectangle2D.Double(xmin, ymin, xmax-xmin, ymax-ymin);
}
/**
* Calcule la transformation affine inverse d'un
* point sans prendre en compte la translation.
*
* @param transform Transformation affine � utiliser.
* @param source Point � transformer. Ce rectangle ne sera pas modifi�.
* @param dest Point dans lequel placer le r�sultat. Si nul, un nouveau point sera cr��.
* @return La transformation inverse du point <code>source</code>.
* @throws NoninvertibleTransformException si la transformation affine ne peut pas �tre invers�e.
*/
public static Point2D inverseDeltaTransform(final AffineTransform transform, final Point2D source, final Point2D dest) throws NoninvertibleTransformException
{
final double m00 = transform.getScaleX();
final double m11 = transform.getScaleY();
final double m01 = transform.getShearX();
final double m10 = transform.getShearY();
final double det = m00*m11 - m01*m10;
if (!(Math.abs(det) > Double.MIN_VALUE))
{
return transform.createInverse().deltaTransform(source, dest);
}
final double x = source.getX();
final double y = source.getY();
if (dest!=null)
{
dest.setLocation((x*m11 - y*m01)/det,
(y*m00 - x*m10)/det);
return dest;
}
return new Point2D.Double((x*m11 - y*m01)/det,
(y*m00 - x*m10)/det);
}
/**
* Retourne le facteur d'�chelle <var>x</var> en annulant l'effet d'une �ventuelle rotation.
* Ce facteur est calcul� par <IMG src="{@docRoot}/net/seas/map/layer/doc-files/equation1.gif">.
*/
public static double getScaleX0(final AffineTransform zoom)
{return XMath.hypot(zoom.getScaleX(), zoom.getShearX());}
/**
* Retourne le facteur d'�chelle <var>y</var> en annulant l'effet d'une �ventuelle rotation.
* Ce facteur est calcul� par <IMG src="{@docRoot}/net/seas/map/layer/doc-files/equation2.gif">.
*/
public static double getScaleY0(final AffineTransform zoom)
{return XMath.hypot(zoom.getScaleY(), zoom.getShearY());}
/**
* Retourne une transformation affine repr�sentant un zoom fait autour d'un point
* central (<var>x</var>,<var>y</var>). Les transformations laisseront inchang�es
* la coordonn�e (<var>x</var>,<var>y</var>) sp�cifi�e.
*
* @param sx Echelle le long de l'axe des <var>x</var>.
* @param sy Echelle le long de l'axe des <var>y</var>.
* @param x Coordonn�es <var>x</var> du point central.
* @param y Coordonn�es <var>y</var> du point central.
* @return Transformation affine d'un zoom qui laisse
* la coordonn�e (<var>x</var>,<var>y</var>)
* inchang�e.
*/
public static AffineTransform getScaleInstance(final double sx, final double sy, final double x, final double y)
{return new AffineTransform(sx, 0, 0, sy, (1-sx)*x, (1-sy)*y);}
/*
* V�rifie si les co�fficients de la matrice sont proches de valeurs enti�res.
* Si c'est le cas, ces co�fficients seront arrondis aux valeurs enti�res les
* plus proches. Cet arrondissement est utile par exemple pour accel�rer les
* affichages d'images. Il est surtout efficace lorsque l'on sait qu'une matrice
* a des chances d'�tre proche de la matrice identit�e.
*/
public static void round(final AffineTransform zoom)
{
double r;
final double m00,m01,m10,m11;
if (Math.abs((m00=Math.rint(r=zoom.getScaleX()))-r) <= EPS &&
Math.abs((m01=Math.rint(r=zoom.getShearX()))-r) <= EPS &&
Math.abs((m11=Math.rint(r=zoom.getScaleY()))-r) <= EPS &&
Math.abs((m10=Math.rint(r=zoom.getShearY()))-r) <= EPS)
{
if ((m00!=0 || m01!=0) && (m10!=0 || m11!=0))
{
double m02=Math.rint(r=zoom.getTranslateX()); if (!(Math.abs(m02-r)<=EPS)) m02=r;
double m12=Math.rint(r=zoom.getTranslateY()); if (!(Math.abs(m12-r)<=EPS)) m12=r;
zoom.setTransform(m00,m10,m01,m11,m02,m12);
}
}
}
}