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    }