Complexity is Key

I got it to work perfectly! The output is a beautiful Peirce Quincuncial
projection. My main problem before was that this projection requires a
complex function called cn(u,k), and every single definition on the
internet was exceedingly vague. Luckily, I found a package online that
could calculate cn for me, which meant that I didn't need to do any
crazy integration or calculus whatsoever.
This commit is contained in:
Galactic Ketchup 2015-11-24 13:39:56 -05:00
parent c4d6a85f21
commit fe8a778365
7 changed files with 134 additions and 44 deletions

View File

@ -2,6 +2,7 @@
<classpath> <classpath>
<classpathentry kind="src" path="src"/> <classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="C:/Users/jkunimune/workspace/commons-math3-3.5/commons-math3-3.5.jar"/> <classpathentry kind="lib" path="C:/Users/jkunimune/workspace/ellipticFunctions/ellipticFunctions.jar"/>
<classpathentry kind="lib" path="C:/Users/jkunimune/workspace/ellipticFunctions/mfc.jar"/>
<classpathentry kind="output" path="bin"/> <classpathentry kind="output" path="bin"/>
</classpath> </classpath>

Binary file not shown.

BIN
bin/Number.class Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 KiB

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

@ -5,7 +5,8 @@ import java.util.Scanner;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.apache.commons.math3.complex.Complex; import ellipticFunctions.Jacobi;
import mfc.field.Complex;
/** /**
* *
@ -16,10 +17,10 @@ import org.apache.commons.math3.complex.Complex;
* *
*/ */
public class MapProjections { public class MapProjections {
private static final int QUINCUNCIAL = 3; private static final int QUINCUNCIAL = 4;
private static final int EQUIRECTANGULAR = 1; private static final int EQUIRECTANGULAR = 1;
private static final int MERCATOR = 2; private static final int MERCATOR = 2;
private static final int POLAR = 4; private static final int POLAR = 3;
@ -78,37 +79,13 @@ public class MapProjections {
/* PROJECTION METHODS: Return RGB at a given pixel based on a reference map and a unique projection method */ /* PROJECTION METHODS: Return RGB at a given pixel based on a reference map and a unique projection method */
public static int quincuncial(final double lat0, final double lon0, final double orientation, public static int quincuncial(final double lat0, final double lon0, final double orientation,
final int width, final int height, int x, int y, BufferedImage ref) { // a tessalatable square map final int width, final int height, int x, int y, BufferedImage ref) { // a tessalatable square map
final double dx = .01; Complex u = new Complex(3.7116*x/width, 3.7116*y/height-1.8558); // don't ask me where 3.7116 come from because I have no idea
Complex k = new Complex(Math.sqrt(0.5)); // the rest comes from some fancy complex calculus stuff
double phi, lambda; // the correct phi and lambda combination is the one where func goes to 0. Complex ans = Jacobi.cn(u, k);
double phiMin = -Math.PI/2; double p = 2*Math.atan(ans.abs());
double phiMax = Math.PI/2; double theta = Math.atan2(ans.getRe(), ans.getIm());
double lamMin = -Math.PI; double lambda = p-Math.PI/2;
double lamMax = Math.PI; return getColor(lambda,theta,ref);
double[] error = new double[3];
int i = 0;
do {
phi = (phiMax+phiMin)/2.0;
lambda = (lamMax+lamMin)/2.0;
error[0] = func(phi, lambda, 2.0*x/width-1, 2.0*y/height-1);
error[1] = func(phi+dx, lambda, 2.0*x/width-1, 2.0*y/height-1);
error[2] = func(phi, lambda+dx, 2.0*x/width-1, 2.0*y/height-1);
if (error[0] > error[1]) // phi is too small
phiMin = phi;
else // phi is too big
phiMax = phi;
if (error[0] > error[2]) // lambda is too small
lamMin = lambda;
else // lambda is too big
lamMax = lambda;
i ++;
} while (phiMax-phiMin > Math.PI/ref.getHeight() && lamMax-lamMin > 2*Math.PI/ref.getWidth() && i < 256);
return getColor(phi,lambda,ref);
} }
@ -182,17 +159,17 @@ public class MapProjections {
/* PRECONDITION: -1 <= x,y <= 1 */ /* PRECONDITION: -1 <= x,y <= 1 */
private static double func(double p, double l, double x, double y) { // a super complicated function that calculates stuff for peirce quincuncial private static double func(double p, double l, double x, double y) { // a super complicated function that calculates stuff for peirce quincuncial
final double rt2 = Math.sqrt(2); final double rt2 = Math.sqrt(2);
Complex X = new Complex(rt2*Math.tan(p/2)*Math.cos(l), rt2*Math.tan(p/2)*Math.sin(l)); Number X = new Number(rt2*Math.tan(p/2), l);
Complex Z = new Complex(x,y); // the point on the map Number Z = new Number(x,y,true); // the point on the map
// X*sqrt(-X^2/2 + 1)/2 // X*sqrt(-X^2/2 + 1)/2
Complex radical1 = X.multiply(X.multiply(X).divide(-2).add(1).pow(.5)).divide(2); Number radical1 = X.times(X.sqrd().over(-2).plus(1)).over(2);
// asin(X/sqrt(2))/sqrt(2) // asin(X/sqrt(2))/sqrt(2)
Complex arcSin = X.divide(rt2).asin().divide(rt2); Number arcSin = Number.asin(X.over(rt2)).over(rt2);
// X*sqrt(-X^2/4 + 1) // X*sqrt(-X^2/4 + 1)
Complex radical2 = X.multiply(X.multiply(X).divide(-4).add(1).pow(.5)); Number radical2 = X.times(Number.sqrt(X.sqrd().over(-4).plus(1)));
// 2*Z + pi*sqrt(2)/8 // 2*Z - pi*sqrt(2)/8
Complex finalBit = Z.multiply(2).subtract(Math.PI*rt2/8); Number finalBit = Z.times(2).minus(Math.PI*rt2/8);
System.out.println("F("+X+","+Z+") = "+(radical1.subtract(arcSin).subtract(radical2).subtract(finalBit)).abs()); //System.out.println("F("+X+","+Z+") = "+(radical1.subtract(arcSin).subtract(radical2).subtract(finalBit)).abs());
return (radical1.subtract(arcSin).subtract(radical2).subtract(finalBit)).abs(); return Number.abs(radical1.minus(radical2).minus(arcSin).minus(finalBit));
} }
} }

112
src/Number.java Normal file
View File

@ -0,0 +1,112 @@
public class Number {
private double r;
private double theta;
public static final Number ONE = new Number(1,0);
public Number(double mag, double ang) {
r = mag;
theta = ang;
}
public Number(double x, double y, boolean cartesian) {
r = Math.hypot(x, y);
theta = Math.atan2(y, x);
}
public Number plus(Number that) {
return new Number (this.x()+that.x(), this.y()+that.y(), true);
}
public Number plus(double x) {
return new Number (this.x()+x, this.y(), true);
}
public Number minus(Number that) {
return new Number (this.x()-that.x(), this.y()-that.y(), true);
}
public Number minus(double x) {
return new Number (this.x()-x, this.y(), true);
}
public Number times(Number that) {
return new Number (this.r()*that.r(), this.theta()+that.theta());
}
public Number times(double x) {
return new Number (r*x, theta);
}
public Number over(double x) {
return new Number (r/x, theta);
}
public Number sqrd() { // squares it
return new Number (r*r, theta*2);
}
public Number timesI() { // multiplies it by i
return new Number (-y(), x());
}
public Number overI() { // multiplies by negative I
return new Number (y(), -x());
}
public static Number sqrt(Number z) { // sqrts it
return new Number (Math.sqrt(z.r()), z.theta()/2);
}
public static Number ln(Number z) {
return new Number (Math.log(z.r()), z.theta(), true);
}
public static Number asin(Number z) {
return Number.ln(z.timesI().plus(Number.sqrt(Number.ONE.minus(z.sqrd())))).overI();
}
public static double abs(Number z) {
return z.r();
}
public double r() {
return r;
}
public double theta() {
return theta;
}
public double x() {
return r*Math.cos(theta);
}
public double y() {
return r*Math.sin(theta);
}
}