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 org
.sourceforge
.desert
;
28 import java
.awt
.Dimension
;
29 import java
.util
.ArrayList
;
30 import java
.util
.Iterator
;
31 import java
.util
.List
;
33 import static java
.lang
.Math
.*;
34 import org
.sourceforge
.desert
.Particle
.Type
;
38 * @author codistmonk (creation 2010-04-13)
40 public class ParticleEngineCustomImplementation
implements ParticleEngine
{
42 private final List
<Particle
> particles
;
44 private float gravity
;
46 private Dimension boardSize
;
48 public ParticleEngineCustomImplementation() {
49 this.particles
= new ArrayList
<Particle
>();
50 this.gravity
= DEFAULT_GRAVITY
;
54 * Updates the positions and speeds of all the particles.
56 * <br>Range: <code>[0 .. Float.POSITIVE_INFINITY[</code>
59 public final void update(final float deltaTime
) {
60 for (final Particle particle
: this.particles
) {
61 final ParticleDefaultImplementation p
= (ParticleDefaultImplementation
) particle
;
63 if (particle
.getType().isMobile()) {
64 // Effect of gravity on speed
65 p
.setSpeedY(particle
.getSpeedY() + this.getGravity() * deltaTime
);
67 this.moveUntilCollision(p
, deltaTime
);
70 // Make sure that the particle doesn't get out if bounds are set
76 public final float getGravity() {
81 public final void setGravity(final float gravity
) {
82 this.gravity
= gravity
;
86 * Moves the particle according to its speed and deltaTime, but stop just before colliding
87 * with another particle.
88 * When a particle is stopped before a collision, is speed is set to 0F.
90 * <br>Should not be null
92 * <br>Range: <code>[0 .. Float.POSITIVE_INFINITY[</code>
94 private final void moveUntilCollision(final ParticleDefaultImplementation particle
, final float deltaTime
) {
95 float deltaX
= particle
.getSpeedX() * deltaTime
;
96 float deltaY
= particle
.getSpeedY() * deltaTime
;
97 final int pixelCount
= (int) ceil(max(abs(deltaX
), abs(deltaY
)));
99 if (pixelCount
!= 0) {
100 // Instead of moving the particle to its destination in one step,
101 // we are going to move it in as many steps as there are pixels
102 deltaX
/= pixelCount
;
103 deltaY
/= pixelCount
;
105 boolean collision
= false;
107 for (int i
= 0; i
< pixelCount
&& !collision
; ++i
) {
108 move(particle
, deltaX
, deltaY
);
110 // The goal is to go as far as possible without overlapping another particle
111 // Only the particles that have already been moved are tested for collision
112 // XXX this arbitrary and doesn't always prevent particles from overlapping
113 final Iterator
<Particle
> particleIterator
= this.particles
.iterator();
114 ParticleDefaultImplementation otherParticle
= (ParticleDefaultImplementation
) next(particleIterator
);
116 while (particle
!= otherParticle
&& otherParticle
!= null && !(collision
= collision(particle
, otherParticle
))) {
117 otherParticle
= (ParticleDefaultImplementation
) next(particleIterator
);
122 move(particle
, -deltaX
, -deltaY
);
123 particle
.setSpeedX(0F
);
124 particle
.setSpeedY(0F
);
130 * Constrains the particle inside the bounding rectangle if it exists.
132 * <br>Should not be null
133 * <br>Input-output parameter
135 private final void constrain(final ParticleDefaultImplementation particle
) {
136 if (this.getBoardSize() != null) {
138 if (particle
.getX() < 0F
) {
140 particle
.setSpeedX(0F
);
143 if (particle
.getY() < 0F
) {
145 particle
.setSpeedY(0F
);
148 if (particle
.getX() >= this.getBoardSize().width
) {
149 particle
.setX(this.getBoardSize().width
- 1F
);
150 particle
.setSpeedX(0F
);
153 if (particle
.getY() >= this.getBoardSize().height
) {
154 particle
.setY(this.getBoardSize().height
- 1);
155 particle
.setSpeedY(0F
);
163 * <br>Should no be null
164 * <br>Input-output parameter
165 * @return the next available element if there is one, or else null
166 * <br>A possibly null value
169 private static final <T
> T
next(final Iterator
<T
> iterator
) {
170 return iterator
.hasNext() ? iterator
.next() : null;
174 * TODO move this function into a ParticleUtilities class, or ParticleDefaultImplementation itself
176 * <br>Should not be null
178 * <br>Range: <code>]Float.NEGATIVE_INFINITY .. Float.POSITIVE_INFINITY[</code>
180 * <br>Range: <code>]Float.NEGATIVE_INFINITY .. Float.POSITIVE_INFINITY[</code>
182 private static final void move(final ParticleDefaultImplementation particle
, final float deltaX
, final float deltaY
) {
183 particle
.setX(particle
.getX() + deltaX
);
184 particle
.setY(particle
.getY() + deltaY
);
188 * TODO move this function into a ParticleUtilities class
190 * <br>Should not be null
192 * <br>Should not be null
193 * @return <code>distance(particle1, particle2) <= particle1.getRadius() + particle2.getRadius()</code>
194 * <br>A non-null value
196 private static final Boolean
collision(final Particle particle1
, final Particle particle2
) {
197 return distance(particle1
, particle2
) <= particle1
.getType().getRadius() + particle2
.getType().getRadius();
201 * TODO move this function into a ParticleUtilities class
203 * <br>Should not be null
205 * <br>Should not be null
206 * @return <code>max(abs(particle1.getX() - particle2.getX()), abs(particle1.getY() - particle2.getY()))</code>
207 * <br>A non-null value
208 * <br>Range: <code>[0 .. Float.POSITIVE_INFINITY[</code>
210 private static final float distance(final Particle particle1
, final Particle particle2
) {
211 return max(abs(particle1
.getX() - particle2
.getX()), abs(particle1
.getY() - particle2
.getY()));
215 public final Dimension
getBoardSize() {
216 return this.boardSize
;
220 public final void setBoardSize(final Dimension size
) {
221 this.boardSize
= size
;
225 public final void addParticle(final Type type
, final float x
, final float y
, final float speedX
, final float speedY
) {
226 this.particles
.add(new ParticleDefaultImplementation(type
, x
, y
, speedX
, speedY
));
230 public int getParticleCount() {
231 return this.particles
.size();
235 public Iterator
<Particle
> iterator() {
236 return ((Iterable
<Particle
>) this.particles
).iterator();