Skip to content

Commit

Permalink
use coordinates [0, 1] instead of [-1, 1]
Browse files Browse the repository at this point in the history
  • Loading branch information
tom-mohr committed Sep 18, 2024
1 parent 390dcea commit 9d80030
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 64 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
// to match JitPack format 'com.github.User:Repo:Tag'
// when using local installs
group 'com.github.tom-mohr'
version 'v0.4.0'
version 'v0.5.0'

repositories {
mavenCentral()
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/particle_life/DefaultPositionSetter.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ public class DefaultPositionSetter implements PositionSetter {
@Override
public void set(Vector3d position, int type, int nTypes) {
position.set(
Math.random() * 2 - 1,
Math.random() * 2 - 1,
Math.random(),
Math.random(),
0
);
}
Expand Down
40 changes: 21 additions & 19 deletions src/main/java/com/particle_life/Physics.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class Physics {
// container layout:
private int nx;
private int ny;
private double containerSize = 0.13;//todo: implement makeContainerNeighborhood() to make this independent of rmax
private double containerSize = 0.065;//todo: implement makeContainerNeighborhood() to make this independent of rmax

public Accelerator accelerator;
public MatrixGenerator matrixGenerator;
Expand Down Expand Up @@ -80,12 +80,8 @@ public Physics(Accelerator accelerator,
}

private void calcNxNy() {
// nx = (int) Math.ceil(2 * range.range.x / containerSize);
// ny = (int) Math.ceil(2 * range.range.y / containerSize);

// currently, "floor" is needed (because containerSize = rmax), so that rmax lies inside "simple" neighborhood
nx = (int) Math.floor(2 / containerSize);
ny = (int) Math.floor(2 / containerSize);
nx = (int) Math.floor(1 / containerSize);
ny = (int) Math.floor(1 / containerSize);
}

private void makeContainerNeighborhood() {
Expand Down Expand Up @@ -304,7 +300,7 @@ private Particle generateParticle() {

protected final void setPosition(Particle p) {
positionSetter.set(p.position, p.type, settings.matrix.size());
Range.wrap(p.position);
ensurePosition(p.position);
p.velocity.x = 0;
p.velocity.y = 0;
p.velocity.z = 0;
Expand Down Expand Up @@ -368,8 +364,8 @@ private void makeContainers() {
* @return index of the container containing <code>position</code>
*/
private int getContainerIndex(Vector3d position) {
int cx = (int) ((position.x + 1) / containerSize);
int cy = (int) ((position.y + 1) / containerSize);
int cx = (int) (position.x / containerSize);
int cy = (int) (position.y / containerSize);

// for solid borders
if (cx == nx) {
Expand Down Expand Up @@ -409,8 +405,8 @@ private void updateVelocity(int i) {
double frictionFactor = Math.pow(settings.friction, 60 * settings.dt); // is normalized to 60 fps
p.velocity.mul(frictionFactor);

int cx0 = (int) Math.floor((p.position.x + 1) / containerSize);
int cy0 = (int) Math.floor((p.position.y + 1) / containerSize);
int cx0 = (int) Math.floor(p.position.x / containerSize);
int cy0 = (int) Math.floor(p.position.y / containerSize);

for (int[] containerNeighbor : containerNeighborhood) {
int cx = wrapContainerX(cx0 + containerNeighbor[0]);
Expand Down Expand Up @@ -457,24 +453,30 @@ private void updatePosition(int i) {
ensurePosition(p.position);
}

/**
* Calculates the shortest connection between two positions.
* If <code>settings.wrap == true</code>, the connection might
* go across the world's borders.
* @param pos1 first position, with coordinates in the range [0, 1].
* @param pos2 second position, with coordinates in the range [0, 1].
* @return the shortest connection between the two positions
*/
public Vector3d connection(Vector3d pos1, Vector3d pos2) {

Vector3d delta = new Vector3d(pos2).sub(pos1);

if (settings.wrap) {
// wrapping the connection gives us the shortest possible distance
Range.wrap(delta);
Range.wrapConnection(delta);
}

return delta;
}

/**
* Calculates the distance between two positions.
* If <code>settings.wrap == true</code>, this will
* return the shortest possible distance, even if
* that connection goes across the world's borders.
* Calculates the shortest distance between two positions.
* @return shortest possible distance between two points
* @see #connection(Vector3d, Vector3d)
*/
public double distance(Vector3d pos1, Vector3d pos2) {
return connection(pos1, pos2).length();
Expand All @@ -485,11 +487,11 @@ public double distance(Vector3d pos1, Vector3d pos2) {
* <ul>
* <li>
* If <code>settings.wrap == false</code>,
* the coordinates are simply clamped to [-1.0, 1.0].
* the coordinates are simply clamped to [0.0, 1.0].
* </li>
* <li>
* If <code>settings.wrap == true</code>,
* the coordinates are made to be inside [-1.0, 1.0) by adding or subtracting multiples of 2.
* the coordinates are made to be inside [0.0, 1.0) by adding or subtracting multiples of 1.
* </li>
* </ul>
* This method is called by {@link #update()} after changing the particles' positions.
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/particle_life/PhysicsSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class PhysicsSettings {
/**
* no interaction between particles that are further apart than rmax
*/
public double rmax = 0.04;
public double rmax = 0.02;

/**
* Friction coefficient.
Expand Down
94 changes: 53 additions & 41 deletions src/main/java/com/particle_life/Range.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,83 +3,95 @@
import org.joml.Vector3d;

/**
* Provides functions for assuring that the coordinates of particles are in [-1, 1].
* Provides functions for assuring that the coordinates of particles are in [0, 1].
* <p>Two approaches are possible:
* <ol>
* <li>{@link #clamp(Vector3d) Range.clamp(x)}<p>
* Leaves coordinates inside [-1, 1] untouched.
* All other coordinates are clamped between -1 and 1.
* Leaves coordinates inside [0, 1] untouched.
* All other coordinates are clamped between 0 and 1.
* <p>Example:
* <pre>
* x = new Vector3d(0.4, -1.3, 2.0);
* Range.clamp(x);
* // x is now (0.4, -1.0, 1.0).
* // x is now (0.4, 0.0, 1.0).
* </pre>
* </li>
* <li>{@link #wrap(Vector3d) Range.wrap(x)}<p>
* Leaves coordinates inside [-1, 1) untouched.
* All other coordinates are modified by repeatedly adding or subtracting 2 until they are in [-1, 1).
* Leaves coordinates inside [0, 1) untouched.
* All other coordinates are modified by repeatedly adding or subtracting 1 until they are in [0, 1).
* <p>Example:
* <pre>
* x = new Vector3d(0.4, -1.3, 2.0);
* x = new Vector3d(0.4, -0.3, 2.0);
* Range.wrap(x);
* // x is now (0.4, 0.7, 0.0).
* </pre>
* When using this approach, the shortest path from point <code>a</code> to point <code>b</code>
* can be determined using <code>wrap(b - a)</code>.
* </li>
* </ol>
*/
class Range {

/**
* Wraps the coordinates of <code>x</code> to [0, 1).
* <p>Coordinates are modified by repeatedly adding or subtracting 1 until they are in [0, 1).
* This will make the algorithm take longer for coordinates that are further away from [0, 1).
*
* @param x vector with coordinates in (-inf, +inf)
*/
public static void wrap(Vector3d x) {
x.x = wrap(x.x);
x.y = wrap(x.y);
x.z = 0; //todo 3D
}

private static double wrap(double value) {
if (value < 0) {
do {
value += 1;
} while (value < 0);
return value;
}
while (value >= 1) {
value -= 1;
}
return value;
}

/**
* Assumes that the coordinates of <code>x</code> are in (-1, 1),
* so that the wrapping algorithm can be simplified.
* This is the case for connections between points
* with coordinates in [0, 1).
* Performs a single wind wrap on the interval [-0.5, 0.5).
*
* @param x vector with coordinates in (-1, 1)
*/
public static void wrapConnection(Vector3d x) {
x.x = wrapConnection(x.x);
x.y = wrapConnection(x.y);
x.z = 0; //todo 3D
}

private static double wrapConnection(double value) {
if (value < -0.5) {
return value + 1;
} else if (value >= 0.5) {
return value - 1;
}
return value;
}

public static void clamp(Vector3d x) {
x.x = clamp(x.x);
x.y = clamp(x.y);
x.z = 0; // todo 3D
}

private static double wrap(double value) {
return modulo(value + 1, 2) - 1;
}

private static double clamp(double val) {
if (val < -1) {
return -1;
if (val < 0) {
return 0;
} else if (val > 1) {
return 1;
}
return val;
}

/**
* Fast implementation of the floored modulo operation.
* Only works for positive <code>b</code>, but <code>a</code> may be negative.
* <p>The speed of this implementation depends on the assumption
* that <code>a</code> is only a few times larger (positive or negative) than <code>b</code>.
*
* @param a dividend
* @param b divisor, must be positive
* @return remainder of <code>a / b</code>, always positive
*/
private static double modulo(double a, double b) {

if (a < 0) {
do {
a += b;
} while(a < 0);
return a;
} else if (a >= b) {
do {
a -= b;
} while (a >= b);
return a;
}
return a;
}
}

0 comments on commit 9d80030

Please sign in to comment.