2 * Copyright (c) 2010 The Desert team
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use,
8 * copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
26 package net
.sourceforge
.desert
;
28 import java
.awt
.image
.BufferedImage
;
30 import java
.io
.IOException
;
31 import java
.net
.JarURLConnection
;
33 import java
.util
.ArrayList
;
34 import java
.util
.Collections
;
35 import java
.util
.Enumeration
;
36 import java
.util
.Iterator
;
37 import java
.util
.LinkedList
;
38 import java
.util
.List
;
39 import java
.util
.jar
.JarEntry
;
40 import java
.util
.jar
.JarFile
;
41 import javax
.imageio
.ImageIO
;
43 import static java
.lang
.Math
.*;
47 * @author codistmonk (creation 2010-04-17)
49 public class Utilities
{
51 // TODO add automated tests
54 * Private default constructor to ensure that the class isn't instantiated.
60 public static final String RESOURCE_BASE
= "net/sourceforge/desert/resources/";
65 * <br>Should no be null
66 * <br>Input-output parameter
67 * @return the next available element if there is one, or else null
68 * <br>A possibly null value
71 public static final <T
> T
next(final Iterator
<T
> iterator
) {
72 return iterator
.hasNext() ? iterator
.next() : null;
76 * TODO move this function into a ParticleUtilities class, or ParticleDefaultImplementation itself
78 * <br>Should not be null
80 * <br>Range: <code>]Float.NEGATIVE_INFINITY .. Float.POSITIVE_INFINITY[</code>
82 * <br>Range: <code>]Float.NEGATIVE_INFINITY .. Float.POSITIVE_INFINITY[</code>
84 public static final void move(final ParticleDefaultImplementation particle
, final float deltaX
, final float deltaY
) {
85 particle
.setX(particle
.getX() + deltaX
);
86 particle
.setY(particle
.getY() + deltaY
);
90 * TODO move this function into a ParticleUtilities class
92 * <br>Should not be null
94 * <br>Should not be null
95 * @return <code>distance(particle1, particle2) <= particle1.getRadius() + particle2.getRadius()</code>
96 * <br>A non-null value
98 public static final Boolean
collision(final Particle particle1
, final Particle particle2
) {
99 return distance(particle1
, particle2
) <= particle1
.getType().getRadius() + particle2
.getType().getRadius();
103 * TODO move this function into a ParticleUtilities class
105 * <br>Should not be null
107 * <br>Should not be null
108 * @return <code>sqrt(square(particle1.getX() - particle2.getX()) + square(particle1.getY() - particle2.getY()))</code>
109 * <br>Range: <code>[0F .. Float.POSITIVE_INFINITY[</code>
111 public static final float distance(final Particle particle1
, final Particle particle2
) {
112 return length(particle1
.getX() - particle2
.getX(), particle1
.getY() - particle2
.getY());
118 * <br>Range: <code>]Float.NEGATIVE_INFINITY .. Float.OSITIVE_INFINITY[</code>
120 * <br>Range: <code>]Float.NEGATIVE_INFINITY .. Float.OSITIVE_INFINITY[</code>
122 * <br>Range: <code>[0F .. Float.POSITIVE_INFINITY[</code>
124 public static final float length(final float deltaX
, final float deltaY
) {
125 return LengthAlgorithm
.EUCLIDIAN
.length(deltaX
, deltaY
);
131 * <br>Range: <code>]Float.NEGATIVE_INFINITY .. Float.POSITIVE_INFINITY[</code>
133 * <br>Range: <code>[0F .. Float.POSITIVE_INFINITY[</code>
135 public static final float square(final float value
) {
136 return value
* value
;
141 * @param resourcePath
142 * <br>Should not be null
143 * <br>Eg: "org/sourceforge/desert/resources/images/"
144 * @return An unmodifiable list
145 * <br>A non-null value
146 * @throws IOException
148 public static final List
<String
> listSubresources(final String resourcePath
) throws IOException
{
149 final URL resourceURL
= Utilities
.class.getClassLoader().getResource(resourcePath
);
151 if (resourceURL
.getProtocol().equals("jar")) {
152 final JarURLConnection connection
= (JarURLConnection
) resourceURL
.openConnection();
154 return listSubentryNames(connection
.getJarFile(), connection
.getEntryName());
157 return listSubfilePaths(resourcePath
);
162 * Lists the subfiles of <code>path</code>.
163 * @param resourcePath
164 * <br>Should not be null
165 * @return An unmodifiable list; empty if <code>path</code> is not a directory
166 * <br>A non-null value
168 * @throws IOException
170 public static final List
<String
> listSubfilePaths(final String resourcePath
) throws IOException
{
171 final List
<String
> result
= new ArrayList
<String
>();
172 final File file
= new File(Utilities
.class.getClassLoader().getResource(resourcePath
).getFile());
174 if (file
.isDirectory()) {
175 final File
[] subfiles
= file
.listFiles();
177 if (subfiles
== null) {
178 throw new IOException("Could not list subfiles for " + file
);
181 for (final File subFile
: subfiles
) {
182 result
.add(resourcePath
+ File
.separator
+ subFile
.getName());
186 return Collections
.unmodifiableList(result
);
190 * Lists the subentries of the entry named <code>entryName</code> in <code>jarFile</code>.
192 * <br>Should not be null
194 * <br>Should not be null
195 * @return An unmodifiable list; empty if the entry is not a directory
197 * <br>A non-null value
199 public static final List
<String
> listSubentryNames(final JarFile jarFile
, final String entryName
) {
200 final List
<String
> result
= new ArrayList
<String
>();
202 for (final Enumeration
<JarEntry
> entries
= jarFile
.entries(); entries
.hasMoreElements();) {
203 final String otherEntryName
= entries
.nextElement().getName();
205 if (otherEntryName
.startsWith(entryName
) && !otherEntryName
.equals(entryName
+ "/")) {
206 result
.add(otherEntryName
);
210 return Collections
.unmodifiableList(result
);
215 * @return an unmodifiable list
216 * <br>A non-null value
218 * @throws RuntimeException if an IO exception occurs
220 public static final List
<String
> listBrushPaths() {
222 final List
<String
> result
= new LinkedList
<String
>(listSubresources(RESOURCE_BASE
+ "images/"));
224 for (final Iterator
<String
> iterator
= result
.iterator(); iterator
.hasNext();) {
225 final String resourcePath
= iterator
.next();
227 if (!getResourceName(resourcePath
).startsWith("brush_")) {
232 return Collections
.unmodifiableList(result
);
233 } catch (final IOException exception
) {
234 throw new RuntimeException(exception
);
240 * @param resourcePath
241 * <br>Should not be null
243 * <br>A non-null value
245 public static final String
getResourceName(final String resourcePath
) {
246 return resourcePath
.substring(resourcePath
.lastIndexOf(File
.separator
) + 1);
250 * A brush resource is an image where non-black pixels represent a particle generation point.
251 * @param resourcePath
252 * <br>Should not be null
253 * <br>Eg: "org/sourceforge/desert/resources/images/brush_dot_1x1.png"
254 * @return A boolean representation of the pixels of the resource image,
255 * where <code>true</code> indicate non-black pixels
256 * <br>A non-null value
258 * @throws RuntimeException if the resource couldn't be read
260 public static final boolean[][] getBrush(final String resourcePath
) {
262 final BufferedImage brush
= ImageIO
.read(Utilities
.class.getClassLoader().getResourceAsStream(resourcePath
));
264 final boolean[][] result
= new boolean[brush
.getHeight()][brush
.getWidth()];
266 for (int y
= 0; y
< brush
.getHeight(); ++y
) {
267 for (int x
= 0; x
< brush
.getWidth(); ++x
) {
268 // A non-black pixels means "put a particle here"
269 result
[y
][x
] = (brush
.getRGB(x
, y
) & 0x00FFFFFF) != 0;
274 } catch (final IOException exception
) {
275 throw new RuntimeException(exception
);
280 * Concatenates the source location of the call and the string representations
281 * of the parameters separated by spaces.
282 * <br>This is method helps to perform console debugging using System.out or System.err.
283 * @param stackIndex 1 is the source of this mehod, 2 is the source of the call, 3 is the source of the call's caller, and so forth
284 * <br>Range: <code>[O .. Integer.MAX_VALUE]</code>
286 * <br>Should not be null
289 * <br>A non-null value
291 public static final String
debug(final int stackIndex
, final Object
... objects
) {
292 final StringBuilder builder
= new StringBuilder(Thread
.currentThread().getStackTrace()[stackIndex
].toString());
294 for (final Object object
: objects
) {
295 builder
.append(" ").append(object
);
298 return builder
.toString();
302 * Prints on the standard output the concatenation of the source location of the call
303 * and the string representations of the parameters separated by spaces.
305 * <br>Should not be null
307 public static final void debugPrint(final Object
... objects
) {
308 System
.out
.println(debug(3, objects
));
314 * @author codistmonk (2010-04-28)
316 public static enum LengthAlgorithm
{
321 public final float length(final float deltaX
, final float deltaY
) {
322 return max(abs(deltaX
), abs(deltaY
));
330 public final float length(final float deltaX
, final float deltaY
) {
331 return (float) sqrt(square(deltaX
) + square(deltaY
));
339 * <br>Range: <code>]Float.NEGATIVE_INFINITY .. Float.POSITIVE_INFINITY[</code>
341 * <br>Range: <code>]Float.NEGATIVE_INFINITY .. Float.POSITIVE_INFINITY[</code>
343 * <br>Range: <code>[0F .. Float.POSITIVE_INFINITY[</code>
345 public abstract float length(float x
, float y
);