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>
<classpathentry kind="src" path="src"/>
<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"/>
</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 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 {
private static final int QUINCUNCIAL = 3;
private static final int QUINCUNCIAL = 4;
private static final int EQUIRECTANGULAR = 1;
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 */
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 double dx = .01;
double phi, lambda; // the correct phi and lambda combination is the one where func goes to 0.
double phiMin = -Math.PI/2;
double phiMax = Math.PI/2;
double lamMin = -Math.PI;
double lamMax = Math.PI;
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);
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
Complex ans = Jacobi.cn(u, k);
double p = 2*Math.atan(ans.abs());
double theta = Math.atan2(ans.getRe(), ans.getIm());
double lambda = p-Math.PI/2;
return getColor(lambda,theta,ref);
}
@ -182,17 +159,17 @@ public class MapProjections {
/* 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
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));
Complex Z = new Complex(x,y); // the point on the map
Number X = new Number(rt2*Math.tan(p/2), l);
Number Z = new Number(x,y,true); // the point on the map
// 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)
Complex arcSin = X.divide(rt2).asin().divide(rt2);
Number arcSin = Number.asin(X.over(rt2)).over(rt2);
// X*sqrt(-X^2/4 + 1)
Complex radical2 = X.multiply(X.multiply(X).divide(-4).add(1).pow(.5));
// 2*Z + pi*sqrt(2)/8
Complex finalBit = Z.multiply(2).subtract(Math.PI*rt2/8);
System.out.println("F("+X+","+Z+") = "+(radical1.subtract(arcSin).subtract(radical2).subtract(finalBit)).abs());
return (radical1.subtract(arcSin).subtract(radical2).subtract(finalBit)).abs();
Number radical2 = X.times(Number.sqrt(X.sqrd().over(-4).plus(1)));
// 2*Z - pi*sqrt(2)/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());
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);
}
}