001/*
002Copyright 2006 Jerry Huxtable
003
004Licensed under the Apache License, Version 2.0 (the "License");
005you may not use this file except in compliance with the License.
006You may obtain a copy of the License at
007
008   http://www.apache.org/licenses/LICENSE-2.0
009
010Unless required by applicable law or agreed to in writing, software
011distributed under the License is distributed on an "AS IS" BASIS,
012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013See the License for the specific language governing permissions and
014limitations under the License.
015*/
016
017package com.jhlabs.image;
018
019import java.awt.*;
020import java.awt.image.*;
021
022/**
023 * A filter which distorts and image by performing coordinate conversions between rectangular and polar coordinates.
024 */
025public class PolarFilter extends TransformFilter {
026        
027        /**
028     * Convert from rectangular to polar coordinates.
029     */
030    public final static int RECT_TO_POLAR = 0;
031
032        /**
033     * Convert from polar to rectangular coordinates.
034     */
035        public final static int POLAR_TO_RECT = 1;
036
037        /**
038     * Invert the image in a circle.
039     */
040        public final static int INVERT_IN_CIRCLE = 2;
041
042        private int type;
043        private float width, height;
044        private float centreX, centreY;
045        private float radius;
046
047        /**
048     * Construct a PolarFilter.
049     */
050    public PolarFilter() {
051                this(RECT_TO_POLAR);
052        }
053
054        /**
055     * Construct a PolarFilter.
056     * @param type the distortion type
057     */
058        public PolarFilter(int type) {
059                this.type = type;
060                setEdgeAction(CLAMP);
061        }
062
063    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
064                this.width = src.getWidth();
065                this.height = src.getHeight();
066                centreX = width/2;
067                centreY = height/2;
068                radius = Math.max(centreY, centreX);
069                return super.filter( src, dst );
070        }
071        
072        /**
073     * Set the distortion type.
074     * @param type the distortion type
075     * @see #getType
076     */
077        public void setType(int type) {
078                this.type = type;
079        }
080
081        /**
082     * Get the distortion type.
083     * @return the distortion type
084     * @see #setType
085     */
086        public int getType() {
087                return type;
088        }
089
090        private float sqr(float x) {
091                return x*x;
092        }
093
094        protected void transformInverse(int x, int y, float[] out) {
095                float theta, t;
096                float m, xmax, ymax;
097                float r = 0;
098                
099                switch (type) {
100                case RECT_TO_POLAR:
101                        theta = 0;
102                        if (x >= centreX) {
103                                if (y > centreY) {
104                                        theta = ImageMath.PI - (float)Math.atan(((float)(x - centreX))/((float)(y - centreY)));
105                                        r = (float)Math.sqrt(sqr (x - centreX) + sqr (y - centreY));
106                                } else if (y < centreY) {
107                                        theta = (float)Math.atan (((float)(x - centreX))/((float)(centreY - y)));
108                                        r = (float)Math.sqrt (sqr (x - centreX) + sqr (centreY - y));
109                                } else {
110                                        theta = ImageMath.HALF_PI;
111                                        r = x - centreX;
112                                }
113                        } else if (x < centreX) {
114                                if (y < centreY) {
115                                        theta = ImageMath.TWO_PI - (float)Math.atan (((float)(centreX -x))/((float)(centreY - y)));
116                                        r = (float)Math.sqrt (sqr (centreX - x) + sqr (centreY - y));
117                                } else if (y > centreY) {
118                                        theta = ImageMath.PI + (float)Math.atan (((float)(centreX - x))/((float)(y - centreY)));
119                                        r = (float)Math.sqrt (sqr (centreX - x) + sqr (y - centreY));
120                                } else {
121                                        theta = 1.5f * ImageMath.PI;
122                                        r = centreX - x;
123                                }
124                        }
125                        if (x != centreX)
126                                m = Math.abs (((float)(y - centreY)) / ((float)(x - centreX)));
127                        else
128                                m = 0;
129                        
130                        if (m <= ((float)height / (float)width)) {
131                                if (x == centreX) {
132                                        xmax = 0;
133                                        ymax = centreY;
134                                } else {
135                                        xmax = centreX;
136                                        ymax = m * xmax;
137                                }
138                        } else {
139                                ymax = centreY;
140                                xmax = ymax / m;
141                        }
142                        
143                        out[0] = (width-1) - (width - 1)/ImageMath.TWO_PI * theta;
144                        out[1] = height * r / radius;
145                        break;
146                case POLAR_TO_RECT:
147                        theta = x / width * ImageMath.TWO_PI;
148                        float theta2;
149
150                        if (theta >= 1.5f * ImageMath.PI)
151                                theta2 = ImageMath.TWO_PI - theta;
152                        else if (theta >= ImageMath.PI)
153                                theta2 = theta - ImageMath.PI;
154                        else if (theta >= 0.5f * ImageMath.PI)
155                                theta2 = ImageMath.PI - theta;
156                        else
157                                theta2 = theta;
158        
159                        t = (float)Math.tan(theta2);
160                        if (t != 0)
161                                m = 1.0f / t;
162                        else
163                                m = 0;
164        
165                        if (m <= ((float)(height) / (float)(width))) {
166                                if (theta2 == 0) {
167                                        xmax = 0;
168                                        ymax = centreY;
169                                } else {
170                                        xmax = centreX;
171                                        ymax = m * xmax;
172                                }
173                        } else {
174                                ymax = centreY;
175                                xmax = ymax / m;
176                        }
177        
178                        r = radius * (float)(y / (float)(height));
179
180                        float nx = -r * (float)Math.sin(theta2);
181                        float ny = r * (float)Math.cos(theta2);
182                        
183                        if (theta >= 1.5f * ImageMath.PI) {
184                                out[0] = (float)centreX - nx;
185                                out[1] = (float)centreY - ny;
186                        } else if (theta >= Math.PI) {
187                                out[0] = (float)centreX - nx;
188                                out[1] = (float)centreY + ny;
189                        } else if (theta >= 0.5 * Math.PI) {
190                                out[0] = (float)centreX + nx;
191                                out[1] = (float)centreY + ny;
192                        } else {
193                                out[0] = (float)centreX + nx;
194                                out[1] = (float)centreY - ny;
195                        }
196                        break;
197                case INVERT_IN_CIRCLE:
198                        float dx = x-centreX;
199                        float dy = y-centreY;
200                        float distance2 = dx*dx+dy*dy;
201                        out[0] = centreX + centreX*centreX * dx/distance2;
202                        out[1] = centreY + centreY*centreY * dy/distance2;
203                        break;
204                }
205        }
206
207        public String toString() {
208                return "Distort/Polar Coordinates...";
209        }
210
211}