add option for number of gores

I had to adjust the projection code a little bit to account for odd numbers.
This commit is contained in:
Justin Kunimune 2024-04-25 20:32:38 -04:00
parent 9098c4d87d
commit 1f49c5b16f

View File

@ -378,34 +378,39 @@ public class Misc {
public static final Projection LEMONS = new Projection( public static final Projection LEMONS = new Projection(
"Gores", "A heavily interrupted projection that can be pasted onto a globe", "Gores", "A heavily interrupted projection that can be pasted onto a globe",
null, false, true, true, true, Type.CYLINDRICAL, Property.COMPROMISE, 2) { null, false, true, true, true, Type.OTHER, Property.COMPROMISE, 2,
new String[] {"Number of gores"}, new double[][] {{4, 72, 12}}) {
private static final int NUM_LEMONS = 12; //number of lemons private int numLemons;
private static final double LEMON_WIDTH = 2*PI/NUM_LEMONS; //longitude span of 1 lemon private double lemonWidth;
public void initialize(double... params) { public void initialize(double... params) {
// read in the number of gores and calculate the longitudinal extent of each
this.numLemons = (int) Math.round(params[0]);
this.lemonWidth = 2*PI/numLemons;
// to calculate the shape, start by getting the shape of a generic meridian // to calculate the shape, start by getting the shape of a generic meridian
List<Path.Command> polewardSegment = CASSINI.drawLoxodrome( List<Path.Command> polewardSegment = CASSINI.drawLoxodrome(
0, LEMON_WIDTH/2, PI/2, LEMON_WIDTH/2, .1); 0, lemonWidth/2, PI/2, lemonWidth/2, .1);
// make an equator-to-pole version and a pole-to-equator version // make an equator-to-pole version and a pole-to-equator version
List<Path.Command> tropicwardSegment = Path.reversed(polewardSegment); List<Path.Command> tropicwardSegment = Path.reversed(polewardSegment);
// remove one endpoint from each so there are no duplicate vertices // remove one endpoint from each so there are no duplicate vertices
polewardSegment = polewardSegment.subList(0, polewardSegment.size() - 1); polewardSegment = polewardSegment.subList(0, polewardSegment.size() - 1);
tropicwardSegment = tropicwardSegment.subList(0, tropicwardSegment.size() - 1); tropicwardSegment = tropicwardSegment.subList(0, tropicwardSegment.size() - 1);
// then build up the full shape by transforming the generic segments // then build up the full shape by transforming the generic segments
List<Path.Command> envelope = new ArrayList<>(NUM_LEMONS*4*polewardSegment.size()); List<Path.Command> envelope = new ArrayList<>(numLemons*4*polewardSegment.size());
// go east to west in the north hemisphere // go east to west in the north hemisphere
for (int i = NUM_LEMONS - 1; i >= 0; i --) { for (int i = numLemons - 1; i >= 0; i --) {
envelope.addAll(Path.transformed(1, 1, (i - (NUM_LEMONS - 1)/2.)*LEMON_WIDTH, 0, envelope.addAll(Path.transformed(1, 1, (i - (numLemons - 1)/2.)*lemonWidth, 0,
polewardSegment)); polewardSegment));
envelope.addAll(Path.transformed(-1, 1, (i - (NUM_LEMONS - 1)/2.)*LEMON_WIDTH, 0, envelope.addAll(Path.transformed(-1, 1, (i - (numLemons - 1)/2.)*lemonWidth, 0,
tropicwardSegment)); tropicwardSegment));
} }
// go west to east in the south hemisphere // go west to east in the south hemisphere
for (int i = 0; i < NUM_LEMONS; i ++) { for (int i = 0; i < numLemons; i ++) {
envelope.addAll(Path.transformed(-1, -1, (i - (NUM_LEMONS - 1)/2.)*LEMON_WIDTH, 0, envelope.addAll(Path.transformed(-1, -1, (i - (numLemons - 1)/2.)*lemonWidth, 0,
polewardSegment)); polewardSegment));
envelope.addAll(Path.transformed(1, -1, (i - (NUM_LEMONS - 1)/2.)*LEMON_WIDTH, 0, envelope.addAll(Path.transformed(1, -1, (i - (numLemons - 1)/2.)*lemonWidth, 0,
tropicwardSegment)); tropicwardSegment));
} }
// finally, convert it all to a Shape // finally, convert it all to a Shape
@ -413,22 +418,25 @@ public class Misc {
} }
public double[] project(double lat, double lon) { public double[] project(double lat, double lon) {
final double lemonIndex = double lemonIndex = floor((lon + PI)/lemonWidth); // pick a lemon
max(-NUM_LEMONS/2., min((NUM_LEMONS - 1)/2., floor(lon/LEMON_WIDTH) + .5)); // pick a lemon if (lon == 2*PI)
final double dl = lon - lemonIndex*LEMON_WIDTH; // find the relative longitude lemonIndex = numLemons - 1;
final double lemonCenter = (lemonIndex - numLemons/2. + 1/2.)*lemonWidth;
final double dl = lon - lemonCenter; // find the relative longitude
double[] xy = CASSINI.project(lat, dl); // project on Cassini with that double[] xy = CASSINI.project(lat, dl); // project on Cassini with that
xy[0] += lemonIndex*LEMON_WIDTH; // shift according to the lemon's x center xy[0] += lemonCenter; // shift according to the lemon's x center
return xy; return xy;
} }
public double[] inverse(double x, double y) { public double[] inverse(double x, double y) {
final int lemonIndex = (int)floor(x/LEMON_WIDTH); // pick a lemon final int lemonIndex = (int)floor((x + PI)/lemonWidth); // pick a lemon
final double dx = (x+2*PI)%LEMON_WIDTH - LEMON_WIDTH/2; // find the relative x final double lemonCenter = (lemonIndex - numLemons/2. + 1/2.)*lemonWidth;
final double dx = x - lemonCenter; // find the relative x
double[] latLon = CASSINI.inverse(dx, y); // project from Cassini with that double[] latLon = CASSINI.inverse(dx, y); // project from Cassini with that
if (abs(latLon[1]) > LEMON_WIDTH/2) // make sure it's still in the correct lemon if (abs(latLon[1]) > lemonWidth/2) // make sure it's still in the correct lemon
return null; return null;
else { else {
latLon[1] += (lemonIndex+.5)*LEMON_WIDTH; latLon[1] += lemonCenter;
return latLon; return latLon;
} }
} }