mirror of
https://github.com/csharpee/Map-Projections.git
synced 2025-12-08 00:00:14 -05:00
Put the scissors down!
I (finally) made it automatically snip paths that are obviously crossing interruptions. This should make vector maps look world better straight out of the program, and will be crucial to mass-producing vector maps. There's some room for improvement regarding the handling of closepaths, but all in all, this is a big step up. I also got a new input, fixed some minor issues with the new Cahill-Keyes (did you know it's pronounced /kais/?), and might have fixed the sporadic BufferOverflowExceptions, but I'm not sure about the last one, as that problem is extremely difficult to reproduce, but I have a strong suspicion it has something to do with threads, and I added a bunch of Platform.runLater()s, so we'll see.
This commit is contained in:
parent
027347071c
commit
9ae6c2a668
BIN
input/Night.png
Normal file
BIN
input/Night.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 MiB |
@ -543,14 +543,18 @@ public abstract class MapApplication extends Application {
|
||||
|
||||
|
||||
protected void disable(ButtonType... buttons) {
|
||||
for (ButtonType bt: buttons)
|
||||
this.buttons.get(bt).setDisable(true);
|
||||
Platform.runLater(() -> {
|
||||
for (ButtonType bt: buttons)
|
||||
this.buttons.get(bt).setDisable(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
protected void enable(ButtonType... buttons) {
|
||||
for (ButtonType bt: buttons)
|
||||
this.buttons.get(bt).setDisable(false);
|
||||
Platform.runLater(() -> {
|
||||
for (ButtonType bt: buttons)
|
||||
this.buttons.get(bt).setDisable(false);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import dialogs.ProgressBarDialog;
|
||||
import javafx.application.Platform;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.canvas.Canvas;
|
||||
@ -160,14 +161,18 @@ public class MapDesignerVector extends MapApplication {
|
||||
final Iterable<Path> transformed = map(input, input.length()/maxVtx+1, aspect.clone(), null);
|
||||
|
||||
if (this.getProjection().getWidth() > this.getProjection().getHeight()) {
|
||||
viewer.setWidth(IMG_WIDTH);
|
||||
viewer.setHeight(IMG_WIDTH/this.getProjection().getAspectRatio());
|
||||
Platform.runLater(() -> {
|
||||
viewer.setWidth(IMG_WIDTH);
|
||||
viewer.setHeight(IMG_WIDTH/this.getProjection().getAspectRatio());
|
||||
});
|
||||
}
|
||||
else {
|
||||
viewer.setWidth(IMG_WIDTH*this.getProjection().getAspectRatio());
|
||||
viewer.setHeight(IMG_WIDTH);
|
||||
Platform.runLater(() -> {
|
||||
viewer.setWidth(IMG_WIDTH*this.getProjection().getAspectRatio());
|
||||
viewer.setHeight(IMG_WIDTH);
|
||||
});
|
||||
}
|
||||
drawImage(transformed, viewer);
|
||||
Platform.runLater(() -> drawImage(transformed, viewer));
|
||||
|
||||
enable(ButtonType.SAVE_MAP);
|
||||
}
|
||||
@ -228,7 +233,7 @@ public class MapDesignerVector extends MapApplication {
|
||||
|
||||
|
||||
private void drawImage(
|
||||
Iterable<Path> paths, Canvas c) { //parse the SVG path, with a few modifications
|
||||
Iterable<Path> paths, Canvas c) { //parse the SVG path, with a few modifications (run this from the GUI thread!)
|
||||
final double mX = this.getProjection().getWidth()/2;
|
||||
final double mY = this.getProjection().getHeight()/2;
|
||||
GraphicsContext g = c.getGraphicsContext2D();
|
||||
|
||||
@ -307,17 +307,19 @@ public class Octohedral {
|
||||
}
|
||||
|
||||
public double[] inverse(double x, double y) {
|
||||
if (Math.hypot(x-southPoleX, y-southPoleY) < 0.325) { //do the special Antarctica thing
|
||||
if (Math.hypot(x-southPoleX, y-southPoleY) < 0.324) { //do the special Antarctica thing
|
||||
double tht = Math.atan2(southPoleX-x, y-southPoleY);
|
||||
double centralAngle =
|
||||
Math.floor((tht+Math.PI/12)/(Math.PI/2))*Math.PI/2 + Math.PI/6;
|
||||
if (centralAngle == 7*Math.PI/6)
|
||||
centralAngle = -5*Math.PI/6;
|
||||
double maxLat = (centralAngle != Math.PI/6) ? -Math.PI/3 : Math.PI/2;
|
||||
return new double[] {
|
||||
southPoleX - (2-cutRatio)*Math.sin(centralAngle),
|
||||
southPoleY + (2-cutRatio)*Math.cos(centralAngle),
|
||||
centralAngle, -Math.PI/12 - centralAngle, -Math.PI/2, maxLat};
|
||||
}
|
||||
else {
|
||||
else { //everything besides Antarctica is pretty straightforward
|
||||
double tht = Math.atan2(Math.abs(x)-1, -y);
|
||||
if (tht < -Math.PI/3) return null;
|
||||
double centralAngle = Math.floor(tht/(Math.PI/3))*Math.PI/3 + Math.PI/6;
|
||||
|
||||
@ -31,6 +31,7 @@ import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -279,7 +280,7 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
||||
|
||||
out.write(replacePlaceholders(formatIterator.next(), inWidth/inHeight));
|
||||
while (curveIterator.hasNext()) {
|
||||
out.write(curveIterator.next().toString(inMinX, inMaxY, vbMinX, vbMinY,
|
||||
out.write(breakWraps(curveIterator.next()).toString(inMinX, inMaxY, vbMinX, vbMinY,
|
||||
Math.max(vbWidth, vbHeight)/Math.max(inWidth, inHeight)));
|
||||
out.write(formatIterator.next());
|
||||
tracker.accept((double)i/paths.size());
|
||||
@ -312,6 +313,32 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
||||
}
|
||||
|
||||
|
||||
private Path breakWraps(Path continuous) { //break excessively long commands, as they are likely wrapping over a discontinuity
|
||||
if (continuous.size() <= 2) return continuous;
|
||||
Path broken = new Path();
|
||||
double[] lens = {Double.NaN, Double.NaN, Double.NaN}; //the revolving array of command lengths
|
||||
for (int i = 0; i < continuous.size(); i ++) {
|
||||
if (i < continuous.size()-1 && continuous.get(i+1).type != 'M')
|
||||
lens[2] = Math.hypot( //compute this next length
|
||||
continuous.get(i+1).args[0] - continuous.get(i).args[0],
|
||||
continuous.get(i+1).args[1] - continuous.get(i).args[1]);
|
||||
else
|
||||
lens[2] = Double.NaN;
|
||||
|
||||
char type = continuous.get(i).type;
|
||||
if ((Double.isNaN(lens[0]) || lens[1] > 20*lens[0]) //and compare it to the last two lengths
|
||||
&& (Double.isNaN(lens[2]) || lens[1] > 20*lens[2]))
|
||||
type = 'M';
|
||||
|
||||
broken.add(new Command(type, continuous.get(i).args.clone()));
|
||||
lens[0] = lens[1];
|
||||
lens[1] = lens[2];
|
||||
}
|
||||
|
||||
return broken;
|
||||
}
|
||||
|
||||
|
||||
private static boolean isNonELetter(char c) {
|
||||
return (c >= 'A' && c <= 'Z' && c != 'E') || (c >= 'a' && c <= 'z' && c != 'e');
|
||||
}
|
||||
@ -337,6 +364,11 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
||||
super();
|
||||
}
|
||||
|
||||
public Path(Command... commands) {
|
||||
this();
|
||||
this.addAll(Arrays.asList(commands));
|
||||
}
|
||||
|
||||
public Path(String d, double vbWidth, double vbHeight) throws Exception {
|
||||
this(d, new double[] {1,1,0,0}, 0, 0, vbWidth, vbHeight);
|
||||
}
|
||||
@ -346,7 +378,8 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
||||
super();
|
||||
|
||||
int i = 0;
|
||||
double[] last = new double[] {0,0}; //for relative coordinates
|
||||
double[] lastMove = {0, 0}; //for closepaths
|
||||
double[] last = {0, 0}; //for relative coordinates
|
||||
while (i < d.length()) {
|
||||
char type = d.charAt(i);
|
||||
String argString = "";
|
||||
@ -374,6 +407,10 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
||||
last[direcIdx] = args[direcIdx];
|
||||
type = 'L';
|
||||
}
|
||||
else if (type == 'z' || type == 'Z') { //change this to 'L', too
|
||||
args = new double[] {lastMove[0], lastMove[1]};
|
||||
type = 'L';
|
||||
}
|
||||
else {
|
||||
args = new double[argStrings.length];
|
||||
for (int j = 0; j < args.length; j ++) {
|
||||
@ -386,6 +423,10 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
||||
if (type >= 'a') //make all letters uppercase
|
||||
type -= 32;
|
||||
}
|
||||
if (type == 'M') { //make note, so we can interpret closepaths properly
|
||||
lastMove[0] = args[args.length-2];
|
||||
lastMove[1] = args[args.length-1];
|
||||
}
|
||||
|
||||
for (int j = 0; j < args.length; j ++) {
|
||||
if (!Double.isFinite(args[j]))
|
||||
@ -406,10 +447,8 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean addAll(Path p) {
|
||||
for (Command c: p)
|
||||
add(c);
|
||||
return true;
|
||||
public Path(Command command) {
|
||||
// TODO: Implement this
|
||||
}
|
||||
|
||||
public String toString(
|
||||
@ -435,6 +474,10 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public Command(Command command) {
|
||||
this(command.type, command.args.clone());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.toString(-1, -1, 0, 0, 1);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user