Skip to content

Commit a993215

Browse files
authored
add Koch snowflake (TheAlgorithms#2168)
* add KochSnowflake.java * add package
1 parent 57bf40b commit a993215

File tree

1 file changed

+234
-0
lines changed

1 file changed

+234
-0
lines changed

Others/KochSnowflake.java

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
package Others;
2+
3+
import java.awt.*;
4+
import java.awt.image.BufferedImage;
5+
import java.io.File;
6+
import java.io.IOException;
7+
import java.util.ArrayList;
8+
import javax.imageio.ImageIO;
9+
10+
/**
11+
* The Koch snowflake is a fractal curve and one of the earliest fractals to have been described.
12+
* The Koch snowflake can be built up iteratively, in a sequence of stages. The first stage is an
13+
* equilateral triangle, and each successive stage is formed by adding outward bends to each side of
14+
* the previous stage, making smaller equilateral triangles. This can be achieved through the
15+
* following steps for each line: 1. divide the line segment into three segments of equal length. 2.
16+
* draw an equilateral triangle that has the middle segment from step 1 as its base and points
17+
* outward. 3. remove the line segment that is the base of the triangle from step 2. (description
18+
* adapted from https://en.wikipedia.org/wiki/Koch_snowflake ) (for a more detailed explanation and
19+
* an implementation in the Processing language, see
20+
* https://natureofcode.com/book/chapter-8-fractals/ #84-the-koch-curve-and-the-arraylist-technique
21+
* ).
22+
*/
23+
public class KochSnowflake {
24+
25+
public static void main(String[] args) {
26+
// Test Iterate-method
27+
ArrayList<Vector2> vectors = new ArrayList<Vector2>();
28+
vectors.add(new Vector2(0, 0));
29+
vectors.add(new Vector2(1, 0));
30+
ArrayList<Vector2> result = Iterate(vectors, 1);
31+
32+
assert result.get(0).x == 0;
33+
assert result.get(0).y == 0;
34+
35+
assert result.get(1).x == 1. / 3;
36+
assert result.get(1).y == 0;
37+
38+
assert result.get(2).x == 1. / 2;
39+
assert result.get(2).y == Math.sin(Math.PI / 3) / 3;
40+
41+
assert result.get(3).x == 2. / 3;
42+
assert result.get(3).y == 0;
43+
44+
assert result.get(4).x == 1;
45+
assert result.get(4).y == 0;
46+
47+
// Test GetKochSnowflake-method
48+
int imageWidth = 600;
49+
double offsetX = imageWidth / 10.;
50+
double offsetY = imageWidth / 3.7;
51+
BufferedImage image = GetKochSnowflake(imageWidth, 5);
52+
53+
// The background should be white
54+
assert image.getRGB(0, 0) == new Color(255, 255, 255).getRGB();
55+
56+
// The snowflake is drawn in black and this is the position of the first vector
57+
assert image.getRGB((int) offsetX, (int) offsetY) == new Color(0, 0, 0).getRGB();
58+
59+
// Save image
60+
try {
61+
ImageIO.write(image, "png", new File("KochSnowflake.png"));
62+
} catch (IOException e) {
63+
e.printStackTrace();
64+
}
65+
}
66+
67+
/**
68+
* Go through the number of iterations determined by the argument "steps". Be careful with high
69+
* values (above 5) since the time to calculate increases exponentially.
70+
*
71+
* @param initialVectors The vectors composing the shape to which the algorithm is applied.
72+
* @param steps The number of iterations.
73+
* @return The transformed vectors after the iteration-steps.
74+
*/
75+
public static ArrayList<Vector2> Iterate(ArrayList<Vector2> initialVectors, int steps) {
76+
ArrayList<Vector2> vectors = initialVectors;
77+
for (int i = 0; i < steps; i++) {
78+
vectors = IterationStep(vectors);
79+
}
80+
81+
return vectors;
82+
}
83+
84+
/**
85+
* Method to render the Koch snowflake to a image.
86+
*
87+
* @param imageWidth The width of the rendered image.
88+
* @param steps The number of iterations.
89+
* @return The image of the rendered Koch snowflake.
90+
*/
91+
public static BufferedImage GetKochSnowflake(int imageWidth, int steps) {
92+
if (imageWidth <= 0) {
93+
throw new IllegalArgumentException("imageWidth should be greater than zero");
94+
}
95+
96+
double offsetX = imageWidth / 10.;
97+
double offsetY = imageWidth / 3.7;
98+
Vector2 vector1 = new Vector2(offsetX, offsetY);
99+
Vector2 vector2 =
100+
new Vector2(imageWidth / 2, Math.sin(Math.PI / 3) * imageWidth * 0.8 + offsetY);
101+
Vector2 vector3 = new Vector2(imageWidth - offsetX, offsetY);
102+
ArrayList<Vector2> initialVectors = new ArrayList<Vector2>();
103+
initialVectors.add(vector1);
104+
initialVectors.add(vector2);
105+
initialVectors.add(vector3);
106+
initialVectors.add(vector1);
107+
ArrayList<Vector2> vectors = Iterate(initialVectors, steps);
108+
return GetImage(vectors, imageWidth, imageWidth);
109+
}
110+
111+
/**
112+
* Loops through each pair of adjacent vectors. Each line between two adjacent vectors is divided
113+
* into 4 segments by adding 3 additional vectors in-between the original two vectors. The vector
114+
* in the middle is constructed through a 60 degree rotation so it is bent outwards.
115+
*
116+
* @param vectors The vectors composing the shape to which the algorithm is applied.
117+
* @return The transformed vectors after the iteration-step.
118+
*/
119+
private static ArrayList<Vector2> IterationStep(ArrayList<Vector2> vectors) {
120+
ArrayList<Vector2> newVectors = new ArrayList<Vector2>();
121+
for (int i = 0; i < vectors.size() - 1; i++) {
122+
Vector2 startVector = vectors.get(i);
123+
Vector2 endVector = vectors.get(i + 1);
124+
newVectors.add(startVector);
125+
Vector2 differenceVector = endVector.subtract(startVector).multiply(1. / 3);
126+
newVectors.add(startVector.add(differenceVector));
127+
newVectors.add(startVector.add(differenceVector).add(differenceVector.rotate(60)));
128+
newVectors.add(startVector.add(differenceVector.multiply(2)));
129+
}
130+
131+
newVectors.add(vectors.get(vectors.size() - 1));
132+
return newVectors;
133+
}
134+
135+
/**
136+
* Utility-method to render the Koch snowflake to an image.
137+
*
138+
* @param vectors The vectors defining the edges to be rendered.
139+
* @param imageWidth The width of the rendered image.
140+
* @param imageHeight The height of the rendered image.
141+
* @return The image of the rendered edges.
142+
*/
143+
private static BufferedImage GetImage(
144+
ArrayList<Vector2> vectors, int imageWidth, int imageHeight) {
145+
BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
146+
Graphics2D g2d = image.createGraphics();
147+
148+
// Set the background white
149+
g2d.setBackground(Color.WHITE);
150+
g2d.fillRect(0, 0, imageWidth, imageHeight);
151+
152+
// Draw the edges
153+
g2d.setColor(Color.BLACK);
154+
BasicStroke bs = new BasicStroke(1);
155+
g2d.setStroke(bs);
156+
for (int i = 0; i < vectors.size() - 1; i++) {
157+
int x1 = (int) vectors.get(i).x;
158+
int y1 = (int) vectors.get(i).y;
159+
int x2 = (int) vectors.get(i + 1).x;
160+
int y2 = (int) vectors.get(i + 1).y;
161+
162+
g2d.drawLine(x1, y1, x2, y2);
163+
}
164+
165+
return image;
166+
}
167+
168+
/** Inner class to handle the vector calculations. */
169+
private static class Vector2 {
170+
171+
double x, y;
172+
173+
public Vector2(double x, double y) {
174+
this.x = x;
175+
this.y = y;
176+
}
177+
178+
@Override
179+
public String toString() {
180+
return String.format("[%f, %f]", this.x, this.y);
181+
}
182+
183+
/**
184+
* Vector addition
185+
*
186+
* @param vector The vector to be added.
187+
* @return The sum-vector.
188+
*/
189+
public Vector2 add(Vector2 vector) {
190+
double x = this.x + vector.x;
191+
double y = this.y + vector.y;
192+
return new Vector2(x, y);
193+
}
194+
195+
/**
196+
* Vector subtraction
197+
*
198+
* @param vector The vector to be subtracted.
199+
* @return The difference-vector.
200+
*/
201+
public Vector2 subtract(Vector2 vector) {
202+
double x = this.x - vector.x;
203+
double y = this.y - vector.y;
204+
return new Vector2(x, y);
205+
}
206+
207+
/**
208+
* Vector scalar multiplication
209+
*
210+
* @param scalar The factor by which to multiply the vector.
211+
* @return The scaled vector.
212+
*/
213+
public Vector2 multiply(double scalar) {
214+
double x = this.x * scalar;
215+
double y = this.y * scalar;
216+
return new Vector2(x, y);
217+
}
218+
219+
/**
220+
* Vector rotation (see https://en.wikipedia.org/wiki/Rotation_matrix)
221+
*
222+
* @param angleInDegrees The angle by which to rotate the vector.
223+
* @return The rotated vector.
224+
*/
225+
public Vector2 rotate(double angleInDegrees) {
226+
double radians = angleInDegrees * Math.PI / 180;
227+
double ca = Math.cos(radians);
228+
double sa = Math.sin(radians);
229+
double x = ca * this.x - sa * this.y;
230+
double y = sa * this.x + ca * this.y;
231+
return new Vector2(x, y);
232+
}
233+
}
234+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy