001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.demos.sandbox.fractle;
031
032import java.util.Collections;
033import java.util.Iterator;
034
035import org.openimaj.image.DisplayUtilities;
036import org.openimaj.image.MBFImage;
037import org.openimaj.image.colour.RGBColour;
038import org.openimaj.math.geometry.line.Line2d;
039import org.openimaj.math.geometry.point.Point2d;
040import org.openimaj.math.geometry.point.Point2dImpl;
041import org.openimaj.math.geometry.point.PointList;
042import org.openimaj.math.geometry.transforms.TransformUtilities;
043
044import Jama.Matrix;
045
046/**
047 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
048 *
049 */
050public class DragonCurve {
051
052        private PointList currentCurve;
053        private Point2d startOfDragon;
054        private Point2d endOfDragon;
055        public DragonCurve() {
056                this.startOfDragon = new Point2dImpl(1/3f,0.5f);
057                endOfDragon = new Point2dImpl(2/3f,0.5f);
058                this.currentCurve = new PointList(startOfDragon,endOfDragon);
059        }
060
061        public PointList nextHalfCurve() {
062                Matrix dragonTurn = TransformUtilities.rotationMatrixAboutPoint(-Math.PI/2f, startOfDragon.getX(), startOfDragon.getY());
063                PointList newCurve = this.currentCurve.transform(dragonTurn);
064                return newCurve;
065        }
066
067        public PointList nextCurve(float prop) {
068                Matrix dragonTurn = TransformUtilities.rotationMatrixAboutPoint((-Math.PI/2f)*prop, startOfDragon.getX(), startOfDragon.getY());
069                PointList newCurve = this.currentCurve.transform(dragonTurn);
070                return newCurve;
071        }
072
073        public void iterate() {
074                PointList newCurve = nextHalfCurve();
075                Collections.reverse(newCurve.points);
076                for (Point2d point2d : this.currentCurve) {
077                        if(!point2d.equals(this.startOfDragon))
078                                newCurve.points.add(point2d);
079                }
080                Point2dImpl minXY = minXY(newCurve);
081                Point2dImpl maxXY = maxXY(newCurve);
082                double bbScalar = Math.max(maxXY.x - minXY.x,maxXY.y - minXY.y);
083                Matrix translateToPointMatrix = TransformUtilities.translateToPointMatrix(
084                                minXY,
085                                new Point2dImpl(1/3f,1/3f)
086                );
087                Matrix translate = translateToPointMatrix;
088                float d = (float) ((1/3f)/bbScalar);
089                Matrix scale = TransformUtilities.scaleMatrix(d, d);
090                Matrix transform = translate.times(scale);
091//              Matrix transform = translate;
092                newCurve = newCurve.transform(transform);
093                this.currentCurve = newCurve;
094                this.startOfDragon = newCurve.points.get(0);
095                this.endOfDragon = newCurve.points.get(newCurve.points.size()-1);
096        }
097
098        interface StepListener{
099                public void state(PointList list);
100        }
101        public void iterate(StepListener listener, int nSteps) {
102
103                PointList newCurve = null;
104
105                for (int i = 0; i <= nSteps; i++) {
106                        newCurve = nextCurve((float)i/nSteps);
107                        Collections.reverse(newCurve.points);
108                        for (Point2d point2d : this.currentCurve) {
109                                if(!point2d.equals(this.startOfDragon))
110                                        newCurve.points.add(point2d);
111                        }
112                        prune(newCurve);
113                        Point2dImpl minXY = minXY(newCurve);
114                        Point2dImpl maxXY = maxXY(newCurve);
115                        double bbScalar = Math.max(maxXY.x - minXY.x,maxXY.y - minXY.y);
116                        Matrix translateToPointMatrix = TransformUtilities.translateToPointMatrix(
117                                        minXY,
118                                        new Point2dImpl(1/3f,1/3f)
119                        );
120                        Matrix translate = translateToPointMatrix;
121                        float d = (float) ((1/3f)/bbScalar);
122                        Matrix scale = TransformUtilities.scaleMatrix(d, d);
123                        Matrix transform = translate.times(scale);
124        //              Matrix transform = translate;
125                        newCurve = newCurve.transform(transform);
126                        listener.state(newCurve);
127                }
128                this.currentCurve = newCurve;
129                this.startOfDragon = newCurve.points.get(0);
130                this.endOfDragon = newCurve.points.get(newCurve.points.size()-1);
131        }
132
133
134        private void prune(PointList newCurve) {
135                Point2d prev = null;
136                for (Iterator<Point2d> iterator = newCurve.iterator(); iterator.hasNext();) {
137                        Point2d p = iterator.next();
138                        if(prev!= null){
139                                if(Line2d.distance(prev, p) < 1/200f){
140                                        iterator.remove();
141                                }
142                                else{
143                                        prev = p;
144                                }
145                        }else{
146                                prev = p;
147                        }
148                }
149        }
150
151        private Point2dImpl minXY(PointList newCurve) {
152                float minX = Float.MAX_VALUE;
153                float minY = Float.MAX_VALUE;
154                for (Point2d point2d : newCurve) {
155                        float px = point2d.getX();
156                        float py = point2d.getY();
157                        minX = Math.min(px, minX);
158                        minY = Math.min(py, minY);
159                }
160                return new Point2dImpl(minX,minY);
161        }
162
163        private Point2dImpl maxXY(PointList newCurve) {
164                float maxX = Float.MIN_VALUE;
165                float maxY = Float.MIN_VALUE;
166                for (Point2d point2d : newCurve) {
167                        float px = point2d.getX();
168                        float py = point2d.getY();
169                        maxX = Math.max(px, maxX);
170                        maxY = Math.max(py, maxY);
171                }
172                return new Point2dImpl(maxX,maxY);
173        }
174
175
176        public static void main(String[] args) throws InterruptedException {
177                final MBFImage img = new MBFImage(800,800,3);
178                final Matrix scaleMatrix = TransformUtilities.scaleMatrix(img.getWidth(), img.getHeight());
179                DragonCurve curve = new DragonCurve();
180                while(true){
181
182                        curve.iterate(new StepListener() {
183
184                                @Override
185                                public void state(PointList curve) {
186                                        img.fill(RGBColour.BLACK);
187                                        Point2d prev = null;
188                                        for (Point2d p : curve) {
189                                                p = p.transform(scaleMatrix);
190                                                if(prev!=null){
191                                                        img.drawLine(p, prev, 1,RGBColour.RED);
192                                                }
193                                                prev = p;
194                                        }
195                                        DisplayUtilities.displayName(img,"dragon");
196                                        try {
197                                                Thread.sleep(1);
198                                        } catch (InterruptedException e) {
199                                                // TODO Auto-generated catch block
200                                                e.printStackTrace();
201                                        }
202                                }
203                        },100);
204
205                }
206        }
207
208        private PointList getScaledCurve(int width, int height) {
209                return this.currentCurve.clone().scaleXY(width, height);
210        }
211}