I am trying to generate a height map using Perlin Noise, but am having trouble with generating truly unique maps. That is, each one is a minor variation of all the others. Two examples are below:
And here is my code (most was just copied and pasted from Ken Perlin's implementation, though adapted for 2D):
public class HeightMap {
private ArrayList<Point> map = new ArrayList<>();
private double elevationMax, elevationMin;
private final int[] P = new int[512], PERMUTATION = { 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
public HeightMap() {
this.map = null;
this.elevationMax = 0.0;
this.elevationMin = 0.0;
}
public HeightMap(HeightMap map) {
this.map = map.getPoints();
this.elevationMax = map.getElevationMax();
this.elevationMin = map.getElevationMin();
}
/**
* Generates a Height Map that is, along an imaginary z-axis, centered around the median elevation, given the following parameters:
* #param mapWidth the width [x] of the map
* #param mapHeight the height [y] of the map
* #param tileWidth the width [x] of each tile, or Point
* #param tileHeight the height [y] of each tile, or Point
* #param elevationMax the maximum elevation [z] of the map
* #param elevationMin the minimum elevation [z] of the map
*/
public HeightMap(int mapWidth, int mapHeight, int tileWidth, int tileHeight, double elevationMax, double elevationMin) {
this.elevationMax = elevationMax;
this.elevationMin = elevationMin;
for (int i=0; i < 256 ; i++) {
P[256+i] = P[i] = PERMUTATION[i];
}
int numTilesX = mapWidth / tileWidth;
int numTilesY = mapHeight / tileHeight;
Random r = new Random();
for (int t = 0; t < numTilesX * numTilesY; t++) {
double x = t % numTilesX;
double y = (t - x) / numTilesX;
r = new Random();
x += r.nextDouble();
y += r.nextDouble();
this.map.add(new Point(x, y, lerp(noise(x, y, 13), (elevationMin + elevationMax) / 2, elevationMax), tileWidth, tileHeight));
}
}
/**
* Ken Perlin's Improved Noise Java Implementation (https://mrl.cs.nyu.edu/~perlin/noise/)
* Adapted for 2D
* #param x the x-coordinate on the map
* #param y the y-coordinate on the map
* #param stretch the factor by which adjacent points are smoothed
* #return a value between -1.0 and 1.0 to represent the height of the terrain at (x, y)
*/
private double noise(double x, double y, double stretch) {
x /= stretch;
y /= stretch;
int X = (int)Math.floor(x) & 255, Y = (int)Math.floor(y) & 255;
x -= Math.floor(x);
y -= Math.floor(y);
double u = fade(x),
v = fade(y);
int AA = P[P[X ] + Y ],
AB = P[P[X ] + Y + 1],
BA = P[P[X + 1] + Y ],
BB = P[P[X + 1] + Y + 1];
return lerp(v, lerp(u, grad(P[AA], x, y), grad(P[BA], x - 1, y)), lerp(u, grad(P[AB], x, y - 1), grad(P[BB], x - 1, y - 1)));
}
private double fade(double t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
private double lerp(double t, double a, double b) {
return a + t * (b - a);
}
//Riven's Optimization (http://riven8192.blogspot.com/2010/08/calculate-perlinnoise-twice-as-fast.html)
private double grad(int hash, double x, double y) {
switch(hash & 0xF)
{
case 0x0:
case 0x8:
return x + y;
case 0x1:
case 0x9:
return -x + y;
case 0x2:
case 0xA:
return x - y;
case 0x3:
case 0xB:
return -x - y;
case 0x4:
case 0xC:
return y + x;
case 0x5:
case 0xD:
return -y + x;
case 0x6:
case 0xE:
return y - x;
case 0x7:
case 0xF:
return -y - x;
default: return 0; // never happens
}
}
}
Is this problem inherent in Perlin Noise because the 'height' is calculated from nearly the same (x, y) coordinate each time? Is there a way to implement the noise function so that it doesn't depend on the (x, y) coordinate of each point but still looks like terrain? Any help is greatly appreciated.
With some help from a friend of mine, I resolved the problem. Because I was using the same PERMUTATION array each generation cycle, the noise calculation was using the same base values each time. To fix this, I made a method permute() that filled PERMUTATION with the numbers 0 to 255 in a random, non-repeating order. I changed the instantiation of PERMUTATION to just be a new int[].
private final int[] P = new int[512], PERMUTATION = new int[256];
...
public void permute() {
for (int i = 0; i < PERMUTATION.length; i++) {
PERMUTATION[i] = i;
}
Random r = new Random();
int rIndex, rIndexVal;
for (int i = 0; i < PERMUTATION.length; i++) {
rIndex = r.nextInt(PERMUTATION.length);
rIndexVal = PERMUTATION[rIndex];
PERMUTATION[rIndex] = PERMUTATION[i];
PERMUTATION[i] = rIndexVal;
}
}
I have a set of 3d points S.
I need to find the set X of all sets of points in S which are within manhattan distance d of each other.
i.e. for each set Y in X there exists atleast one point in 3d space that is within distance d of all points in Y
The length of set S will never be >20 but I will have to run this analysis on a stream of sets which are being produced at ~10 new sets per second, so whatever solution I use will have to be fairly efficient.
an example to help visualize the problem, given the following:
the output would be ((A,B), (B,C,E), (B,D,E))
we only care about the largest possible sets so the sets (B,C), (B,D), (B,E), (C,E) and (D,E), while within the given parameters, are not in the output given they are subsets of other sets in X
also this I'm doing this in java but any pointers in terms of algorithms or pseudo code would be greatly appreciated, thanks in advance.
A solution in pseudocode would be:
calculate_intersections(areas):
intersections = calculate every two intersecting areas
combinations = combine_intersections(intersections)
reduced = remove all sets in combinations that are included in bigger sets
combine_intersections(intersections):
do:
combinations = new HashSet
for s1 in intersections:
for s2 in intersections:
diff_1_2 = s1 \ s2
diff_2_1 = s2 \ s1
if diff_1_2.len == 1 && diff_2_1.len == 1:
union = diff_1_2 + diff_2_1
if union in intersections:
union2 = s1 + s2
if !union2 in intersections:
combinations.add(union)
while (combinations not empty)
An implementation in Java could look like this:
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.commons.collections4.SetUtils;
public class IntersectionSetCalculation {
private static class ManhattanDistanceArea {
private String id;
private Vector3D center;
private double distance;
public ManhattanDistanceArea(Vector3D center, double distance, String id) {
this.center = center;
this.distance = distance;
this.id = id;
}
#Override
public String toString() {
return id;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((center == null) ? 0 : center.hashCode());
long temp;
temp = Double.doubleToLongBits(distance);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ManhattanDistanceArea other = (ManhattanDistanceArea) obj;
if (center == null) {
if (other.center != null)
return false;
}
else if (!center.equals(other.center))
return false;
if (Double.doubleToLongBits(distance) != Double.doubleToLongBits(other.distance))
return false;
if (id == null) {
if (other.id != null)
return false;
}
else if (!id.equals(other.id))
return false;
return true;
}
public boolean intersects(ManhattanDistanceArea other) {
double maxDist = distance + other.distance;
return center.distance(other.center, 1) < maxDist;
}
}
/**
* Calculate the intersection of all areas (maximum of 2 areas in an intersection)
*/
public static Set<Set<ManhattanDistanceArea>> getIntersectingAreas(Set<ManhattanDistanceArea> areas) {
Set<Set<ManhattanDistanceArea>> intersections = new HashSet<Set<ManhattanDistanceArea>>();
for (ManhattanDistanceArea area : areas) {
for (ManhattanDistanceArea area2 : areas) {
if (!area.equals(area2) && area.intersects(area2)) {
HashSet<ManhattanDistanceArea> intersection = new HashSet<ManhattanDistanceArea>();
intersection.add(area);
intersection.add(area2);
intersections.add(intersection);
}
}
}
Set<Set<ManhattanDistanceArea>> combined = combineIntersections(intersections);
Set<Set<ManhattanDistanceArea>> reduced = reduceIntersections(combined);
return reduced;
}
/**
* Combine the small intersections (with a maximum of 2 areas in an intersection) to bigger intersections
*/
public static Set<Set<ManhattanDistanceArea>> combineIntersections(Set<Set<ManhattanDistanceArea>> inters) {
Set<Set<ManhattanDistanceArea>> intersections = new HashSet<Set<ManhattanDistanceArea>>(inters);
Set<Set<ManhattanDistanceArea>> combinations;
do {
combinations = new HashSet<Set<ManhattanDistanceArea>>();
for (Set<ManhattanDistanceArea> intersecting1 : intersections) {
for (Set<ManhattanDistanceArea> intersecting2 : intersections) {
Set<ManhattanDistanceArea> diff_1_2 = SetUtils.difference(intersecting1, intersecting2);
Set<ManhattanDistanceArea> diff_2_1 = SetUtils.difference(intersecting2, intersecting1);
if (diff_1_2.size() == 1 && diff_2_1.size() == 1) {
Set<ManhattanDistanceArea> union_1_2 = SetUtils.union(diff_1_2, diff_2_1);
if (intersections.contains(union_1_2)) {
Set<ManhattanDistanceArea> union = SetUtils.union(intersecting1, intersecting2);
if (!intersections.contains(union)) {
combinations.add(union);
}
}
}
}
}
intersections.addAll(combinations);
} while (!combinations.isEmpty());
return intersections;
}
/**
* Remove the small intersections that are completely covered by bigger intersections
*/
public static Set<Set<ManhattanDistanceArea>> reduceIntersections(Set<Set<ManhattanDistanceArea>> inters) {
Set<Set<ManhattanDistanceArea>> intersections = new HashSet<Set<ManhattanDistanceArea>>(inters);
Iterator<Set<ManhattanDistanceArea>> iter = intersections.iterator();
while (iter.hasNext()) {
Set<ManhattanDistanceArea> intersection = iter.next();
for (Set<ManhattanDistanceArea> intersection2 : inters) {
if (intersection2.size() > intersection.size() && intersection2.containsAll(intersection)) {
iter.remove();
break;
}
}
}
return intersections;
}
public static void main(String[] args) {
final double dist = 2d;//the manhattan distance d
ManhattanDistanceArea A = new ManhattanDistanceArea(new Vector3D(0, -3, 0), dist, "A");
ManhattanDistanceArea B = new ManhattanDistanceArea(new Vector3D(0, 0, 0), dist, "B");
ManhattanDistanceArea C = new ManhattanDistanceArea(new Vector3D(3.5, 0, 0), dist, "C");
ManhattanDistanceArea D = new ManhattanDistanceArea(new Vector3D(0, 3.5, 0), dist, "D");
ManhattanDistanceArea E = new ManhattanDistanceArea(new Vector3D(1, 1, 0), dist, "E");
ManhattanDistanceArea F = new ManhattanDistanceArea(new Vector3D(-1, 1, 0), dist, "F");
//test the example you provided
Set<ManhattanDistanceArea> abcde = new HashSet<ManhattanDistanceArea>();
abcde.addAll(Arrays.asList(new ManhattanDistanceArea[] {A, B, C, D, E}));
//test another example
Set<ManhattanDistanceArea> abcdef = new HashSet<ManhattanDistanceArea>();
abcdef.addAll(abcde);
abcdef.add(F);
Set<Set<ManhattanDistanceArea>> intersectionsABCDE = getIntersectingAreas(abcde);
Set<Set<ManhattanDistanceArea>> intersectionsABCDEF = getIntersectingAreas(abcdef);
System.out.println(intersectionsABCDE);
System.out.println(intersectionsABCDEF);
//test the runntime for 1000 calculation
double startTime = System.currentTimeMillis();
final int calculations = 1000;
for (int i = 0; i < calculations; i++) {
Set<ManhattanDistanceArea> areas = new HashSet<ManhattanDistanceArea>();
for (int j = 0; j < 20; j++) {
areas.add(new ManhattanDistanceArea(new Vector3D(Math.random() * 10 - 5, Math.random() * 10 - 5, Math.random() * 10 - 5), dist,
"A" + j));
}
getIntersectingAreas(areas);
}
System.out.println("\nTime used for " + calculations + " intersection calculations (with sets of size 20): "
+ (System.currentTimeMillis() - startTime) + "ms");
}
}
For the implementation I used this class Vector3D:
public class Vector3D {
public double x;
public double y;
public double z;
public static final Vector3D NAN_VEC = new Vector3D(Double.NaN, Double.NaN, Double.NaN);
public static final Vector3D NULL_VEC = new Vector3D(0, 0, 0);
public enum Axis {
X, Y, Z;
}
public Vector3D() {
}
/**
* Crate a new Vector2D with x and y components.
*/
public Vector3D(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Vector3D(double... val) {
x = val[0];
y = val[1];
z = val[2];
}
/**
* Create a Vector3D by two angles (in degree).
*
* The first angle is in XY direction. The second angle is the Z direction.
*
* An angle (XY) of 0° results in (x, y) = (1, 0); 90° in (x, y) = (0, 1); ... An angle (Z) of 0° results in (x, y, z) = (x, y, 0); 90° in (x, y,
* z) = (x, y, 1); -90° in (x, y, z) = (x, y, -1)
*
* The resulting vector has a length of 1.
*
* #param angleXY
* The angle of the new vector (in degree) for the XY direction (from 0 to 360).
*
* #param angleZ
* The angle of the new vector (in degree) for the Z direction (from -90 to 90).
*/
public Vector3D(double angleXY, double angleZ) {
x = Math.cos(angleXY * Math.PI / 180) * Math.cos(angleZ * Math.PI / 180);
y = Math.sin(angleXY * Math.PI / 180) * Math.cos(angleZ * Math.PI / 180);
z = Math.sin(angleZ * Math.PI / 180);
double len = length();
x /= len;
y /= len;
z /= len;
}
private Vector3D(Vector3D clone) {
this.x = clone.x;
this.y = clone.y;
}
#Override
public Vector3D clone() {
return new Vector3D(this);
}
#Override
public String toString() {
return "Vector3D[x: " + x + " y: " + y + " z:" + z + "]";
}
#Override
public boolean equals(Object obj) {
if (obj instanceof Vector3D) {
Vector3D v = (Vector3D) obj;
return Math.abs(x - v.x) < 1e-8 && Math.abs(y - v.y) < 1e-8 && Math.abs(z - v.z) < 1e-8;
}
return false;
}
/**
* Get this vector as 3D-Array.
*/
public double[] asArray() {
return new double[] {x, y, z};
}
/**
* The (euclidean) length of the Vector.
*/
public double length() {
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
}
/**
* The length of this vector in a given norm.
*
* #param norm
* The norm of the vector length.
*
* #return The length of this vector in the given norm.
*/
public double length(int norm) {
if (norm == Integer.MAX_VALUE) {
return Math.max(Math.max(x, y), z);
}
return Math.pow(Math.pow(x, norm) + Math.pow(y, norm) + Math.pow(z, norm), 1.0 / norm);
}
/**
* Rotate this vector an angle (in degrees) around an axis resulting in a new Vector that is returned.
*
* #param degrees
* The angle to return the vector.
*
* #param axis
* The axis around which the vector is rotated.
*
* #return The new created vector.
*/
public Vector3D rotate(double degrees, Axis axis) {
double cos = Math.cos(degrees * Math.PI / 180);
double sin = Math.sin(degrees * Math.PI / 180);
switch (axis) {
case X:
return new Vector3D(x, cos * y - sin * z, sin * y + cos * z);
case Y:
return new Vector3D(cos * x + sin * z, y, -sin * x + cos * z);
case Z:
return new Vector3D(cos * x - sin * y, sin * x + cos * y, z);
default:
return null;
}
}
/**
* Project the vector given as parameter on this vector.
*
* #param vec
* The vector that is to be projected on this vector.
*
* #return The projected vector.
*/
public Vector3D project(Vector3D vec) {
return mult(scalar(vec) / Math.pow(length(), 2));
}
/**
* Add another Vector3D to this vector resulting in a new Vector that is returned.
*
* #param vec
* The vector added to this vector.
*
* #return The new created vector.
*/
public Vector3D add(Vector3D vec) {
return new Vector3D(x + vec.x, y + vec.y, z + vec.z);
}
/**
* Subtract another Vector3D from this vector resulting in a new Vector that is returned.
*
* #param vec
* The vector subtracted from this vector.
*
* #return The new created vector.
*/
public Vector3D sub(Vector3D vec) {
return new Vector3D(x - vec.x, y - vec.y, z - vec.z);
}
/**
* Multiply this vector with a scalar resulting in a new Vector that is returned.
*
* #param scalar
* The scalar to multiply this vector with.
*
* #return The new created vector.
*/
public Vector3D mult(double scalar) {
return new Vector3D(x * scalar, y * scalar, z * scalar);
}
/**
* Check whether this vector is linearly dependent to the parameter vector.
*
* #param vec
* The checked vector.
*
* #return True if the vectors are linearly dependent. False otherwise.
*/
public boolean isLinearlyDependent(Vector3D vec) {
double t1 = (x == 0 ? 0 : vec.x / x);
double t2 = (y == 0 ? 0 : vec.y / y);
double t3 = (z == 0 ? 0 : vec.z / z);
return Math.abs(t1 - t2) < 1e-5 && Math.abs(t1 - t3) < 1e-5 && t1 != 0;//all parameters t are equal and != 0
}
/**
* Calculate the scalar product of this vector and the parameter vector.
*
* #param vec
* The vector to calculate the scalar with this vector.
*
* #return The scalar of the vectors.
*/
public double scalar(Vector3D vec) {
return this.x * vec.x + this.y * vec.y + this.z * vec.z;
}
/**
* Calculate the cross product of this vector with another vector (resulting vector = this X parameter vector)
*
* #param vec
* The second vector for the cross product calculation.
*
* #return The cross product vector of the two vectors.
*/
public Vector3D cross(Vector3D vec) {
return new Vector3D(y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x);
}
/**
* Create a new vector with the same direction but a different length as this vector.
*
* #param length
* The length of the new vector.
*
* #return The new vector with a new length.
*/
public Vector3D setLength(double length) {
double len = length();
return new Vector3D(x * length / len, y * length / len, z * length / len);
}
/**
* Get the distance of this point's position vector to another point's position vector.
*
* #param p
* The second point's position vector.
*
* #return The distance between the points.
*/
public double distance(Vector3D p) {
return Math.sqrt((this.x - p.x) * (this.x - p.x) + (this.y - p.y) * (this.y - p.y) + (this.z - p.z) * (this.z - p.z));
}
/**
* Get the distance of this point's position vector to another point's position vector in a given norm.
*
* #param p
* The second point's position vector.
*
* #param norm
* The norm in which the distance is calculated (1 -> manhattan, 2 -> euclide, ...)
*
* #return The distance between the points in the given norm.
*/
public double distance(Vector3D p, int norm) {
return Math.pow((Math.pow(Math.abs(this.x - p.x), norm) + Math.pow(Math.abs(this.y - p.y), norm) + Math.pow(Math.abs(this.z - p.z), norm)),
1d / norm);
}
/**
* Change this vector to the new coordinates.
*/
public void move(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
/**
* Move a point's position vector in a direction (by a vector) and a distance.
*
* #param p
* The direction vector.
*
* #param distance
* The distance to move the new vector
*
* #return The new created vector.
*/
public Vector3D moveTo(Vector3D p, double distance) {
double d = distance(p);
double dx = p.x - x;
double dy = p.y - y;
double dz = p.z - z;
double coef = distance / d;
return new Vector3D(x + dx * coef, y + dy * coef, z + dz * coef);
}
/**
* Get the angle difference of this vector to another vector.
*
* #param vec
* The other vector.
*
* #return The angle difference of the two vectors (from 0° to 180°).
*/
public double getAngleTo(Vector3D vec) {
double angle = Math.acos(scalar(vec) / (length() * vec.length())) * 180 / Math.PI;
if (angle > 180) {
angle = 360 - angle;
}
return angle;
}
/**
* Get the vector from this point to another.
*
* #param vec
* The point to which the vector is calculated.
*
* #return The vector from this points position vector to the other point.
*/
public Vector3D vectorTo(Vector3D vec) {
return new Vector3D(vec.x - x, vec.y - y, vec.z - z);
}
/**
* Checks whether a point (by its position vector) is in a given range of this point.
*
* #param p
* The point that is checked.
*
* #param range
* The range used for the check.
*
* #return True if the point is in the range of this point (distance <= range).
*/
public boolean isInRange(Vector3D p, double range) {
return p != this && distance(p) <= range;
}
}
and the class SetUtils from the apache commons lib.
I also added some tests:
the test from your question
another test with a bigger intersection set
a test for the runtime
The results are:
[[A, B], [B, E, C], [B, E, D]]
[[A, B], [B, E, C], [D, E, F, B]]
Time used for 1000 intersection calculations (with sets of size 20):
791.0ms
So the results seem to be correct and you can calculate more than 1000 intersections in a second.
Exhaustive distance computation between 20 points, i.e. 190 distances is nothing for a PC. Time will measure in microseconds. You can draw the desired information from the "close to" relation encoded in a matrix.
I am currently on spring break and since I only work 25 hours a week I have too much free time on my hands. I am a Computer Science student and I decided to spend my free time improving my coding skills and my understanding of electromagnetic physics by creating a very simple calculator that is used to solve Coulomb's law with vectors which are used to solve the force between any two charged particles in a system of charged particles.
Now I have a somewhat accurate calculator working, I still have a few minor inaccuracies that I need to address but the code can be found down below, but it still works enough for the next step I would like to achieve.
This next step for me is to create a GUI and an interactive graph for the users to see. I say interactive because my ideal program would give the user the ability to drag and drop points on the canvas all the while the program is calculating the force vectors and net force that is present between the points the user places on the space. I would also be satisfied with just being able to draw the graph given the charges and XY-coordinates as well.
My question is does anyone know of any good tutorials or respectable libraries that I can look into to achieve either of these two ideas? I have a strong background in Java so I would prefer this to be done in Java, but I am never one to step away from learning a new language when I need too.
Code:
Charge.java
package objects;
/**
* Charge is an object used to represent an arbitrarily charged particle in some
* XY-plane. This class will store the value of the charge, the XY-coordinates
* of that charge's location, and functions to access these values.
*
* #author Seth
*
*/
public class Charge {
private double q; // the charge of the particle in Coulombs
private double x; // the location along the x-axis of the charge
private double y; // the location along the y-axis of the charge
public Charge(double x, double y, double q) {
this.x = x;
this.y = y;
this.q = q;
}
/**
* public method to get q
*
* #return
* double - value of the Charge object
*/
public double getQ() {
if (this != null)
return q;
else
return 0;
}
/**
* public method to get x
*
* #return
* double - value of the x coordinate
*/
public double getX() {
if (this != null)
return x;
else
return 0;
}
/**
* public method to get y
*
* #return
* double - value of the y coordinate
*/
public double getY() {
if (this != null)
return y;
else
return 0;
}
}
ElectricForceCalculator.java
package objects;
/**
* ElectricForceCalculator is a class that has the ability to solve the net
* force between two charged particles in a xy-plane
*
* #author Seth
*
*/
public class ElectricForceCalculator {
/**
* Private method used to find the force between two charges
*
* #param Q
* Charge - The charged we are focused on
* #param q
* Charge - The second charge effecting Q
* #return double array - the force vectors found where i = 0 is the x
* direction and i = 1 is the y direction
*/
private double[] calcForceHelper(Charge Q, Charge q) {
double radius, quanOfCharge;
double[] radiusVector = new double[2], forceVector = new double[2];
double x1 = Q.getX(), y1 = Q.getY();
double x2 = q.getX(), y2 = q.getY();
double q1 = Q.getQ(), q2 = q.getQ();
System.out.print("Calculating radius of Q and q...\n\t");
radius = calcRadius(x1, x2, y1, y2);
System.out.print("Calculating radiusVector of Q and q...\n\t");
radiusVector = calcRadiusVector(x1, x2, y1, y2, radius);
System.out.print("Calculating quantityOfCharge of Q and q...\n\t");
quanOfCharge = calcCharge(q1, q2, radius);
System.out.print("Calculating Force Vector of Q and q...\n\t");
forceVector = calcForceVector(radius, radiusVector, quanOfCharge);
return forceVector;
}
/**
* Public method used to find the overall net force of a system of charged
* particles
*
* #param Q
* Charge - the Charge object we are focused on
* #param q
* Charge array - the list of all the other Charge object
* effecting Q
* #param numCharges
* int - the number of indices found in the list of Charge
* objects q
* #return double - value of the net force experienced by Charge object Q
*/
public double calcForce(Charge Q, Charge[] q, int numCharges) {
double result;
double[] sum = { 0.0, 0.0 }, tempSum = new double[2];
for (int i = 0; i < numCharges - 1; i++) {
tempSum = calcForceHelper(Q, q[i]); // Help with readability to call
// method helper
fixSigns(Q, q[i], tempSum);
sum[0] += tempSum[0];
sum[1] += tempSum[1];
}
System.out.println("\nOverall force Vector on Q = (" + sum[0] + ")x + (" + sum[1] + ")y.");
result = Math.pow(sum[0], 2) + Math.pow(sum[1], 2);
result = Math.sqrt(result);
double theta = calcTheta(sum);
System.out.print("Force on charge Q by all other charges = " + result + " at ");
if (sum[1] > 0) {
if (sum[0] > 0)
System.out.print((float) Math.round(90 - theta) + " degrees.");
else
System.out.print((float) Math.round(180 - theta) + " degrees.");
} else {
if (sum[0] > 0)
System.out.print((float) Math.round(180 + theta) + " degrees.");
else
System.out.print((float) Math.round(270 + theta) + " degrees.");
}
return result;
}
/**
* Method used to assign negative values to any forces that should be
* negative
*
* #param Q
* Charge - the charge we are focused on
* #param q
* charge - the charge effecting Q
* #param tempSum
* the force vectors we are fixing, if needed
*/
private void fixSigns(Charge Q, Charge q, double[] tempSum) {
if (Q.getQ() < 0 && q.getQ() < 0) {
if (q.getX() > Q.getX())
tempSum[0] *= -1;
if (q.getY() > Q.getY())
tempSum[1] *= -1;
} else if (Q.getQ() > 0 && q.getQ() > 0) {
if (q.getX() > Q.getX())
tempSum[0] *= -1;
if (q.getY() > Q.getY())
tempSum[1] *= -1;
} else {
if (q.getX() < Q.getX())
tempSum[0] *= -1;
if (q.getY() < Q.getY())
tempSum[1] *= -1;
}
}
/**
* Private method used to calculate the angle Theta between two vectors
*
* #param forceVector
* double array - the force vectors we are finding the angle
* between
* #return double - the angle between the two force vectors given
*/
private double calcTheta(double[] vectors) {
double result = Math.abs(vectors[1]) / Math.abs(vectors[0]);// finds
// angle
// between 0
// and 90
result = Math.toDegrees(Math.atan(result));
return result;
}
/**
* Private method used to calculate the force vectors experienced by a
* charged particle
*
* #param radius
* double - distance between two charges
* #param radiusVector
* double array - the radius vectors between two charges
* #param quanOfCharge
* double - the quantity of charge given by k(coulomb's constant)
* * charge of Q * charge of q
* #return double array - the force vectors experienced by two charges
*/
private double[] calcForceVector(double radius, double[] radiusVector, double quanOfCharge) {
double[] result = new double[2];
double theta = calcTheta(radiusVector), magnitude = quanOfCharge;
result[0] = magnitude * Math.cos(Math.toRadians(theta));
result[1] = magnitude * Math.sin(Math.toRadians(theta));
System.out.print("Force Vector = (" + result[0] + ")x + (" + result[1] + ")y.\n");
return result;
}
/**
* private method used to calculate the quantity of charge between two
* charged particles(k * charge of Q * charge of q)
*
* #param Q
* double - the charge in Coulombs of the Charge object Q
* #param q
* double - the charge in Coulombs of the Charge object q
* #return double - the quantity of charge
*/
private double calcCharge(double Q, double q, double d) {
double result;
result = (((Q * q) / (d * d)) * 9E9);
result = Math.abs(result);
System.out.print("Quantity of charge(Q*q*k) = " + result + "\n");
return result;
}
/**
* Private method used to calculate the radius vector between two points on
* a xy-plane
*
* #param x1
* double - x coordinate of the Charge object Q
* #param x2
* double - y coordinate of the Charge object q
* #param y1
* double - x coordinate of the Charge object Q
* #param y2
* double - y coordinate of the Charge object q
* #param radius
* double - distance between the two points
* #return double array - the distance vectors between two charges
*/
private static double[] calcRadiusVector(double x1, double x2, double y1, double y2, double radius) {
double[] result = new double[2];
result[0] = (x2 - x1) / radius;
result[1] = (y2 - y1) / radius;
result[0] = Math.abs(result[0]);
result[1] = Math.abs(result[1]);
System.out.print("Radius vector = (" + result[0] + ")x + (" + result[1] + ")y\n");
return result;
}
/**
* Private method used to calculate the distance between two points on a
* xy-plane using the distance formula or [(x2-x1)+(y2-y1]^(1/2)
*
* #param x1
* double - x coordinate of the Charge object Q
* #param x2
* double - y coordinate of the Charge object q
* #param y1
* double - x coordinate of the Charge object Q
* #param y2
* double - y coordinate of the Charge object q
* #return double - distance between the two points given
*/
private double calcRadius(double x1, double x2, double y1, double y2) {
double distance = Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2);
distance = Math.sqrt(distance);
System.out.print("Radius = " + distance + "\n");
return distance;
}
/**
* Main
*
* #param args
*/
public static void main(String[] args) {
Charge Q = new Charge(-.02, .015, -4E-6);
Charge[] q = new Charge[2];
q[0] = new Charge(.035, .005, 3E-6);
q[1] = new Charge(-.0837, .0262, 4E-6);
ElectricForceCalculator calc = new ElectricForceCalculator();
calc.calcForce(Q, q, 3);
}
}
Sample output:
charge Q is -4.0E-6 C at (x,y) = (-0.02, 0.015).
charge q1 is 3.0E-6 C at (x,y) = (0.035, 0.005).
charge q1 is 4.0E-6 C at (x,y) = (-0.0837, 0.0262).
Calculating radius of Q and q...
Radius = 0.05590169943749475
Calculating radiusVector of Q and q...
Radius vector = (0.9838699100999075)x + (0.17888543819998312)y
Calculating quantityOfCharge of Q and q...
Quantity of charge(Q*q*k) = 34.559999999999995
Calculating Force Vector of Q and q...
Force Vector = (34.0025440930528)x + (6.182280744191415)y.
Calculating radius of Q and q...
Radius = 0.06467712114805357
Calculating radiusVector of Q and q...
Radius vector = (0.984892321570454)x + (0.17316788071568426)y
Calculating quantityOfCharge of Q and q...
Quantity of charge(Q*q*k) = 34.4239839545986
Calculating Force Vector of Q and q...
Force Vector = (33.903917474748674)x + (5.96112834720856)y.
Overall force Vector on Q = (0.09862661830412378)x + (-0.2211523969828546)y.
Force on charge Q by all other charges = 0.24214787327038295