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}