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.geom.*;
021import java.awt.image.*;
022
023/**
024 * A Filter which produces the effect of looking into a kaleidoscope.
025 */
026public class KaleidoscopeFilter extends TransformFilter {
027        
028        private float angle = 0;
029        private float angle2 = 0;
030        private float centreX = 0.5f;
031        private float centreY = 0.5f;
032        private int sides = 3;
033        private float radius = 0;
034
035        private float icentreX;
036        private float icentreY;
037
038        /**
039         * Construct a KaleidoscopeFilter with no distortion.
040         */
041        public KaleidoscopeFilter() {
042                setEdgeAction( CLAMP );
043        }
044
045        /**
046         * Set the number of sides of the kaleidoscope.
047         * @param sides the number of sides
048     * @min-value 2
049     * @see #getSides
050         */
051        public void setSides(int sides) {
052                this.sides = sides;
053        }
054
055        /**
056         * Get the number of sides of the kaleidoscope.
057         * @return the number of sides
058     * @see #setSides
059         */
060        public int getSides() {
061                return sides;
062        }
063
064        /**
065     * Set the angle of the kaleidoscope.
066     * @param angle the angle of the kaleidoscope.
067     * @angle
068     * @see #getAngle
069     */
070        public void setAngle(float angle) {
071                this.angle = angle;
072        }
073        
074        /**
075     * Get the angle of the kaleidoscope.
076     * @return the angle of the kaleidoscope.
077     * @see #setAngle
078     */
079        public float getAngle() {
080                return angle;
081        }
082        
083        /**
084     * Set the secondary angle of the kaleidoscope.
085     * @param angle2 the angle
086     * @angle
087     * @see #getAngle2
088     */
089        public void setAngle2(float angle2) {
090                this.angle2 = angle2;
091        }
092        
093        /**
094     * Get the secondary angle of the kaleidoscope.
095     * @return the angle
096     * @see #setAngle2
097     */
098        public float getAngle2() {
099                return angle2;
100        }
101        
102        /**
103         * Set the centre of the effect in the X direction as a proportion of the image size.
104         * @param centreX the center
105     * @see #getCentreX
106         */
107        public void setCentreX( float centreX ) {
108                this.centreX = centreX;
109        }
110
111        /**
112         * Get the centre of the effect in the X direction as a proportion of the image size.
113         * @return the center
114     * @see #setCentreX
115         */
116        public float getCentreX() {
117                return centreX;
118        }
119        
120        /**
121         * Set the centre of the effect in the Y direction as a proportion of the image size.
122         * @param centreY the center
123     * @see #getCentreY
124         */
125        public void setCentreY( float centreY ) {
126                this.centreY = centreY;
127        }
128
129        /**
130         * Get the centre of the effect in the Y direction as a proportion of the image size.
131         * @return the center
132     * @see #setCentreY
133         */
134        public float getCentreY() {
135                return centreY;
136        }
137        
138        /**
139         * Set the centre of the effect as a proportion of the image size.
140         * @param centre the center
141     * @see #getCentre
142         */
143        public void setCentre( Point2D centre ) {
144                this.centreX = (float)centre.getX();
145                this.centreY = (float)centre.getY();
146        }
147
148        /**
149         * Get the centre of the effect as a proportion of the image size.
150         * @return the center
151     * @see #setCentre
152         */
153        public Point2D getCentre() {
154                return new Point2D.Float( centreX, centreY );
155        }
156        
157        /**
158         * Set the radius of the effect.
159         * @param radius the radius
160     * @min-value 0
161     * @see #getRadius
162         */
163        public void setRadius( float radius ) {
164                this.radius = radius;
165        }
166
167        /**
168         * Get the radius of the effect.
169         * @return the radius
170     * @see #setRadius
171         */
172        public float getRadius() {
173                return radius;
174        }
175        
176    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
177                icentreX = src.getWidth() * centreX;
178                icentreY = src.getHeight() * centreY;
179                return super.filter( src, dst );
180        }
181        
182        protected void transformInverse(int x, int y, float[] out) {
183                double dx = x-icentreX;
184                double dy = y-icentreY;
185                double r = Math.sqrt( dx*dx + dy*dy );
186                double theta = Math.atan2( dy, dx ) - angle - angle2;
187                theta = ImageMath.triangle( (float)( theta/Math.PI*sides*.5 ) );
188                if ( radius != 0 ) {
189                        double c = Math.cos(theta);
190                        double radiusc = radius/c;
191                        r = radiusc * ImageMath.triangle( (float)(r/radiusc) );
192                }
193                theta += angle;
194
195                out[0] = (float)(icentreX + r*Math.cos(theta));
196                out[1] = (float)(icentreY + r*Math.sin(theta));
197        }
198
199        public String toString() {
200                return "Distort/Kaleidoscope...";
201        }
202
203}