001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/resources/XAffineTransform.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/exse/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 It has been implemented within SEAGIS - An OpenSource implementation of OpenGIS specification 012 (C) 2001, Institut de Recherche pour le D�veloppement (http://sourceforge.net/projects/seagis/) 013 SEAGIS Contacts: Surveillance de l'Environnement Assist�e par Satellite 014 Institut de Recherche pour le D�veloppement / US-Espace 015 mailto:seasnet@teledetection.fr 016 017 018 This library is free software; you can redistribute it and/or 019 modify it under the terms of the GNU Lesser General Public 020 License as published by the Free Software Foundation; either 021 version 2.1 of the License, or (at your option) any later version. 022 023 This library is distributed in the hope that it will be useful, 024 but WITHOUT ANY WARRANTY; without even the implied warranty of 025 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 026 Lesser General Public License for more details. 027 028 You should have received a copy of the GNU Lesser General Public 029 License along with this library; if not, write to the Free Software 030 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 031 032 Contact: 033 034 Andreas Poth 035 lat/lon GmbH 036 Aennchenstr. 19 037 53115 Bonn 038 Germany 039 E-Mail: poth@lat-lon.de 040 041 Klaus Greve 042 Department of Geography 043 University of Bonn 044 Meckenheimer Allee 166 045 53115 Bonn 046 Germany 047 E-Mail: klaus.greve@uni-bonn.de 048 049 050 ---------------------------------------------------------------------------*/ 051 package org.deegree.model.csct.resources; 052 053 // Geometry 054 import java.awt.geom.AffineTransform; 055 import java.awt.geom.NoninvertibleTransformException; 056 import java.awt.geom.Point2D; 057 import java.awt.geom.Rectangle2D; 058 059 060 /** 061 * Utility methods for affine transforms. This class provides a set 062 * of public static methods working on any {@link AffineTransform}. 063 * <br><br> 064 * Class <code>XAffineTransform</code> overrides all mutable methods 065 * of {@link AffineTransform} in order to check for permission before 066 * to change the transform's state. If {@link #checkPermission} is 067 * defined to always thrown an exception, then <code>XAffineTransform</code> 068 * is immutable. 069 * 070 * @version 1.0 071 * @author Martin Desruisseaux 072 */ 073 public abstract class XAffineTransform extends AffineTransform 074 { 075 /** 076 * Serial number for interoperability with different versions. 077 */ 078 private static final long serialVersionUID = 4891543057571195291L; 079 080 /** 081 * Tolerance value for floating point comparaisons. 082 */ 083 private static final double EPS=1E-6; 084 085 /** 086 * Constructs a new <code>XAffineTransform</code> that is a 087 * copy of the specified <code>AffineTransform</code> object. 088 */ 089 protected XAffineTransform(final AffineTransform tr) 090 {super(tr);} 091 092 /** 093 * Check if the caller is allowed to change this 094 * <code>XAffineTransform</code>'s state. 095 */ 096 protected abstract void checkPermission(); 097 098 /** 099 * Check for permission before translating this transform. 100 */ 101 public void translate(double tx, double ty) 102 { 103 checkPermission(); 104 super.translate(tx, ty); 105 } 106 107 /** 108 * Check for permission before rotating this transform. 109 */ 110 public void rotate(double theta) 111 { 112 checkPermission(); 113 super.rotate(theta); 114 } 115 116 /** 117 * Check for permission before rotating this transform. 118 */ 119 public void rotate(double theta, double x, double y) 120 { 121 checkPermission(); 122 super.rotate(theta, x, y); 123 } 124 125 /** 126 * Check for permission before scaling this transform. 127 */ 128 public void scale(double sx, double sy) 129 { 130 checkPermission(); 131 super.scale(sx, sy); 132 } 133 134 /** 135 * Check for permission before shearing this transform. 136 */ 137 public void shear(double shx, double shy) 138 { 139 checkPermission(); 140 super.shear(shx, shy); 141 } 142 143 /** 144 * Check for permission before setting this transform. 145 */ 146 public void setToIdentity() 147 { 148 checkPermission(); 149 super.setToIdentity(); 150 } 151 152 /** 153 * Check for permission before setting this transform. 154 */ 155 public void setToTranslation(double tx, double ty) 156 { 157 checkPermission(); 158 super.setToTranslation(tx, ty); 159 } 160 161 /** 162 * Check for permission before setting this transform. 163 */ 164 public void setToRotation(double theta) 165 { 166 checkPermission(); 167 super.setToRotation(theta); 168 } 169 170 /** 171 * Check for permission before setting this transform. 172 */ 173 public void setToRotation(double theta, double x, double y) 174 { 175 checkPermission(); 176 super.setToRotation(theta, x, y); 177 } 178 179 /** 180 * Check for permission before setting this transform. 181 */ 182 public void setToScale(double sx, double sy) 183 { 184 checkPermission(); 185 super.setToScale(sx, sy); 186 } 187 188 /** 189 * Check for permission before setting this transform. 190 */ 191 public void setToShear(double shx, double shy) 192 { 193 checkPermission(); 194 super.setToShear(shx, shy); 195 } 196 197 /** 198 * Check for permission before setting this transform. 199 */ 200 public void setTransform(AffineTransform Tx) 201 { 202 checkPermission(); 203 super.setTransform(Tx); 204 } 205 206 /** 207 * Check for permission before setting this transform. 208 */ 209 public void setTransform(double m00, double m10, double m01, double m11, double m02, double m12) 210 { 211 checkPermission(); 212 super.setTransform(m00, m10, m01, m11, m02, m12); 213 } 214 215 /** 216 * Check for permission before concatenating this transform. 217 */ 218 public void concatenate(AffineTransform Tx) 219 { 220 checkPermission(); 221 super.concatenate(Tx); 222 } 223 224 /** 225 * Check for permission before concatenating this transform. 226 */ 227 public void preConcatenate(AffineTransform Tx) 228 { 229 checkPermission(); 230 super.preConcatenate(Tx); 231 } 232 233 /** 234 * Retourne un rectangle qui contient enti�rement la transformation directe de <code>bounds</code>. 235 * Cette op�ration est l'�quivalent de <code>createTransformedShape(bounds).getBounds2D()</code>. 236 * 237 * @param transform Transformation affine � utiliser. 238 * @param bounds Rectangle � transformer. Ce rectangle ne sera pas modifi�. 239 * @param dest Rectangle dans lequel placer le r�sultat. Si nul, un nouveau rectangle sera cr��. 240 * @return La transformation directe du rectangle <code>bounds</code>. 241 */ 242 public static Rectangle2D transform(final AffineTransform transform, final Rectangle2D bounds, final Rectangle2D dest) 243 { 244 double xmin=Double.POSITIVE_INFINITY; 245 double ymin=Double.POSITIVE_INFINITY; 246 double xmax=Double.NEGATIVE_INFINITY; 247 double ymax=Double.NEGATIVE_INFINITY; 248 final Point2D.Double point=new Point2D.Double(); 249 for (int i=0; i<4; i++) 250 { 251 point.x = (i&1)==0 ? bounds.getMinX() : bounds.getMaxX(); 252 point.y = (i&2)==0 ? bounds.getMinY() : bounds.getMaxY(); 253 transform.transform(point, point); 254 if (point.x<xmin) xmin=point.x; 255 if (point.x>xmax) xmax=point.x; 256 if (point.y<ymin) ymin=point.y; 257 if (point.y>ymax) ymax=point.y; 258 } 259 if (dest!=null) 260 { 261 dest.setRect(xmin, ymin, xmax-xmin, ymax-ymin); 262 return dest; 263 } 264 return new Rectangle2D.Double(xmin, ymin, xmax-xmin, ymax-ymin); 265 } 266 267 /** 268 * Retourne un rectangle qui contient enti�rement la transformation inverse de <code>bounds</code>. 269 * Cette op�ration est l'�quivalent de <code>createInverse().createTransformedShape(bounds).getBounds2D()</code>. 270 * 271 * @param transform Transformation affine � utiliser. 272 * @param bounds Rectangle � transformer. Ce rectangle ne sera pas modifi�. 273 * @param dest Rectangle dans lequel placer le r�sultat. Si nul, un nouveau rectangle sera cr��. 274 * @return La transformation inverse du rectangle <code>bounds</code>. 275 * @throws NoninvertibleTransformException si la transformation affine ne peut pas �tre invers�e. 276 */ 277 public static Rectangle2D inverseTransform(final AffineTransform transform, final Rectangle2D bounds, final Rectangle2D dest) throws NoninvertibleTransformException 278 { 279 double xmin=Double.POSITIVE_INFINITY; 280 double ymin=Double.POSITIVE_INFINITY; 281 double xmax=Double.NEGATIVE_INFINITY; 282 double ymax=Double.NEGATIVE_INFINITY; 283 final Point2D.Double point=new Point2D.Double(); 284 for (int i=0; i<4; i++) 285 { 286 point.x = (i&1)==0 ? bounds.getMinX() : bounds.getMaxX(); 287 point.y = (i&2)==0 ? bounds.getMinY() : bounds.getMaxY(); 288 transform.inverseTransform(point, point); 289 if (point.x<xmin) xmin=point.x; 290 if (point.x>xmax) xmax=point.x; 291 if (point.y<ymin) ymin=point.y; 292 if (point.y>ymax) ymax=point.y; 293 } 294 if (dest!=null) 295 { 296 dest.setRect(xmin, ymin, xmax-xmin, ymax-ymin); 297 return dest; 298 } 299 return new Rectangle2D.Double(xmin, ymin, xmax-xmin, ymax-ymin); 300 } 301 302 /** 303 * Calcule la transformation affine inverse d'un 304 * point sans prendre en compte la translation. 305 * 306 * @param transform Transformation affine � utiliser. 307 * @param source Point � transformer. Ce rectangle ne sera pas modifi�. 308 * @param dest Point dans lequel placer le r�sultat. Si nul, un nouveau point sera cr��. 309 * @return La transformation inverse du point <code>source</code>. 310 * @throws NoninvertibleTransformException si la transformation affine ne peut pas �tre invers�e. 311 */ 312 public static Point2D inverseDeltaTransform(final AffineTransform transform, final Point2D source, final Point2D dest) throws NoninvertibleTransformException 313 { 314 final double m00 = transform.getScaleX(); 315 final double m11 = transform.getScaleY(); 316 final double m01 = transform.getShearX(); 317 final double m10 = transform.getShearY(); 318 final double det = m00*m11 - m01*m10; 319 if (!(Math.abs(det) > Double.MIN_VALUE)) 320 { 321 return transform.createInverse().deltaTransform(source, dest); 322 } 323 final double x = source.getX(); 324 final double y = source.getY(); 325 if (dest!=null) 326 { 327 dest.setLocation((x*m11 - y*m01)/det, 328 (y*m00 - x*m10)/det); 329 return dest; 330 } 331 return new Point2D.Double((x*m11 - y*m01)/det, 332 (y*m00 - x*m10)/det); 333 } 334 335 /** 336 * Retourne le facteur d'�chelle <var>x</var> en annulant l'effet d'une �ventuelle rotation. 337 * Ce facteur est calcul� par <IMG src="{@docRoot}/net/seas/map/layer/doc-files/equation1.gif">. 338 */ 339 public static double getScaleX0(final AffineTransform zoom) 340 {return XMath.hypot(zoom.getScaleX(), zoom.getShearX());} 341 342 /** 343 * Retourne le facteur d'�chelle <var>y</var> en annulant l'effet d'une �ventuelle rotation. 344 * Ce facteur est calcul� par <IMG src="{@docRoot}/net/seas/map/layer/doc-files/equation2.gif">. 345 */ 346 public static double getScaleY0(final AffineTransform zoom) 347 {return XMath.hypot(zoom.getScaleY(), zoom.getShearY());} 348 349 /** 350 * Retourne une transformation affine repr�sentant un zoom fait autour d'un point 351 * central (<var>x</var>,<var>y</var>). Les transformations laisseront inchang�es 352 * la coordonn�e (<var>x</var>,<var>y</var>) sp�cifi�e. 353 * 354 * @param sx Echelle le long de l'axe des <var>x</var>. 355 * @param sy Echelle le long de l'axe des <var>y</var>. 356 * @param x Coordonn�es <var>x</var> du point central. 357 * @param y Coordonn�es <var>y</var> du point central. 358 * @return Transformation affine d'un zoom qui laisse 359 * la coordonn�e (<var>x</var>,<var>y</var>) 360 * inchang�e. 361 */ 362 public static AffineTransform getScaleInstance(final double sx, final double sy, final double x, final double y) 363 {return new AffineTransform(sx, 0, 0, sy, (1-sx)*x, (1-sy)*y);} 364 365 /* 366 * V�rifie si les co�fficients de la matrice sont proches de valeurs enti�res. 367 * Si c'est le cas, ces co�fficients seront arrondis aux valeurs enti�res les 368 * plus proches. Cet arrondissement est utile par exemple pour accel�rer les 369 * affichages d'images. Il est surtout efficace lorsque l'on sait qu'une matrice 370 * a des chances d'�tre proche de la matrice identit�e. 371 */ 372 public static void round(final AffineTransform zoom) 373 { 374 double r; 375 final double m00,m01,m10,m11; 376 if (Math.abs((m00=Math.rint(r=zoom.getScaleX()))-r) <= EPS && 377 Math.abs((m01=Math.rint(r=zoom.getShearX()))-r) <= EPS && 378 Math.abs((m11=Math.rint(r=zoom.getScaleY()))-r) <= EPS && 379 Math.abs((m10=Math.rint(r=zoom.getShearY()))-r) <= EPS) 380 { 381 if ((m00!=0 || m01!=0) && (m10!=0 || m11!=0)) 382 { 383 double m02=Math.rint(r=zoom.getTranslateX()); if (!(Math.abs(m02-r)<=EPS)) m02=r; 384 double m12=Math.rint(r=zoom.getTranslateY()); if (!(Math.abs(m12-r)<=EPS)) m12=r; 385 zoom.setTransform(m00,m10,m01,m11,m02,m12); 386 } 387 } 388 } 389 }